From c1883ad22550c415635a2f9ed85e965186bc965a Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 22 Apr 2025 20:41:47 +0300 Subject: [PATCH 01/11] Add TryValidate feature and some tests --- .../Microsoft.AspNetCore.OData.xml | 278 +++++++++++++++++- .../PublicAPI.Unshipped.txt | 26 ++ .../Query/ODataQueryOptions.cs | 28 ++ .../Query/Query/ComputeQueryOption.cs | 27 ++ .../Query/Query/CountQueryOption.cs | 29 +- .../Query/Query/FilterQueryOption.cs | 26 ++ .../Query/Query/OrderByQueryOption.cs | 25 ++ .../Query/Query/SearchQueryOption.cs | 29 ++ .../Query/Query/SelectExpandQueryOption.cs | 26 ++ .../Query/Query/SkipQueryOption.cs | 25 ++ .../Query/Query/SkipTokenQueryOption.cs | 27 ++ .../Query/Query/TopQueryOption.cs | 25 ++ .../Query/Validator/ComputeQueryValidator.cs | 36 +++ .../Query/Validator/CountQueryValidator.cs | 54 ++++ .../Query/Validator/FilterQueryValidator.cs | 57 ++++ .../Interfaces/IComputeQueryValidator.cs | 14 +- .../Interfaces/ICountQueryValidator.cs | 14 +- .../Interfaces/IFilterQueryValidator.cs | 14 +- .../Interfaces/IODataQueryValidator.cs | 14 +- .../Interfaces/IOrderByQueryValidator.cs | 12 + .../Interfaces/ISearchQueryValidator.cs | 14 +- .../Interfaces/ISelectExpandQueryValidator.cs | 14 +- .../Interfaces/ISkipQueryValidator.cs | 12 + .../Interfaces/ISkipTokenQueryValidator.cs | 12 + .../Interfaces/ITopQueryValidator.cs | 12 + .../Query/Validator/ODataQueryValidator.cs | 196 +++++++++++- .../Query/Validator/OrderByQueryValidator.cs | 66 ++++- .../Validator/SelectExpandQueryValidator.cs | 69 +++++ .../Query/Validator/SkipQueryValidator.cs | 32 ++ .../Validator/SkipTokenQueryValidator.cs | 37 ++- .../Query/Validator/TopQueryValidator.cs | 53 ++++ .../Validator/ODataQueryValidatorTest.cs | 157 ++++++++++ .../Validator/SearchQueryValidatorTest.cs | 9 + 33 files changed, 1451 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 7ae41a257..e8a0493bd 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -11272,6 +11272,17 @@ The instance which contains all the validation settings. + + + Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing validation + errors, if any occurred; otherwise, if validation was successful or no validator is + configured. + if the validation was successful or no validator is configured; otherwise, if validation failed. + Ensures the given will produce a stable sort. @@ -11865,6 +11876,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents the value of the $count query option and exposes a way to retrieve the number of entities that satisfy a query. @@ -11906,6 +11926,16 @@ The instance which contains all the validation settings. + + + Attempts to validate the count query based on the given . + It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Gets the number of entities that satisfy the given query if the response should include a count query option, or null otherwise. @@ -12039,6 +12069,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents the raw query values in the string format from the incoming request. @@ -12189,6 +12228,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + This defines a $search OData query option for querying. @@ -12242,6 +12290,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents the OData $select and $expand query options. @@ -12325,6 +12382,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + This defines a $skip OData query option for querying. @@ -12381,6 +12447,15 @@ The instance which contains all the validation settings. + + + Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents how NextLink for paging is generated. @@ -12473,6 +12548,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + This defines a $top OData query option for querying. @@ -12529,6 +12613,15 @@ The instance which contains all the validation settings. + + + Attempts to validate the top query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + the corresponding property segment. @@ -12618,6 +12711,16 @@ The $compute query. The validation settings. + + + Attempts to validate the . + + The $compute query. + The validation settings. + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents a validator used to validate a @@ -12631,6 +12734,16 @@ The $count query. The validation settings. + + + Attempts to validate the . + + + + When this method returns, contains a collection of instances describing any + validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents a validator used to validate a based on the . @@ -12643,6 +12756,15 @@ The $filter query. The validation settings. + + + Attempts to validate the . + + The $filter query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Validates a . @@ -12935,10 +13057,19 @@ - Validates the OData query. + Validates the . + + The $compute query. + The validation settings. + + + + Attempts to validate the . The $compute query. The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . @@ -12948,11 +13079,20 @@ - Validates the OData query. + Validates the . The $count query. The validation settings. + + + Attempts to validate the . + + + + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provide the interface used to validate a @@ -12961,10 +13101,19 @@ - Validates the OData query. + Validates the . + + The $filter query. + The validation settings. + + + + Attempts to validate the . The $filter query. The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . @@ -12974,10 +13123,19 @@ - Validates the OData query. + Validates the . + + The OData query options to validate. + The validation settings. + + + + Attempts to validate the . The OData query options to validate. The validation settings. + The collection of validation errors. + True if the validation succeeded; otherwise, false. @@ -12992,6 +13150,15 @@ The $orderby query. The validation settings. + + + Attempts to validate the . + + The $orderby query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provides the interface used to validate a @@ -13000,11 +13167,20 @@ - Validates the OData $search query. + Validates the . The $search query. The validation settings. + + + Attempts to validate the . + + + + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provide the interface used to validate a @@ -13013,11 +13189,20 @@ - Validates the OData query. + Validates the The $select and $expand query. The validation settings. + + + Attempts to validate the . + + The $select and $expand query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provide the interface used to validate a @@ -13031,6 +13216,15 @@ The $skip query. The validation settings. + + + Attempts to validate the . + + The $skip query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provide the interface used to validate a @@ -13044,6 +13238,15 @@ The $skiptoken query. The validation settings. + + + Attempts to validate the . + + The $skiptoken query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Provide the interface used to validate a @@ -13057,6 +13260,15 @@ The $top query. The validation settings. + + + Attempts to validate the . + + The $top query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents a validator used to validate OData queries based on the . @@ -13069,6 +13281,15 @@ The OData query to validate. The validation settings. + + + Try validates the OData query. + + The OData query to validate. + The settings used for validation. + The collection of validation errors. + True if validation is successful; otherwise, false. + This class describes the validation settings for querying. @@ -13173,6 +13394,15 @@ The $orderby query. The validation settings. + + + Attempts to validate the . + + The $orderby query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Validates a . @@ -13485,6 +13715,15 @@ The $select and $expand query. The validation settings. + + + Attempts to validate the . + + The $select and $expand query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Validates all select and expand items in $select and $expand. @@ -13636,6 +13875,15 @@ The $skip query. The validation settings. + + + Attempts to validate the . + + The $skip query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents a validator used to validate a based on the . @@ -13648,6 +13896,15 @@ The $skiptoken query. The validation settings. + + + Attempts to validate the . + + The $skiptoken query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + Represents a validator used to validate a based on the . @@ -13660,6 +13917,15 @@ The $top query. The validation settings. + + + Attempts to validate the . + + The $top query. + The validation settings. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + if the validation succeeded; otherwise, . + diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index e69de29bb..13ea3607a 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -0,0 +1,26 @@ +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.CountQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.FilterQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.OrderByQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SearchQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.TopQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ITopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable errors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs index 0408c23bc..e591dd3ca 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs @@ -654,6 +654,34 @@ public virtual void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing validation + /// errors, if any occurred; otherwise, if validation was successful or no validator is + /// configured. + /// if the validation was successful or no validator is configured; otherwise, if validation failed. + public virtual bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable errors) + { + if (validationSettings == null) + { + errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + return false; + } + + this.Context.ValidationSettings = validationSettings; + + if (Validator != null) + { + return Validator.TryValidate(this, validationSettings, out errors); + } + + errors = null; + return true; + } + private static void ThrowIfEmpty(string queryValue, string queryName) { if (String.IsNullOrWhiteSpace(queryValue)) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index 0242ceb35..f06e4c23a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -7,7 +7,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -137,4 +139,29 @@ public void Validate(ODataValidationSettings validationSettings) Validator.Validate(this, validationSettings); } } + + /// + /// Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index ad291c87d..9696aa31d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -10,6 +10,7 @@ using System.Diagnostics.Contracts; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.UriParser; namespace Microsoft.AspNetCore.OData.Query; @@ -123,7 +124,7 @@ public void Validate(ODataValidationSettings validationSettings) { if (validationSettings == null) { - throw Error.ArgumentNull("validationSettings"); + throw Error.ArgumentNull(nameof(validationSettings)); } if (Validator != null) @@ -132,6 +133,32 @@ public void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate the count query based on the given . + /// It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } + /// /// Gets the number of entities that satisfy the given query if the response should include a count query option, or null otherwise. /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs index 21599e3e9..b73b14393 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs @@ -11,6 +11,7 @@ using System.Linq; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -188,4 +189,29 @@ public void Validate(ODataValidationSettings validationSettings) Validator.Validate(this, validationSettings); } } + + /// + /// Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs index 94fb56753..65b7130e1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs @@ -245,6 +245,31 @@ public void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } + private IOrderedQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) { if (Context.ElementClrType == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs index f3fa6101d..b57ea50c7 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs @@ -10,6 +10,7 @@ using System.Linq; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -172,4 +173,32 @@ public void Validate(ODataValidationSettings validationSettings) validator.Validate(this, validationSettings); } } + + /// + /// Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + + ISearchQueryValidator validator = Context.GetSearchQueryValidator(); + if (validator != null) + { + // If the developer doesn't provide the search validator, let's ignore the $search validation. + validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs index 551b5c093..d5913ed91 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; using Microsoft.OData.ModelBuilder.Config; @@ -289,6 +290,31 @@ public void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } + internal SelectExpandClause ProcessLevels() { bool levelsEncountered; diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs index fd040240b..c8f7ecca4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs @@ -159,6 +159,31 @@ public void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } + private IQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) { if (query == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs index 1bc7b9c09..2852208ce 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs @@ -5,8 +5,10 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; using Microsoft.OData.Edm; namespace Microsoft.AspNetCore.OData.Query; @@ -100,4 +102,29 @@ public void Validate(ODataValidationSettings validationSettings) Validator.Validate(this, validationSettings); } } + + /// + /// Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs index f24b3e9d2..720c2be28 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs @@ -159,6 +159,31 @@ public void Validate(ODataValidationSettings validationSettings) } } + /// + /// Attempts to validate the top query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + if (validationSettings == null) + { + List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + validationErrors = errors; + return false; + } + + validationErrors = Enumerable.Empty(); + if (Validator != null) + { + Validator.TryValidate(this, validationSettings, out validationErrors); + } + + return !validationErrors.Any(); + } + private IQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) { if (query == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index 75921c8fd..1c8187f78 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -35,4 +38,37 @@ public virtual void Validate(ComputeQueryOption computeQueryOption, ODataValidat // however, developer can override this method add his own rules _ = computeQueryOption.ComputeClause; } + + /// + /// Attempts to validate the . + /// + /// The $compute query. + /// The validation settings. + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (computeQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(computeQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // so far, we don't have validation rules here for $compute + // because 'DefaultQuerySetting' doesn't have configuration for $compute + // we can only let ODL to parse and verify the compute clause, + // however, developer can override this method add his own rules + _ = computeQueryOption.ComputeClause; + + validationErrors = errors; + return errors.Count == 0; + } + } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs index e50396479..c2aedb0ac 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs @@ -6,7 +6,9 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; using Microsoft.AspNetCore.OData.Edm; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -57,4 +59,56 @@ public virtual void Validate(CountQueryOption countQueryOption, ODataValidationS } } } + + /// + /// Attempts to validate the . + /// + /// + /// + /// When this method returns, contains a collection of instances describing any + /// validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (countQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(countQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + + ODataPath path = countQueryOption.Context.Path; + + if (path != null && path.Count > 0) + { + IEdmProperty property = countQueryOption.Context.TargetProperty; + IEdmStructuredType structuredType = countQueryOption.Context.TargetStructuredType; + string name = countQueryOption.Context.TargetName; + if (EdmHelpers.IsNotCountable(property, structuredType, + countQueryOption.Context.Model, + countQueryOption.Context.DefaultQueryConfigurations.EnableCount)) + { + string errorMessage = property == null + ? Error.Format(SRResources.NotCountableEntitySetUsedForCount, name) + : Error.Format(SRResources.NotCountablePropertyUsedForCount, name); + + errors.Add(new ODataException(errorMessage)); + } + } + + validationErrors = errors; + return errors.Count == 0; + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs index 92d3c5e16..52a5b157c 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs @@ -5,6 +5,8 @@ // //------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.OData.Edm; @@ -49,6 +51,61 @@ public virtual void Validate(FilterQueryOption filterQueryOption, ODataValidatio ValidateFilter(filterQueryOption.FilterClause, validatorContext); } + /// + /// Attempts to validate the . + /// + /// The $filter query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + // Validate input parameters + if (filterQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(filterQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + + // Create a validation context + var validatorContext = new FilterValidatorContext + { + Filter = filterQueryOption, + Context = filterQueryOption.Context, + ValidationSettings = validationSettings, + Property = filterQueryOption.Context.TargetProperty, + StructuredType = filterQueryOption.Context.TargetStructuredType, + CurrentDepth = 0 + }; + + // Validate the filter clause + try + { + ValidateFilter(filterQueryOption.FilterClause, validatorContext); + } + catch (Exception ex) + { + errors.Add(new ODataException(ex.Message)); + } + + // Set the output parameter + validationErrors = errors; + return errors.Count == 0; + } + /// /// Validates a . /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs index 72c300ae8..667c16592 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface IComputeQueryValidator { /// - /// Validates the OData query. + /// Validates the . /// /// The $compute query. /// The validation settings. void Validate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $compute query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs index 7673b2004..ff348f289 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface ICountQueryValidator { /// - /// Validates the OData query. + /// Validates the . /// /// The $count query. /// The validation settings. void Validate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// + /// + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs index f3152730d..f1a9517ef 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface IFilterQueryValidator { /// - /// Validates the OData query. + /// Validates the . /// /// The $filter query. /// The validation settings. void Validate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $filter query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs index 455f5171c..b56cc31ee 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface IODataQueryValidator { /// - /// Validates the OData query. + /// Validates the . /// /// The OData query options to validate. /// The validation settings. void Validate(ODataQueryOptions options, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The OData query options to validate. + /// The validation settings. + /// The collection of validation errors. + /// True if the validation succeeded; otherwise, false. + bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs index 8703ed242..a53de52ab 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -19,4 +22,13 @@ public interface IOrderByQueryValidator /// The $orderby query. /// The validation settings. void Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $orderby query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs index 4f81f5f19..d80384a0f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface ISearchQueryValidator { /// - /// Validates the OData $search query. + /// Validates the . /// /// The $search query. /// The validation settings. void Validate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// + /// + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs index 66e8ee8d8..1bf333396 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -14,9 +17,18 @@ namespace Microsoft.AspNetCore.OData.Query.Validator; public interface ISelectExpandQueryValidator { /// - /// Validates the OData query. + /// Validates the /// /// The $select and $expand query. /// The validation settings. void Validate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $select and $expand query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs index 124c2de25..3299a1424 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -19,4 +22,13 @@ public interface ISkipQueryValidator /// The $skip query. /// The validation settings. void Validate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $skip query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs index 1f6d9b5ad..2fe81df87 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -19,4 +22,13 @@ public interface ISkipTokenQueryValidator /// The $skiptoken query. /// The validation settings. void Validate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $skiptoken query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs index 189361e41..27b6acb45 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using Microsoft.OData; + namespace Microsoft.AspNetCore.OData.Query.Validator; /// @@ -19,4 +22,13 @@ public interface ITopQueryValidator /// The $top query. /// The validation settings. void Validate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings); + + /// + /// Attempts to validate the . + /// + /// The $top query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 1b252a4da..7321892e4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; using Microsoft.AspNetCore.OData.Extensions; using Microsoft.OData; @@ -123,14 +124,197 @@ public virtual void Validate(ODataQueryOptions options, ODataValidationSettings } } + /// + /// Try validates the OData query. + /// + /// The OData query to validate. + /// The settings used for validation. + /// The collection of validation errors. + /// True if validation is successful; otherwise, false. + public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + // Validate input parameters + if (options == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(options)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // If there are parameter errors, return early + if (errors.Count > 0) + { + validationErrors = errors; + return false; + } + + // Helper function to aggregate errors + void AddValidationErrors(IEnumerable queryOptionErrors) + { + if (queryOptionErrors != null) + { + errors.AddRange(queryOptionErrors); + } + } + + // Validate each query option + if (options.Compute != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out IEnumerable computeErrors); + AddValidationErrors(computeErrors); + + if (!options.Compute.TryValidate(validationSettings, out IEnumerable computeValidationErrors)) + { + AddValidationErrors(computeValidationErrors); + } + } + + if (options.Apply?.ApplyClause != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out IEnumerable applyErrors); + AddValidationErrors(applyErrors); + } + + if (options.Skip != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out IEnumerable skipErrors); + AddValidationErrors(skipErrors); + + if (!options.Skip.TryValidate(validationSettings, out IEnumerable skipValidationErrors)) + { + AddValidationErrors(skipValidationErrors); + } + } + + if (options.Top != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out IEnumerable topErrors); + AddValidationErrors(topErrors); + + if (!options.Top.TryValidate(validationSettings, out IEnumerable topValidationErrors)) + { + AddValidationErrors(topValidationErrors); + } + } + + if (options.OrderBy != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out IEnumerable orderByErrors); + AddValidationErrors(orderByErrors); + + if (!options.OrderBy.TryValidate(validationSettings, out IEnumerable orderByValidationErrors)) + { + AddValidationErrors(orderByValidationErrors); + } + } + + if (options.Filter != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out IEnumerable filterErrors); + AddValidationErrors(filterErrors); + + if (!options.Filter.TryValidate(validationSettings, out IEnumerable filterValidationErrors)) + { + AddValidationErrors(filterValidationErrors); + } + } + + if (options.Search != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out IEnumerable searchErrors); + AddValidationErrors(searchErrors); + + if (!options.Search.TryValidate(validationSettings, out IEnumerable searchValidationErrors)) + { + AddValidationErrors(searchValidationErrors); + } + } + + if (options.Count != null || options.Request.IsCountRequest()) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out IEnumerable countErrors); + AddValidationErrors(countErrors); + + if (options.Count?.TryValidate(validationSettings, out IEnumerable countValidationErrors) == false) + { + AddValidationErrors(countValidationErrors); + } + } + + if (options.SkipToken != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out IEnumerable skipTokenErrors); + AddValidationErrors(skipTokenErrors); + + if (!options.SkipToken.TryValidate(validationSettings, out IEnumerable skipTokenValidationErrors)) + { + AddValidationErrors(skipTokenValidationErrors); + } + } + + if (options.RawValues.Expand != null) + { + TryValidateNotEmptyOrWhitespace(options.RawValues.Expand, errors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out IEnumerable expandErrors); + AddValidationErrors(expandErrors); + } + + if (options.RawValues.Select != null) + { + TryValidateNotEmptyOrWhitespace(options.RawValues.Select, errors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out IEnumerable selectErrors); + AddValidationErrors(selectErrors); + } + + if (options.SelectExpand != null) + { + if (!options.SelectExpand.TryValidate(validationSettings, out IEnumerable selectExpandValidationErrors)) + { + AddValidationErrors(selectExpandValidationErrors); + } + } + + if (options.RawValues.Format != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out IEnumerable formatErrors); + AddValidationErrors(formatErrors); + } + + if (options.RawValues.DeltaToken != null) + { + TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out IEnumerable deltaTokenErrors); + AddValidationErrors(deltaTokenErrors); + } + + validationErrors = errors; + return errors.Count == 0; + } + + private static void ValidateQueryOptionAllowed(AllowedQueryOptions queryOption, AllowedQueryOptions allowed) { if ((queryOption & allowed) == AllowedQueryOptions.None) { - throw new ODataException(Error.Format(SRResources.NotAllowedQueryOption, queryOption, "AllowedQueryOptions")); + throw new ODataException(Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions))); + } + } + + private static void TryValidateQueryOptionAllowed(AllowedQueryOptions queryOption, AllowedQueryOptions allowed, out IEnumerable validationErrors) + { + List errors = new List(); + if ((queryOption & allowed) == AllowedQueryOptions.None) + { + errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions)))); } + + validationErrors = errors; } - + private static void ValidateNotEmptyOrWhitespace(string rawValue) { if (rawValue != null && string.IsNullOrWhiteSpace(rawValue)) @@ -138,4 +322,12 @@ private static void ValidateNotEmptyOrWhitespace(string rawValue) throw new ODataException(SRResources.SelectExpandEmptyOrWhitespace); } } + + private static void TryValidateNotEmptyOrWhitespace(string rawValue, List validationErrors) + { + if (rawValue != null && string.IsNullOrWhiteSpace(rawValue)) + { + validationErrors.Add(new ODataException(SRResources.SelectExpandEmptyOrWhitespace)); + } + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs index 872d54b47..13a2716fa 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs @@ -5,6 +5,8 @@ // //------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; using Microsoft.AspNetCore.OData.Edm; using Microsoft.OData; using Microsoft.OData.Edm; @@ -26,12 +28,12 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe { if (orderByOption == null) { - throw Error.ArgumentNull("orderByOption"); + throw Error.ArgumentNull(nameof(orderByOption)); } if (validationSettings == null) { - throw Error.ArgumentNull("validationSettings"); + throw Error.ArgumentNull(nameof(validationSettings)); } OrderByValidatorContext validatorContext = new OrderByValidatorContext @@ -55,6 +57,66 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe } } + /// + /// Attempts to validate the . + /// + /// The $orderby query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (orderByOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(orderByOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + + OrderByValidatorContext validatorContext = new OrderByValidatorContext + { + OrderBy = orderByOption, + Context = orderByOption.Context, + ValidationSettings = validationSettings, + Property = orderByOption.Context.TargetProperty, + StructuredType = orderByOption.Context.TargetStructuredType, + CurrentDepth = 0 + }; + + OrderByClause clause = orderByOption.OrderByClause; + while (clause != null) + { + try + { + validatorContext.IncrementNodeCount(); + + ValidateOrderBy(clause, validatorContext); + } + catch(Exception ex) + { + errors.Add(new ODataException(ex.Message)); + } + + clause = clause.ThenBy; + } + + // If there are any errors, return false + validationErrors = errors; + return errors.Count == 0; + } + /// /// Validates a . /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs index 9826814ed..0aee3c5be 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs @@ -71,6 +71,75 @@ public virtual void Validate(SelectExpandQueryOption selectExpandQueryOption, OD } } + /// + /// Attempts to validate the . + /// + /// The $select and $expand query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (selectExpandQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(selectExpandQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + + SelectExpandValidatorContext validatorContext = new SelectExpandValidatorContext + { + SelectExpand = selectExpandQueryOption, + Context = selectExpandQueryOption.Context, + ValidationSettings = validationSettings, + Property = selectExpandQueryOption.Context.TargetProperty, + StructuredType = selectExpandQueryOption.Context.TargetStructuredType, + CurrentDepth = 0 + }; + + try + { + ValidateSelectExpand(selectExpandQueryOption.SelectExpandClause, validatorContext); + + if (validationSettings.MaxExpansionDepth > 0) + { + if (selectExpandQueryOption.LevelsMaxLiteralExpansionDepth < 0) + { + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = validationSettings.MaxExpansionDepth; + } + else if (selectExpandQueryOption.LevelsMaxLiteralExpansionDepth > validationSettings.MaxExpansionDepth) + { + throw new ODataException(Error.Format( + SRResources.InvalidExpansionDepthValue, + "LevelsMaxLiteralExpansionDepth", + "MaxExpansionDepth")); + } + + ValidateDepth(selectExpandQueryOption.SelectExpandClause, validationSettings.MaxExpansionDepth); + } + } + catch (Exception ex) + { + errors.Add(new ODataException(ex.Message)); + } + + // If there are any errors, return false + validationErrors = errors; + return errors.Count == 0; + } + /// /// Validates all select and expand items in $select and $expand. /// For example, ~/Customers?$expand=Nav($expand=subNav;$select=Prop;$top=2)&$select=Addresses($select=City;$top=1) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index ca5aa126d..075290901 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -36,4 +37,35 @@ public virtual void Validate(SkipQueryOption skipQueryOption, ODataValidationSet throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value)); } } + + /// + /// Attempts to validate the . + /// + /// The $skip query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (skipQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(skipQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + if (skipQueryOption.Value > validationSettings.MaxSkip) + { + errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value))); + } + + // If there are any errors, return false + validationErrors = errors; + return errors.Count == 0; + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs index 1fd6f7ea3..3949e373d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs @@ -5,8 +5,8 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; using Microsoft.OData; -using Microsoft.OData.ModelBuilder.Config; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -41,4 +41,39 @@ public virtual void Validate(SkipTokenQueryOption skipToken, ODataValidationSett } } } + + /// + /// Attempts to validate the . + /// + /// The $skiptoken query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (skipToken == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(skipToken)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + if (skipToken?.Context != null) + { + DefaultQueryConfigurations defaultConfigs = skipToken.Context.DefaultQueryConfigurations; + if (!defaultConfigs.EnableSkipToken) + { + errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, "AllowedQueryOptions"))); + } + } + + // If there are any errors, return false + validationErrors = errors; + return errors.Count == 0; + } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs index ec50c974d..cac80da1d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; using Microsoft.AspNetCore.OData.Edm; using Microsoft.OData; using Microsoft.OData.Edm; @@ -54,4 +55,56 @@ public virtual void Validate(TopQueryOption topQueryOption, ODataValidationSetti AllowedQueryOptions.Top, topQueryOption.Value)); } } + + /// + /// Attempts to validate the . + /// + /// The $top query. + /// The validation settings. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + List errors = new List(); + + if (topQueryOption == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(topQueryOption)).Message)); + } + + if (validationSettings == null) + { + errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + } + + if (topQueryOption != null && validationSettings != null && topQueryOption.Value > validationSettings.MaxTop) + { + errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value))); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + + int maxTop; + IEdmProperty property = topQueryOption.Context.TargetProperty; + IEdmStructuredType structuredType = topQueryOption.Context.TargetStructuredType; + + if (EdmHelpers.IsTopLimitExceeded( + property, + structuredType, + topQueryOption.Context.Model, + topQueryOption.Value, topQueryOption.Context.DefaultQueryConfigurations, + out maxTop)) + { + errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value))); + } + + // If there are any errors, return false + validationErrors = errors; + return errors.Count == 0; + } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs index 0ad3c0c9e..bd4417e15 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs @@ -70,6 +70,21 @@ public void ValidateThrowsOnNullOption() _validator.Validate(null, new ODataValidationSettings())); } + [Fact] + public void TryValidate_ReturnsFalseAndErrors_WhenOptionsIsNull() + { + // Arrange + IEnumerable errors; + + // Act + var result = _validator.TryValidate(null, new ODataValidationSettings(), out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + Assert.Equal("Value cannot be null. (Parameter 'options')", errors.First().Message); + } + [Fact] public void ValidateThrowsOnNullSettings() { @@ -79,6 +94,22 @@ public void ValidateThrowsOnNullSettings() _validator.Validate(new ODataQueryOptions(_context, message), null)); } + [Fact] + public void TryValidate_ReturnsFalseAndErrors_WhenValidationSettingsIsNull() + { + // Arrange + var message = RequestFactory.Create(); + IEnumerable errors; + + // Act + var result = _validator.TryValidate(new ODataQueryOptions(_context, message), null, out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Fact] public void QueryOptionDataSets_CoverAllValues() { @@ -152,6 +183,32 @@ public void AllowedQueryOptions_ThrowIfNotAllowed(AllowedQueryOptions exclude, s ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(SupportedQueryOptions))] + [MemberData(nameof(UnsupportedQueryOptions))] + public void AllowedQueryOptions_ReturnsFalseWithErrors_IfNotAllowed_UsingTryValidate(AllowedQueryOptions exclude, string query, string optionName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var expectedMessage = string.Format( + "Query option '{0}' is not allowed. " + + "To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", + optionName); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.All & ~exclude, + }; + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + Assert.Equal(expectedMessage, errors.First().Message); + } + [Theory] [MemberData(nameof(SupportedQueryOptions))] [MemberData(nameof(UnsupportedQueryOptions))] @@ -174,6 +231,35 @@ public void AllowedQueryOptions_ThrowIfNoneAllowed(AllowedQueryOptions unused, s ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(SupportedQueryOptions))] + [MemberData(nameof(UnsupportedQueryOptions))] + public void TryValidate_ReturnsFalseAndErrors_WhenQueryOptionIsNotAllowed(AllowedQueryOptions unused, string query, string optionName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var expectedMessage = string.Format( + "Query option '{0}' is not allowed. " + + "To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", + optionName); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.None, + }; + + IEnumerable errors; + + // Act + var result = _validator.TryValidate(option, settings, out errors); + + // Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + Assert.False(result); + Assert.NotNull(errors); + Assert.Equal(expectedMessage, errors.First().Message); + } + [Theory] [MemberData(nameof(SupportedQueryOptions))] public void SupportedQueryOptions_SucceedIfGroupAllowed(AllowedQueryOptions unused, string query, string unusedName) @@ -192,6 +278,30 @@ public void SupportedQueryOptions_SucceedIfGroupAllowed(AllowedQueryOptions unus ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(SupportedQueryOptions))] + public void TryValidate_ReturnsTrue_WhenQueryOptionIsAllowed(AllowedQueryOptions unused, string query, string unusedName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?$" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.Supported, + }; + + IEnumerable errors; + + // Act + var result = _validator.TryValidate(option, settings, out errors); + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + Assert.NotNull(unusedName); + Assert.True(result); + Assert.Empty(errors); + } + [Theory] [MemberData(nameof(SupportedQueryOptions))] public void SupportedQueryOptions_ThrowIfGroupNotAllowed(AllowedQueryOptions unused, string query, string optionName) @@ -288,4 +398,51 @@ public void Validate_ValidatesNotEmptyOrWhitespaceSelectExpandQueryOption_IfEmpt // Act & Assert ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + + [Theory] + [InlineData("$select=")] + [InlineData("$select= ")] + [InlineData("$expand=")] + [InlineData("$expand= ")] + [InlineData("$select= &$expand= &")] + public void TryValidate_ReturnsFalseAndErrors_WhenSelectExpandIsEmptyOrWhitespace(string query) + { + // Arrange + var message = RequestFactory.Create("Get", $"http://localhost/?{query}", setupAction: null); + var options = new ODataQueryOptions(_context, message); + var settings = new ODataValidationSettings(); + var expectedMessage = "'select' and 'expand' cannot be empty or whitespace. Omit the parameter from the query if it is not used."; + + IEnumerable errors; + + // Act + var result = _validator.TryValidate(options, settings, out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + Assert.Single(errors); + Assert.Equal(expectedMessage, errors.First().Message); + } + + [Fact] + public void TryValidate_ValidatesSelectExpandQueryOption_WhenNotNull() + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?$expand=Contacts/Contacts", setupAction: null); + var options = new ODataQueryOptions(_context, message); + var mockValidator = new Mock(); + options.SelectExpand.Validator = mockValidator.Object; + var settings = new ODataValidationSettings(); + + IEnumerable errors; + + // Act + var result = _validator.TryValidate(options, settings, out errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + mockValidator.Verify(v => v.Validate(options.SelectExpand, settings), Times.Once()); + } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs index 1cd15060c..2cd695242 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs @@ -6,9 +6,11 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData; using Microsoft.OData.Edm; using Xunit; @@ -66,5 +68,12 @@ public void Validate(SearchQueryOption searchQueryOption, ODataValidationSetting { Verify?.Invoke(); } + + public bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + Verify?.Invoke(); + validationErrors = null; + return true; + } } } From 415e713334bfabfe750c122da36eb5bbe4de3a07 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 22 Apr 2025 21:04:19 +0300 Subject: [PATCH 02/11] Fix failed tests --- .../Microsoft.AspNetCore.OData.PublicApi.bsl | 29 +++++++++++++++++++ .../Validator/ODataQueryValidatorTest.cs | 22 -------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index 6924f6fc5..04de3e00c 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -1265,6 +1265,7 @@ public class Microsoft.AspNetCore.OData.Query.ComputeQueryOption { System.Type ResultClrType { public get; } Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator Validator { public get; public set; } + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1277,6 +1278,7 @@ public class Microsoft.AspNetCore.OData.Query.CountQueryOption { bool Value { public get; } public System.Nullable`1[[System.Int64]] GetEntityCount (System.Linq.IQueryable query) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1371,6 +1373,7 @@ public class Microsoft.AspNetCore.OData.Query.FilterQueryOption { Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator Validator { public get; public set; } public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1424,6 +1427,7 @@ public class Microsoft.AspNetCore.OData.Query.ODataQueryOptions { public static bool IsSystemQueryOption (string queryOptionName, bool isDollarSignOptional) public static IQueryable`1 LimitResults (IQueryable`1 queryable, int limit, out System.Boolean& resultsLimited) public static IQueryable`1 LimitResults (IQueryable`1 queryable, int limit, bool parameterize, out System.Boolean& resultsLimited) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& errors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1530,6 +1534,7 @@ public class Microsoft.AspNetCore.OData.Query.OrderByQueryOption { public System.Linq.IOrderedQueryable ApplyTo (System.Linq.IQueryable query) public IOrderedQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IOrderedQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1552,6 +1557,7 @@ public class Microsoft.AspNetCore.OData.Query.SearchQueryOption { Microsoft.OData.UriParser.SearchClause SearchClause { public get; } public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1568,6 +1574,7 @@ public class Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption { public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable queryable, Microsoft.AspNetCore.OData.Query.ODataQuerySettings settings) public object ApplyTo (object entity, Microsoft.AspNetCore.OData.Query.ODataQuerySettings settings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1581,6 +1588,7 @@ public class Microsoft.AspNetCore.OData.Query.SkipQueryOption { public IQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1594,6 +1602,7 @@ public class Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption { public virtual IQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) public virtual System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1607,6 +1616,7 @@ public class Microsoft.AspNetCore.OData.Query.TopQueryOption { public IOrderedQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3029,42 +3039,52 @@ public class Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder : M } public interface Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IOrderByQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISkipTokenQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ITopQueryValidator { + bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3082,18 +3102,21 @@ public abstract class Microsoft.AspNetCore.OData.Query.Validator.QueryValidatorC public class Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator : IComputeQueryValidator { public ComputeQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator : ICountQueryValidator { public CountQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator : IFilterQueryValidator { public FilterQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings settings) protected virtual void ValidateAllNode (Microsoft.OData.UriParser.AllNode allNode, Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext validatorContext) protected virtual void ValidateAnyNode (Microsoft.OData.UriParser.AnyNode anyNode, Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext validatorContext) @@ -3136,6 +3159,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext : public class Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator : IODataQueryValidator { public ODataQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3158,6 +3182,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings public class Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator : IOrderByQueryValidator { public OrderByQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) protected virtual void ValidateAllNode (Microsoft.OData.UriParser.AllNode allNode, Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext validatorContext) protected virtual void ValidateAnyNode (Microsoft.OData.UriParser.AnyNode anyNode, Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext validatorContext) @@ -3197,6 +3222,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext public class Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator : ISelectExpandQueryValidator { public SelectExpandQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) protected virtual void ValidateExpandedCountSelectItem (Microsoft.OData.UriParser.ExpandedCountSelectItem expandCountItem, Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorContext validatorContext) protected virtual void ValidateExpandedNavigationSelectItem (Microsoft.OData.UriParser.ExpandedNavigationSelectItem expandItem, Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorContext validatorContext) @@ -3228,18 +3254,21 @@ public class Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorCon public class Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator : ISkipQueryValidator { public SkipQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator : ISkipTokenQueryValidator { public SkipTokenQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator : ITopQueryValidator { public TopQueryValidator () + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs index bd4417e15..9e319e7b0 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs @@ -421,28 +421,6 @@ public void TryValidate_ReturnsFalseAndErrors_WhenSelectExpandIsEmptyOrWhitespac // Assert Assert.False(result); Assert.NotNull(errors); - Assert.Single(errors); Assert.Equal(expectedMessage, errors.First().Message); } - - [Fact] - public void TryValidate_ValidatesSelectExpandQueryOption_WhenNotNull() - { - // Arrange - var message = RequestFactory.Create("Get", "http://localhost/?$expand=Contacts/Contacts", setupAction: null); - var options = new ODataQueryOptions(_context, message); - var mockValidator = new Mock(); - options.SelectExpand.Validator = mockValidator.Object; - var settings = new ODataValidationSettings(); - - IEnumerable errors; - - // Act - var result = _validator.TryValidate(options, settings, out errors); - - // Assert - Assert.True(result); - Assert.Empty(errors); - mockValidator.Verify(v => v.Validate(options.SelectExpand, settings), Times.Once()); - } } From 11497c49bcea3f0c40a76daf5d734b9b678bbe6d Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Thu, 8 May 2025 19:25:03 +0300 Subject: [PATCH 03/11] Added tests --- .../Query/Validator/ComputeQueryValidator.cs | 25 +- .../Validator/ComputeQueryValidatorTests.cs | 49 + .../Validator/CountQueryValidatorTests.cs | 57 ++ .../Validator/FilterQueryValidatorTests.cs | 913 +++++++++++++++++- 4 files changed, 1015 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index 1c8187f78..3d5131942 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -61,14 +61,27 @@ public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataVali errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); } - // so far, we don't have validation rules here for $compute - // because 'DefaultQuerySetting' doesn't have configuration for $compute - // we can only let ODL to parse and verify the compute clause, - // however, developer can override this method add his own rules - _ = computeQueryOption.ComputeClause; + // If there are parameter errors, return early + if (errors.Count > 0) + { + validationErrors = errors; + return false; + } + + try + { + // so far, we don't have validation rules here for $compute + // because 'DefaultQuerySetting' doesn't have configuration for $compute + // we can only let ODL to parse and verify the compute clause, + // however, developer can override this method add his own rules + _ = computeQueryOption.ComputeClause; + } + catch (ODataException ex) + { + errors.Add(ex); + } validationErrors = errors; return errors.Count == 0; } - } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs index 426dc9647..49b04e213 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs @@ -5,6 +5,8 @@ // //------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Tests.Commons; @@ -26,6 +28,16 @@ public void ValidateComputeQueryValidator_ThrowsOnNullOption() () => _validator.Validate(null, new ODataValidationSettings()), "computeQueryOption"); } + [Fact] + public void TryValidateComputeQueryValidator_ReturnsValidationErrorOnNullOption() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'computeQueryOption')", validationErrors.First().Message); + } + [Fact] public void ValidateComputeQueryValidator_ThrowsOnNullSettings() { @@ -34,6 +46,16 @@ public void ValidateComputeQueryValidator_ThrowsOnNullSettings() () => _validator.Validate(new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), null), "validationSettings"); } + [Fact] + public void TryValidateComputeQueryValidator_ReturnsValidationErrorOnNullSettings() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), null, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + } + [Fact] public void ValidateComputeQueryValidator_ThrowsIfWithoutAsInComputeClause() { @@ -45,6 +67,20 @@ public void ValidateComputeQueryValidator_ThrowsIfWithoutAsInComputeClause() "'as' expected at position 13 in 'test add p12m'."); } + [Fact] + public void TryValidateComputeQueryValidator_ReturnsValidationErrorIfWithoutAsInComputeClause() + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new ComputeQueryOption("test add p12m", _context), + new ODataValidationSettings(), + out IEnumerable validationErrors); + + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("'as' expected at position 13 in 'test add p12m'.", validationErrors.First().Message); + } + [Fact] public void ValidateComputeQueryValidator_ThrowsIfUnknownPropertyInComputeClause() { @@ -55,4 +91,17 @@ public void ValidateComputeQueryValidator_ThrowsIfUnknownPropertyInComputeClause new ODataValidationSettings()), "Could not find a property named 'test' on type 'Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomer'."); } + + [Fact] + public void TryValidateComputeQueryValidator_ReturnsValidationErrorIfUnknownPropertyInComputeClause() + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new ComputeQueryOption("test add p12m as Any", _context), + new ODataValidationSettings(), + out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Could not find a property named 'test' on type 'Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomer'.", validationErrors.First().Message); + } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs index 37a6fba6c..1035a6d2b 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs @@ -7,10 +7,12 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Tests.Commons; using Microsoft.AspNetCore.OData.Tests.Models; +using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; @@ -34,6 +36,16 @@ public void ValidateCountQueryValidator_Throws_NullOption() ExceptionAssert.ThrowsArgumentNull(() => _validator.Validate(null, new ODataValidationSettings()), "countQueryOption"); } + [Fact] + public void TryValidateCountQueryValidator_ReturnsFalseAndValidationError_NullOption() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'countQueryOption')", validationErrors.First().Message); + } + [Fact] public void ValidateCountQueryValidator_Throws_NullSettings() { @@ -45,6 +57,20 @@ public void ValidateCountQueryValidator_Throws_NullSettings() ExceptionAssert.ThrowsArgumentNull(() => _validator.Validate(option, null), "validationSettings"); } + [Fact] + public void TryValidateCountQueryValidator_ReturnsFalseAndValidationError_NullSettings() + { + // Arrange + ODataQueryContext context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); + CountQueryOption option = new CountQueryOption("true", context); + + // Act & Assert + var result = _validator.TryValidate(option, null, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + } + [Theory] [InlineData("LimitedEntities(1)/Integers", "The property 'Integers' cannot be used for $count.")] [InlineData("LimitedEntities(1)/ComplexCollectionProperty", "The property 'ComplexCollectionProperty' cannot be used for $count.")] @@ -73,6 +99,37 @@ public void Validate_Throws_DollarCountAppliedOnNotCountableCollection(string ur ExceptionAssert.Throws(() => _validator.Validate(option, settings), message); } + [Theory] + [InlineData("LimitedEntities(1)/Integers", "The property 'Integers' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexCollectionProperty", "The property 'ComplexCollectionProperty' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/EntityCollectionProperty", "The property 'EntityCollectionProperty' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexProperty/Strings", "The property 'Strings' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexProperty/SimpleEnums", "The property 'SimpleEnums' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/EntityCollectionProperty(1)/ComplexCollectionProperty", "The property 'ComplexCollectionProperty' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/Integers/$count", "The property 'Integers' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexCollectionProperty/$count", "The property 'ComplexCollectionProperty' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/EntityCollectionProperty/$count", "The property 'EntityCollectionProperty' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexProperty/Strings/$count", "The property 'Strings' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/ComplexProperty/SimpleEnums/$count", "The property 'SimpleEnums' cannot be used for $count.")] + [InlineData("LimitedEntities(1)/EntityCollectionProperty(1)/ComplexCollectionProperty/$count", "The property 'ComplexCollectionProperty' cannot be used for $count.")] + public void TryValidate_ReturnsFalseAndValidationError_DollarCountAppliedOnNotCountableCollection(string uri, string message) + { + // Arrange + IEdmModel model = GetEdmModel(); + string serviceRoot = "http://localhost/"; + ODataUriParser pathHandler = new ODataUriParser(model, new Uri(serviceRoot), new Uri(uri, UriKind.RelativeOrAbsolute)); + ODataPath path = pathHandler.ParsePath(); + ODataQueryContext context = new ODataQueryContext(model, EdmCoreModel.Instance.GetInt32(false).Definition, path); + CountQueryOption option = new CountQueryOption("true", context); + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(message, validationErrors.First().Message); + } + private static IEdmModel GetEdmModel() { ODataModelBuilder builder = new ODataModelBuilder(); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs index 6e384dee6..d8f989129 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs @@ -42,6 +42,19 @@ public void ValidateFilterQueryValidator_ThrowsOnNullOption() ExceptionAssert.ThrowsArgumentNull(() => _validator.Validate(null, new ODataValidationSettings()), "filterQueryOption"); } + [Fact] + public void TryValidateFilterQueryValidator_ReturnsFalseOnNullOption() + { + // Arrange & Act + bool result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + + // Assert + Assert.False(result); + Assert.NotNull(validationErrors); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'filterQueryOption')", validationErrors.First().Message); + } + [Fact] public void ValidateFilterQueryValidator_ThrowsOnNullSettings() { @@ -49,6 +62,16 @@ public void ValidateFilterQueryValidator_ThrowsOnNullSettings() ExceptionAssert.ThrowsArgumentNull(() => _validator.Validate(new FilterQueryOption("Name eq 'abc'", _context), null), "settings"); } + [Fact] + public void TryValidateFilterQueryValidator_ReturnsFalseOnNullSettings() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(new FilterQueryOption("Name eq 'abc'", _context), null, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + } + // want to test if all the virtual methods are being invoked correctly [Fact] public void ValidateFilterQueryValidator_VisitAll() @@ -70,6 +93,28 @@ public void ValidateFilterQueryValidator_VisitAll() Assert.Equal(2, _validator.Times["ValidateParameterQueryNode"]); // $it, t } + [Fact] + public void TryValidateFilterQueryValidator_VisitAll() + { + // Arrange + FilterQueryOption option = new FilterQueryOption("Tags/all(t: t eq '42')", _context); + + // Act + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + + // Assert + Assert.True(result); + Assert.Empty(validationErrors); + Assert.Equal(7, _validator.Times.Keys.Count); + Assert.Equal(1, _validator.Times["TryValidate"]); // entry + Assert.Equal(1, _validator.Times["ValidateAllQueryNode"]); // all + Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // eq + Assert.Equal(1, _validator.Times["ValidateCollectionPropertyAccessNode"]); // Tags + Assert.Equal(1, _validator.Times["ValidateConstantQueryNode"]); // 42 + Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // eq + Assert.Equal(2, _validator.Times["ValidateParameterQueryNode"]); // $it, t + } + [Fact] public void ValidateFilterQueryValidator_VisitAny() { @@ -90,6 +135,28 @@ public void ValidateFilterQueryValidator_VisitAny() Assert.Equal(2, _validator.Times["ValidateParameterQueryNode"]); // $it, t } + [Fact] + public void TryValidateFilterQueryValidator_VisitAny() + { + // Arrange + FilterQueryOption option = new FilterQueryOption("Tags/any(t: t eq '42')", _context); + + // Act + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + + // Assert + Assert.True(result); + Assert.Empty(validationErrors); + Assert.Equal(7, _validator.Times.Keys.Count); + Assert.Equal(1, _validator.Times["TryValidate"]); // entry + Assert.Equal(1, _validator.Times["ValidateAnyQueryNode"]); // any + Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // eq + Assert.Equal(1, _validator.Times["ValidateCollectionPropertyAccessNode"]); // Tags + Assert.Equal(1, _validator.Times["ValidateConstantQueryNode"]); // 42 + Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // eq + Assert.Equal(2, _validator.Times["ValidateParameterQueryNode"]); // $it, t + } + [Theory] [InlineData("NotFilterableProperty")] [InlineData("NonFilterableProperty")] @@ -103,6 +170,21 @@ public void ValidateFilterQueryValidator_ThrowsIfNotFilterableProperty(string pr string.Format("The property '{0}' cannot be used in the $filter query option.", property)); } + [Theory] + [InlineData("NotFilterableProperty")] + [InlineData("NonFilterableProperty")] + public void TryValidateFilterQueryValidator_ReturnsFalseIfNotFilterableProperty(string property) + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new FilterQueryOption(string.Format("{0} eq 'David'", property), _context), + new ODataValidationSettings(), + out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + } + [Theory] [InlineData("NotFilterableNavigationProperty")] [InlineData("NonFilterableNavigationProperty")] @@ -116,6 +198,22 @@ public void ValidateFilterQueryValidator_ThrowsIfNotFilterableNavigationProperty string.Format("The property '{0}' cannot be used in the $filter query option.", property)); } + [Theory] + [InlineData("NotFilterableNavigationProperty")] + [InlineData("NonFilterableNavigationProperty")] + public void TryValidateFilterQueryValidator_ReturnsFalseIfNotFilterableNavigationProperty(string property) + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new FilterQueryOption(string.Format("{0}/Name eq 'Seattle'", property), _context), + new ODataValidationSettings(), + out IEnumerable validationErrors); + + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + } + [Theory] [InlineData("NotFilterableProperty")] [InlineData("NonFilterableProperty")] @@ -129,6 +227,22 @@ public void ValidateFilterQueryValidator_ThrowsIfNavigationHasNotFilterablePrope string.Format("The property '{0}' cannot be used in the $filter query option.", property)); } + [Theory] + [InlineData("NotFilterableProperty")] + [InlineData("NonFilterableProperty")] + public void TryValidateFilterQueryValidator_ReturnsFalseIfNavigationHasNotFilterableProperty(string property) + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new FilterQueryOption(string.Format("NavigationWithNotFilterableProperty/{0} eq 'David'", property), _context), + new ODataValidationSettings(), + out IEnumerable validationErrors); + + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + } + public static TheoryDataSet NestedAnyAllInputs { get @@ -157,6 +271,21 @@ public void MaxAnyAllExpressionDepthLimitExceeded(string filter) "The Any/All nesting limit of '1' has been exceeded. 'MaxAnyAllExpressionDepth' can be configured on ODataQuerySettings or EnableQueryAttribute."); } + [Theory] + [MemberData(nameof(NestedAnyAllInputs))] + public void MaxAnyAllExpressionDepthLimitExceeded_WithTryValidate(string filter) + { + // Arrange + var settings = new ODataValidationSettings(); + settings.MaxAnyAllExpressionDepth = 1; + + // Act & Assert + var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("The Any/All nesting limit of '1' has been exceeded. 'MaxAnyAllExpressionDepth' can be configured on ODataQuerySettings or EnableQueryAttribute.", validationErrors.First().Message); + } + [Theory] [MemberData(nameof(NestedAnyAllInputs))] public void IncreaseMaxAnyAllExpressionDepthWillAllowNestedAnyAllInputs(string filter) @@ -169,6 +298,20 @@ public void IncreaseMaxAnyAllExpressionDepthWillAllowNestedAnyAllInputs(string f ExceptionAssert.DoesNotThrow(() => _validator.Validate(new FilterQueryOption(filter, _productContext), settings)); } + [Theory] + [MemberData(nameof(NestedAnyAllInputs))] + public void IncreaseMaxAnyAllExpressionDepthWillAllowNestedAnyAllInputs_WithTryValidate(string filter) + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + settings.MaxAnyAllExpressionDepth = 2; + + // Act & Assert + var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + public static TheoryDataSet LongInputs { get @@ -194,6 +337,25 @@ public void LongInputs_CauseMaxNodeCountExceededException(string filter) "The node count limit of '100' has been exceeded. To increase the limit, set the 'MaxNodeCount' property on EnableQueryAttribute or ODataValidationSettings."); } + [Theory] + [MemberData(nameof(LongInputs))] + public void LongInputs_CauseMaxNodeCountExceededException_WithTryValidate(string filter) + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxAnyAllExpressionDepth = Int32.MaxValue + }; + + FilterQueryOption option = new FilterQueryOption(filter, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal("The node count limit of '100' has been exceeded. To increase the limit, set the 'MaxNodeCount' property on EnableQueryAttribute or ODataValidationSettings.", validationErrors.First().Message); + } + [Theory] [MemberData(nameof(LongInputs))] public void IncreaseMaxNodeCountWillAllowLongInputs(string filter) @@ -211,6 +373,25 @@ public void IncreaseMaxNodeCountWillAllowLongInputs(string filter) ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(LongInputs))] + public void IncreaseMaxNodeCountWillAllowLongInputs_WithTryValidate(string filter) + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxAnyAllExpressionDepth = Int32.MaxValue, + MaxNodeCount = 105, + }; + + FilterQueryOption option = new FilterQueryOption(filter, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + public static TheoryDataSet CloseToLongInputs { get @@ -235,6 +416,24 @@ public void AlmostLongInputs_DonotCauseMaxNodeCountExceededExceptionOrTimeoutDur ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(CloseToLongInputs))] + public void AlmostLongInputs_DonotCauseMaxNodeCountExceededExceptionOrTimeoutDuringCompilation_WithTryValidate(string filter) + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxAnyAllExpressionDepth = Int32.MaxValue + }; + + FilterQueryOption option = new FilterQueryOption(filter, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + public static TheoryDataSet ArithmeticOperators { get @@ -297,6 +496,24 @@ public void AllowedArithmeticOperators_SucceedIfAllowed(AllowedArithmeticOperato ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(ArithmeticOperators))] + public void AllowedArithmeticOperators_WithTryValidate_SucceedIfAllowed(AllowedArithmeticOperators allow, string query, string unused) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedArithmeticOperators = allow, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotNull(unused); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(ArithmeticOperators))] public void AllowedArithmeticOperators_ThrowIfNotAllowed(AllowedArithmeticOperators exclude, string query, string operatorName) @@ -316,6 +533,29 @@ public void AllowedArithmeticOperators_ThrowIfNotAllowed(AllowedArithmeticOperat ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(ArithmeticOperators))] + public void AllowedArithmeticOperators_WithTryValidate_ReturnsFalseIfNotAllowed(AllowedArithmeticOperators exclude, string query, string operatorName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedArithmeticOperators = AllowedArithmeticOperators.All & ~exclude, + }; + + var expectedMessage = string.Format( + "Arithmetic operator '{0}' is not allowed. " + + "To allow it, set the 'AllowedArithmeticOperators' property on EnableQueryAttribute or QueryValidationSettings.", + operatorName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + [Theory] [MemberData(nameof(ArithmeticOperators))] public void AllowedArithmeticOperators_ThrowIfNoneAllowed(AllowedArithmeticOperators unused, string query, string operatorName) @@ -336,6 +576,29 @@ public void AllowedArithmeticOperators_ThrowIfNoneAllowed(AllowedArithmeticOpera ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(ArithmeticOperators))] + public void AllowedArithmeticOperators_WithTryValidate_ReturnsFalseIfNoneAllowed(AllowedArithmeticOperators unused, string query, string operatorName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedArithmeticOperators = AllowedArithmeticOperators.None, + }; + var expectedMessage = string.Format( + "Arithmetic operator '{0}' is not allowed. " + + "To allow it, set the 'AllowedArithmeticOperators' property on EnableQueryAttribute or QueryValidationSettings.", + operatorName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedArithmeticOperators); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + public static TheoryDataSet ArithmeticOperators_CheckArguments { get @@ -371,6 +634,23 @@ public void ArithmeticOperators_CheckArguments_SucceedIfAllowed(string query) ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(ArithmeticOperators_CheckArguments))] + public void ArithmeticOperators_CheckArguments_WithTryValidate_SucceedIfAllowed(string query) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.Day, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(ArithmeticOperators_CheckArguments))] public void ArithmeticOperators_CheckArguments_ThrowIfNotAllowed(string query) @@ -389,6 +669,27 @@ public void ArithmeticOperators_CheckArguments_ThrowIfNotAllowed(string query) ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(ArithmeticOperators_CheckArguments))] + public void ArithmeticOperators_CheckArguments_WithTryValidate_ReturnsFalseIfNotAllowed(string query) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.Day, + }; + var expectedMessage = string.Format( + "Function 'day' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings."); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + // No support for OData v4 functions: // maxdatetime, mindatetime, now, totaloffsetminutes, or totalseconds? public static TheoryDataSet DateTimeFunctions @@ -647,21 +948,20 @@ public void AllowedFunctions_SucceedIfAllowed(AllowedFunctions allow, string que [MemberData(nameof(MathFunctions))] [MemberData(nameof(OtherFunctions))] [MemberData(nameof(StringFunctions))] - public void AllowedFunctions_ThrowIfNotAllowed(AllowedFunctions exclude, string query, string functionName) + public void AllowedFunctions_WithTryValidate_SucceedIfAllowed(AllowedFunctions allow, string query, string unused) { // Arrange var settings = new ODataValidationSettings { - AllowedFunctions = AllowedFunctions.AllFunctions & ~exclude, + AllowedFunctions = allow, }; - var expectedMessage = string.Format( - "Function '{0}' is not allowed. " + - "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", - functionName); var option = new FilterQueryOption(query, _productContext); // Act & Assert - ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); + Assert.NotNull(unused); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); } [Theory] @@ -669,12 +969,12 @@ public void AllowedFunctions_ThrowIfNotAllowed(AllowedFunctions exclude, string [MemberData(nameof(MathFunctions))] [MemberData(nameof(OtherFunctions))] [MemberData(nameof(StringFunctions))] - public void AllowedFunctions_ThrowIfNoneAllowed(AllowedFunctions unused, string query, string functionName) + public void AllowedFunctions_ThrowIfNotAllowed(AllowedFunctions exclude, string query, string functionName) { // Arrange var settings = new ODataValidationSettings { - AllowedFunctions = AllowedFunctions.None, + AllowedFunctions = AllowedFunctions.AllFunctions & ~exclude, }; var expectedMessage = string.Format( "Function '{0}' is not allowed. " + @@ -683,35 +983,45 @@ public void AllowedFunctions_ThrowIfNoneAllowed(AllowedFunctions unused, string var option = new FilterQueryOption(query, _productContext); // Act & Assert - Assert.NotEqual(AllowedFunctions.None, unused); ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } [Theory] [MemberData(nameof(DateTimeFunctions))] - public void DateTimeFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + [MemberData(nameof(MathFunctions))] + [MemberData(nameof(OtherFunctions))] + [MemberData(nameof(StringFunctions))] + public void AllowedFunctions_WithTryValidate_ReturnsFalseIfNotAllowed(AllowedFunctions exclude, string query, string functionName) { // Arrange var settings = new ODataValidationSettings { - AllowedFunctions = AllowedFunctions.AllDateTimeFunctions, + AllowedFunctions = AllowedFunctions.AllFunctions & ~exclude, }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); var option = new FilterQueryOption(query, _productContext); // Act & Assert - Assert.NotEqual(AllowedFunctions.None, unused); - Assert.NotNull(unusedName); - ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); } [Theory] [MemberData(nameof(DateTimeFunctions))] - public void DateTimeFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + [MemberData(nameof(MathFunctions))] + [MemberData(nameof(OtherFunctions))] + [MemberData(nameof(StringFunctions))] + public void AllowedFunctions_ThrowIfNoneAllowed(AllowedFunctions unused, string query, string functionName) { // Arrange var settings = new ODataValidationSettings { - AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.AllDateTimeFunctions, + AllowedFunctions = AllowedFunctions.None, }; var expectedMessage = string.Format( "Function '{0}' is not allowed. " + @@ -725,25 +1035,151 @@ public void DateTimeFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, st } [Theory] + [MemberData(nameof(DateTimeFunctions))] [MemberData(nameof(MathFunctions))] - public void MathFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + [MemberData(nameof(OtherFunctions))] + [MemberData(nameof(StringFunctions))] + public void AllowedFunctions_WithTryValidate_ReturnsFalseIfNoneAllowed(AllowedFunctions unused, string query, string functionName) { // Arrange var settings = new ODataValidationSettings { - AllowedFunctions = AllowedFunctions.AllMathFunctions, + AllowedFunctions = AllowedFunctions.None, }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); var option = new FilterQueryOption(query, _productContext); // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); - Assert.NotNull(unusedName); - ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); } [Theory] - [MemberData(nameof(MathFunctions))] - public void MathFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + [MemberData(nameof(DateTimeFunctions))] + public void DateTimeFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllDateTimeFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); + } + + [Theory] + [MemberData(nameof(DateTimeFunctions))] + public void DateTimeFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllDateTimeFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + + [Theory] + [MemberData(nameof(DateTimeFunctions))] + public void DateTimeFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.AllDateTimeFunctions, + }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); + } + + [Theory] + [MemberData(nameof(DateTimeFunctions))] + public void DateTimeFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.AllDateTimeFunctions, + }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + + [Theory] + [MemberData(nameof(MathFunctions))] + public void MathFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllMathFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); + } + + [Theory] + [MemberData(nameof(MathFunctions))] + public void MathFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllMathFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + + [Theory] + [MemberData(nameof(MathFunctions))] + public void MathFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) { // Arrange var settings = new ODataValidationSettings @@ -761,6 +1197,30 @@ public void MathFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(MathFunctions))] + public void MathFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.AllMathFunctions, + }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + [Theory] [MemberData(nameof(StringFunctions))] public void StringFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) @@ -778,6 +1238,26 @@ public void StringFunctions_SucceedIfGroupAllowed(AllowedFunctions unused, strin ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(StringFunctions))] + public void StringFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllStringFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(StringFunctions))] public void StringFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) @@ -798,6 +1278,30 @@ public void StringFunctions_ThrowIfGroupNotAllowed(AllowedFunctions unused, stri ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(StringFunctions))] + public void StringFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(AllowedFunctions unused, string query, string functionName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions & ~AllowedFunctions.AllStringFunctions, + }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + public static TheoryDataSet OtherFunctions_SomeSingleParameterCasts { get @@ -829,6 +1333,28 @@ public void OtherFunctions_SomeSingleParameterCasts_ThrowODataException(AllowedF ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(OtherFunctions_SomeSingleParameterCasts))] + public void OtherFunctions_SomeSingleParameterCasts_WithTryValidate_ReturnsFalseODataException(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.None, + }; + var expectedMessage = "Cast or IsOf Function must have a type in its arguments."; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + public static TheoryDataSet OtherFunctions_SomeTwoParameterCasts { get @@ -901,6 +1427,26 @@ public void OtherFunctions_SomeQuotedTwoParameterCasts_DoesnotThrowArgumentExcep ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(OtherFunctions_SomeQuotedTwoParameterCasts))] + public void OtherFunctions_SomeQuotedTwoParameterCasts_WithTryValidate_ReturnsTrue_NoValidationError(AllowedFunctions unused, string query, string unusedName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, unused); + Assert.NotNull(unusedName); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + public static TheoryDataSet Functions_CheckArguments { get @@ -955,6 +1501,25 @@ public void Functions_CheckArguments_SucceedIfAllowed(AllowedFunctions outer, Al ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(Functions_CheckArguments))] + public void Functions_CheckArguments_WithTryValidate_SucceedIfAllowed(AllowedFunctions outer, AllowedFunctions inner, string query, string unused) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = outer | inner, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotNull(unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(Functions_CheckArguments))] public void Functions_CheckArguments_ThrowIfNotAllowed(AllowedFunctions outer, AllowedFunctions inner, string query, string functionName) @@ -975,6 +1540,29 @@ public void Functions_CheckArguments_ThrowIfNotAllowed(AllowedFunctions outer, A ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(Functions_CheckArguments))] + public void Functions_CheckArguments_WithTryValidate_ReturnsFalseIfNotAllowed(AllowedFunctions outer, AllowedFunctions inner, string query, string functionName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = outer, + }; + var expectedMessage = string.Format( + "Function '{0}' is not allowed. " + + "To allow it, set the 'AllowedFunctions' property on EnableQueryAttribute or QueryValidationSettings.", + functionName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(AllowedFunctions.None, inner); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors, e => e.Message == expectedMessage); + } + public static TheoryDataSet Functions_CheckNotFilterable { get @@ -1016,6 +1604,27 @@ public void Functions_CheckNotFilterable_ThrowODataException(string query, strin ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(Functions_CheckNotFilterable))] + public void Functions_CheckNotFilterable_WithTryValidate_ReturnsFalseODataException(string query, string propertyName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedFunctions = AllowedFunctions.AllFunctions, + }; + var expectedMessage = string.Format( + "The property '{0}' cannot be used in the $filter query option.", + propertyName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + public static TheoryDataSet LogicalOperators { get @@ -1083,6 +1692,25 @@ public void AllowedLogicalOperators_SucceedIfAllowed(AllowedLogicalOperators all ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(LogicalOperators))] + public void AllowedLogicalOperators_WithTryValidate_SucceedIfAllowed(AllowedLogicalOperators allow, string query, string unused) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedLogicalOperators = allow, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotNull(unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(LogicalOperators))] public void AllowedLogicalOperators_ThrowIfNotAllowed(AllowedLogicalOperators exclude, string query, string operatorName) @@ -1102,6 +1730,28 @@ public void AllowedLogicalOperators_ThrowIfNotAllowed(AllowedLogicalOperators ex ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(LogicalOperators))] + public void AllowedLogicalOperators_WithTryValidate_ReturnsFalseIfNotAllowed(AllowedLogicalOperators exclude, string query, string operatorName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedLogicalOperators = AllowedLogicalOperators.All & ~exclude, + }; + var expectedMessage = string.Format( + "Logical operator '{0}' is not allowed. " + + "To allow it, set the 'AllowedLogicalOperators' property on EnableQueryAttribute or QueryValidationSettings.", + operatorName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + [Theory] [MemberData(nameof(LogicalOperators))] public void AllowedLogicalOperators_ThrowIfNoneAllowed(AllowedLogicalOperators unused, string query, string operatorName) @@ -1122,6 +1772,29 @@ public void AllowedLogicalOperators_ThrowIfNoneAllowed(AllowedLogicalOperators u ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(LogicalOperators))] + public void AllowedLogicalOperators_WithTryValidate_ReturnsFalseIfNoneAllowed(AllowedLogicalOperators unused, string query, string operatorName) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedLogicalOperators = AllowedLogicalOperators.None, + }; + var expectedMessage = string.Format( + "Logical operator '{0}' is not allowed. " + + "To allow it, set the 'AllowedLogicalOperators' property on EnableQueryAttribute or QueryValidationSettings.", + operatorName); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedLogicalOperators); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + public static TheoryDataSet LogicalOperators_CheckArguments { get @@ -1165,6 +1838,23 @@ public void LogicalOperators_CheckArguments_SucceedIfAllowed(string query) ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(LogicalOperators_CheckArguments))] + public void LogicalOperators_CheckArguments_WithTryValidate_SucceedIfAllowed(string query) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedArithmeticOperators = AllowedArithmeticOperators.Add, + }; + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Theory] [MemberData(nameof(LogicalOperators_CheckArguments))] public void LogicalOperators_CheckArguments_ThrowIfNotAllowed(string query) @@ -1183,6 +1873,27 @@ public void LogicalOperators_CheckArguments_ThrowIfNotAllowed(string query) ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(LogicalOperators_CheckArguments))] + public void LogicalOperators_CheckArguments_WithTryValidate_ReturnsFalseIfNotAllowed(string query) + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedArithmeticOperators = AllowedArithmeticOperators.All & ~AllowedArithmeticOperators.Add, + }; + var expectedMessage = string.Format( + "Arithmetic operator 'Add' is not allowed. " + + "To allow it, set the 'AllowedArithmeticOperators' property on EnableQueryAttribute or QueryValidationSettings."); + var option = new FilterQueryOption(query, _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + [Fact] public void ArithmeticNegation_SucceedsIfLogicalNotIsAllowed() { @@ -1197,6 +1908,22 @@ public void ArithmeticNegation_SucceedsIfLogicalNotIsAllowed() ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Fact] + public void ArithmeticNegation_WithTryValidate_SucceedsIfLogicalNotIsAllowed() + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedLogicalOperators = AllowedLogicalOperators.LessThan | AllowedLogicalOperators.Not, + }; + var option = new FilterQueryOption("-UnitPrice lt 0", _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + // Note Negate is _not_ a logical operator. [Fact] public void ArithmeticNegation_ThrowsIfLogicalNotIsNotAllowed() @@ -1215,6 +1942,26 @@ public void ArithmeticNegation_ThrowsIfLogicalNotIsNotAllowed() ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Fact] + public void ArithmeticNegation_WithTryValidate_ReturnsFalseIfLogicalNotIsNotAllowed() + { + // Arrange + var settings = new ODataValidationSettings + { + AllowedLogicalOperators = AllowedLogicalOperators.LessThan, + }; + var expectedMessage = string.Format( + "Logical operator 'Negate' is not allowed. " + + "To allow it, set the 'AllowedLogicalOperators' property on EnableQueryAttribute or QueryValidationSettings."); + var option = new FilterQueryOption("-UnitPrice lt 0", _productContext); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + Assert.False(result); + Assert.Single(validationErrors); + Assert.Equal(expectedMessage, validationErrors.First().Message); + } + [Fact] public void ValidateVisitLogicalOperatorEqual() { @@ -1234,6 +1981,27 @@ public void ValidateVisitLogicalOperatorEqual() Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it } + [Fact] + public void ValidateVisitLogicalOperatorEqual_WithTryValidate() + { + // Arrange + FilterQueryOption option = new FilterQueryOption("Id eq 1", _context); + + // Act + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + + // Assert + Assert.True(result); + Assert.Empty(validationErrors); + Assert.Equal(6, _validator.Times.Keys.Count); + Assert.Equal(1, _validator.Times["TryValidate"]); // entry + Assert.Equal(1, _validator.Times["ValidateSingleValuePropertyAccessNode"]); // Id + Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // eq + Assert.Equal(1, _validator.Times["ValidateConstantQueryNode"]); // 1 + Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // eq + Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it + } + [Fact] public void ValidateVisitLogicalOperatorHas() { @@ -1252,6 +2020,26 @@ public void ValidateVisitLogicalOperatorHas() Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it } + [Fact] + public void ValidateVisitLogicalOperatorHas_WithTryValidate() + { + // Arrange + FilterQueryOption option = new FilterQueryOption("FavoriteColor has Microsoft.AspNetCore.OData.Tests.Models.Color'Red'", _context); + + // Act + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + + // Assert + Assert.True(result); + Assert.Empty(validationErrors); + Assert.Equal(6, _validator.Times.Keys.Count); + Assert.Equal(1, _validator.Times["TryValidate"]); // entry + Assert.Equal(1, _validator.Times["ValidateSingleValuePropertyAccessNode"]); // FavouriteColor + Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // has + Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // has + Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it + } + [Theory] [InlineData("Id eq 1")] [InlineData("Id ne 1")] @@ -1297,6 +2085,53 @@ public void Validator_Doesnot_Throw_For_ValidQueries(string filter) ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, _settings)); } + [Theory] + [InlineData("Id eq 1")] + [InlineData("Id ne 1")] + [InlineData("Id gt 1")] + [InlineData("Id lt 1")] + [InlineData("Id ge 1")] + [InlineData("Id le 1")] + [InlineData("Id eq Id add 1")] + [InlineData("Id eq Id sub 1")] + [InlineData("Id eq Id mul 1")] + [InlineData("Id eq Id div 1")] + [InlineData("Id eq Id mod 1")] + [InlineData("startswith(Name, 'Microsoft')")] + [InlineData("endswith(Name, 'Microsoft')")] + [InlineData("contains(Name, 'Microsoft')")] + [InlineData("substring(Name, 1) eq 'Name'")] + [InlineData("substring(Name, 1, 2) eq 'Name'")] + [InlineData("length(Name) eq 1")] + [InlineData("tolower(Name) eq 'Name'")] + [InlineData("toupper(Name) eq 'Name'")] + [InlineData("trim(Name) eq 'Name'")] + [InlineData("indexof(Name, 'Microsoft') eq 1")] + [InlineData("concat(Name, 'Microsoft') eq 'Microsoft'")] + [InlineData("year(Birthday) eq 2000")] + [InlineData("month(Birthday) eq 2000")] + [InlineData("day(Birthday) eq 2000")] + [InlineData("hour(Birthday) eq 2000")] + [InlineData("minute(Birthday) eq 2000")] + [InlineData("round(AmountSpent) eq 0")] + [InlineData("floor(AmountSpent) eq 0")] + [InlineData("ceiling(AmountSpent) eq 0")] + [InlineData("Tags/any()")] + [InlineData("Tags/all(t : t eq '1')")] + [InlineData("Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomerBase/Id eq 1")] + [InlineData("Contacts/Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomerBase/any()")] + [InlineData("FavoriteColor has Microsoft.AspNetCore.OData.Tests.Models.Color'Red'")] + public void Validator_WithTryValidate_ReturnsTrueWithNoError_For_ValidQueries(string filter) + { + // Arrange + FilterQueryOption option = new FilterQueryOption(filter, _context); + + // Act & Assert + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } + [Fact] public void Validator_Doesnot_Throw_For_ParameterAlias() { @@ -1321,6 +2156,32 @@ public void Validator_Doesnot_Throw_For_ParameterAlias() Assert.Equal(1, _validator.Times["ValidateConstantQueryNode"]); // 1 } + [Fact] + public void Validator__WithTryValidate_ReturnsTrueWithNoError_For_ParameterAlias() + { + // Arrange + FilterQueryOption option = new FilterQueryOption( + "Id eq @p", + _context, + new ODataQueryOptionParser( + _context.Model, + _context.ElementType, + _context.NavigationSource, + new Dictionary { { "$filter", "Id eq @p" }, { "@p", "1" } })); + + // Act & Assert + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + Assert.Equal(6, _validator.Times.Keys.Count); + Assert.Equal(1, _validator.Times["TryValidate"]); // entry + Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it + Assert.Equal(1, _validator.Times["ValidateSingleValuePropertyAccessNode"]); // Id + Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // eq + Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // eq + Assert.Equal(1, _validator.Times["ValidateConstantQueryNode"]); // 1 + } + private static TheoryDataSet GetLongInputsTestData(int maxCount) { return new TheoryDataSet @@ -1353,6 +2214,12 @@ public override void Validate(FilterQueryOption filterQueryOption, ODataValidati base.Validate(filterQueryOption, settings); } + public override bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + { + IncrementCount("TryValidate"); + return base.TryValidate(filterQueryOption, validationSettings, out validationErrors); + } + protected override void ValidateAllNode(AllNode allQueryNode, FilterValidatorContext context) { IncrementCount("ValidateAllQueryNode"); From cb961edc9f56935e1bc846a024dc61483da6b062 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 12 May 2025 23:39:24 +0300 Subject: [PATCH 04/11] Added more tests --- .../Query/Validator/FilterQueryValidator.cs | 22 +- .../Query/Validator/ODataQueryValidator.cs | 14 +- .../Query/Validator/SkipQueryValidator.cs | 7 + .../Validator/SkipTokenQueryValidator.cs | 9 +- .../Query/Validator/TopQueryValidator.cs | 7 + .../Validator/ODataQueryValidatorTest.cs | 186 +++++- .../Validator/OrderByQueryValidatorTests.cs | 321 ++++++++++ .../SelectExpandQueryValidatorTest.cs | 569 ++++++++++++++++++ .../Query/Validator/SkipQueryValidatorTest.cs | 87 +++ .../Validator/SkipTokenQueryValidatorTests.cs | 56 ++ .../Query/Validator/TopQueryValidatorTest.cs | 137 +++++ 11 files changed, 1385 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs index 52a5b157c..7f3446638 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs @@ -80,20 +80,20 @@ public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValida return false; } - // Create a validation context - var validatorContext = new FilterValidatorContext - { - Filter = filterQueryOption, - Context = filterQueryOption.Context, - ValidationSettings = validationSettings, - Property = filterQueryOption.Context.TargetProperty, - StructuredType = filterQueryOption.Context.TargetStructuredType, - CurrentDepth = 0 - }; - // Validate the filter clause try { + // Create a validation context + var validatorContext = new FilterValidatorContext + { + Filter = filterQueryOption, + Context = filterQueryOption.Context, + ValidationSettings = validationSettings, + Property = filterQueryOption.Context.TargetProperty, + StructuredType = filterQueryOption.Context.TargetStructuredType, + CurrentDepth = 0 + }; + ValidateFilter(filterQueryOption.FilterClause, validatorContext); } catch (Exception ex) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 7321892e4..8646a95de 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -6,6 +6,7 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Extensions; using Microsoft.OData; @@ -153,13 +154,20 @@ public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettin return false; } - // Helper function to aggregate errors + // To prevent duplicates in the `errors` list, ensure that each error is unique before adding it. + // Modify the `AddValidationErrors` helper function to check for duplicates. + void AddValidationErrors(IEnumerable queryOptionErrors) { - if (queryOptionErrors != null) + if (queryOptionErrors == null) { - errors.AddRange(queryOptionErrors); + return; } + + var uniqueErrors = queryOptionErrors + .Where(error => !errors.Any(e => e.Message == error.Message)); + + errors.AddRange(uniqueErrors); } // Validate each query option diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index 075290901..8b2dce283 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -59,6 +59,13 @@ public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidation errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); } + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + if (skipQueryOption.Value > validationSettings.MaxSkip) { errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value))); diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs index 3949e373d..525fd57e0 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs @@ -63,12 +63,19 @@ public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationS errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); } + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + if (skipToken?.Context != null) { DefaultQueryConfigurations defaultConfigs = skipToken.Context.DefaultQueryConfigurations; if (!defaultConfigs.EnableSkipToken) { - errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, "AllowedQueryOptions"))); + errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, nameof(AllowedQueryOptions)))); } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs index cac80da1d..f58c784bb 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs @@ -77,6 +77,13 @@ public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSe errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); } + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; + return false; + } + if (topQueryOption != null && validationSettings != null && topQueryOption.Value > validationSettings.MaxTop) { errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value))); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs index 9e319e7b0..df38989ea 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs @@ -73,11 +73,8 @@ public void ValidateThrowsOnNullOption() [Fact] public void TryValidate_ReturnsFalseAndErrors_WhenOptionsIsNull() { - // Arrange - IEnumerable errors; - - // Act - var result = _validator.TryValidate(null, new ODataValidationSettings(), out errors); + // Arrange & Act + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); @@ -99,10 +96,9 @@ public void TryValidate_ReturnsFalseAndErrors_WhenValidationSettingsIsNull() { // Arrange var message = RequestFactory.Create(); - IEnumerable errors; // Act - var result = _validator.TryValidate(new ODataQueryOptions(_context, message), null, out errors); + var result = _validator.TryValidate(new ODataQueryOptions(_context, message), null, out IEnumerable errors); // Assert Assert.False(result); @@ -162,6 +158,27 @@ public void AllowedQueryOptions_SucceedIfAllowed(AllowedQueryOptions allow, stri ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(SupportedQueryOptions))] + [MemberData(nameof(UnsupportedQueryOptions))] + public void AllowedQueryOptions_WithTryValidate_SucceedIfAllowed(AllowedQueryOptions allow, string query, string unused) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?$" + query, setupAction: null); + ODataQueryOptions option = new ODataQueryOptions(_context, message); + ODataValidationSettings settings = new ODataValidationSettings() + { + AllowedQueryOptions = allow, + }; + + // Act & Assert + Assert.NotNull(unused); + + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Theory] [MemberData(nameof(SupportedQueryOptions))] [MemberData(nameof(UnsupportedQueryOptions))] @@ -205,8 +222,7 @@ public void AllowedQueryOptions_ReturnsFalseWithErrors_IfNotAllowed_UsingTryVali // Assert Assert.False(result); - Assert.NotNull(errors); - Assert.Equal(expectedMessage, errors.First().Message); + Assert.Equal(expectedMessage, errors?.First().Message); } [Theory] @@ -248,18 +264,43 @@ public void TryValidate_ReturnsFalseAndErrors_WhenQueryOptionIsNotAllowed(Allowe AllowedQueryOptions = AllowedQueryOptions.None, }; - IEnumerable errors; - // Act - var result = _validator.TryValidate(option, settings, out errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); + Assert.False(result); - Assert.NotNull(errors); + Assert.NotEmpty(errors); Assert.Equal(expectedMessage, errors.First().Message); } + [Theory] + [InlineData(AllowedQueryOptions.Filter, "$filter=Name eq 'Name'", "Filter", "Name")] + [InlineData(AllowedQueryOptions.Expand, "$expand=Contacts", "Expand", "Contacts")] + [InlineData(AllowedQueryOptions.Select, "$select=Name", "Select", "Name")] + public void TryValidate_ReturnsFalseAndMultipleErrors_WhenQueryOptionIsNotAllowed(AllowedQueryOptions unused, string query, string optionName, string propertyName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.None, + }; + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + + Assert.False(result); + Assert.Equal(2, errors.Count()); + Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First().Message); + Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last().Message); + } + [Theory] [MemberData(nameof(SupportedQueryOptions))] public void SupportedQueryOptions_SucceedIfGroupAllowed(AllowedQueryOptions unused, string query, string unusedName) @@ -323,6 +364,54 @@ public void SupportedQueryOptions_ThrowIfGroupNotAllowed(AllowedQueryOptions unu ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(SupportedQueryOptions))] + public void SupportedQueryOptions_ReturnsFalseWithValidationError_IfGroupNotAllowed(AllowedQueryOptions unused, string query, string optionName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var expectedMessage = string.Format( + "Query option '{0}' is not allowed. " + + "To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", + optionName); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.All & ~AllowedQueryOptions.Supported, + }; + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Equal(expectedMessage, errors?.First().Message); + } + + [Theory] + [InlineData(AllowedQueryOptions.Filter, "$filter=Name eq 'Name'", "Filter", "Name")] + [InlineData(AllowedQueryOptions.Expand, "$expand=Contacts", "Expand", "Contacts")] + [InlineData(AllowedQueryOptions.Select, "$select=Name", "Select", "Name")] + public void SupportedQueryOptions_ReturnsFalseWithManyValidationErrors_IfGroupNotAllowed(AllowedQueryOptions unused, string query, string optionName, string propertyName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.All & ~AllowedQueryOptions.Supported, + }; + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Equal(2, errors.Count()); + Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First().Message); + Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last().Message); + } + [Theory] [MemberData(nameof(UnsupportedQueryOptions))] public void UnsupportedQueryOptions_SucceedIfGroupAllowed(AllowedQueryOptions unused, string query, string unusedName) @@ -341,6 +430,26 @@ public void UnsupportedQueryOptions_SucceedIfGroupAllowed(AllowedQueryOptions un ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Theory] + [MemberData(nameof(UnsupportedQueryOptions))] + public void UnsupportedQueryOptions_WithTryValidate_SucceedIfGroupAllowed(AllowedQueryOptions unused, string query, string unusedName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?$" + query, setupAction: null); + ODataQueryOptions option = new ODataQueryOptions(_context, message); + ODataValidationSettings settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.All & ~AllowedQueryOptions.Supported, + }; + + // Act & Assert + Assert.Equal(unused, settings.AllowedQueryOptions); //Equal because only Delta token is unsupported. + Assert.NotNull(unusedName); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Theory] [MemberData(nameof(UnsupportedQueryOptions))] public void UnsupportedQueryOptions_ThrowIfGroupNotAllowed(AllowedQueryOptions unused, string query, string optionName) @@ -362,6 +471,31 @@ public void UnsupportedQueryOptions_ThrowIfGroupNotAllowed(AllowedQueryOptions u ExceptionAssert.Throws(() => _validator.Validate(option, settings), expectedMessage); } + [Theory] + [MemberData(nameof(UnsupportedQueryOptions))] + public void UnsupportedQueryOptions_ReturnsFalseWithValidationError_IfGroupNotAllowed(AllowedQueryOptions unused, string query, string optionName) + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?" + query, setupAction: null); + var option = new ODataQueryOptions(_context, message); + var expectedMessage = string.Format( + "Query option '{0}' is not allowed. " + + "To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", + optionName); + var settings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.Supported, + }; + + // Act & Assert + Assert.NotEqual(unused, settings.AllowedQueryOptions); + + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal(expectedMessage, errors.First().Message); + } + [Fact] public void Validate_ValidatesSelectExpandQueryOption_IfItIsNotNull() { @@ -380,6 +514,29 @@ public void Validate_ValidatesSelectExpandQueryOption_IfItIsNotNull() selectExpandValidator.Verify(v => v.Validate(option.SelectExpand, settings), Times.Once()); } + [Fact] + public void TryValidate_ValidatesSelectExpandQueryOption_IfItIsNotNull() + { + // Arrange + var message = RequestFactory.Create("Get", "http://localhost/?$expand=Contacts/Contacts", setupAction: null); + ODataQueryOptions option = new ODataQueryOptions(_context, message); + + Mock selectExpandValidator = new Mock(); + option.SelectExpand.Validator = selectExpandValidator.Object; + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + + // Assert + selectExpandValidator.Verify(v => v.TryValidate(option.SelectExpand, settings, out errors), Times.Once()); + Assert.Empty(errors); + Assert.NotNull(errors); + } + + [Theory] [InlineData("$select=")] [InlineData("$select= ")] @@ -413,10 +570,9 @@ public void TryValidate_ReturnsFalseAndErrors_WhenSelectExpandIsEmptyOrWhitespac var settings = new ODataValidationSettings(); var expectedMessage = "'select' and 'expand' cannot be empty or whitespace. Omit the parameter from the query if it is not used."; - IEnumerable errors; // Act - var result = _validator.TryValidate(options, settings, out errors); + var result = _validator.TryValidate(options, settings, out IEnumerable errors); // Assert Assert.False(result); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs index 7e5c36bb2..7ac0b16ba 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs @@ -37,6 +37,16 @@ public void ValidateOrderByQueryValidator_ThrowsOnNullOption() ExceptionAssert.Throws(() => _validator.Validate(null, new ODataValidationSettings())); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_OnNullOption() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'orderByOption')", errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_ThrowsOnNullSettings() { @@ -44,6 +54,16 @@ public void ValidateOrderByQueryValidator_ThrowsOnNullSettings() ExceptionAssert.Throws(() => _validator.Validate(new OrderByQueryOption("Name eq 'abc'", _context), null)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_OnNullSettings() + { + // Arrange & Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption("Name eq 'abc'", _context), null, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Theory] [InlineData("NotSortableProperty")] [InlineData("UnsortableProperty")] @@ -58,6 +78,21 @@ public void ValidateOrderByQueryValidator_ThrowsNotSortableException_ForNotSorta string.Format("The property '{0}' cannot be used in the $orderby query option.", property)); } + [Theory] + [InlineData("NotSortableProperty")] + [InlineData("UnsortableProperty")] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotSortableProperty_OnEmptyAllowedPropertiesList(string property) + { + // Arrange : empty allowed orderby list + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal($"The property '{property}' cannot be used in the $orderby query option.", errors.First().Message); + } + [Theory] [InlineData("NotSortableProperty")] [InlineData("UnsortableProperty")] @@ -71,6 +106,21 @@ public void ValidateOrderByQueryValidator_DoesntThrowNotSortableException_ForNot _validator.Validate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings); } + [Theory] + [InlineData("NotSortableProperty")] + [InlineData("UnsortableProperty")] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForNotSortableProperty_OnNonEmptyAllowedPropertiesList(string property) + { + // Arrange : nonempty allowed orderby list + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add(property); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_NoException_ForAllowedAndSortableUnlimitedProperty_OnEmptyAllowedPropertiesList() { @@ -81,6 +131,18 @@ public void ValidateOrderByQueryValidator_NoException_ForAllowedAndSortableUnlim ExceptionAssert.DoesNotThrow(() => _validator.Validate(new OrderByQueryOption("Name asc", _context), settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedAndSortableUnlimitedProperty_OnEmptyAllowedPropertiesList() + { + // Arrange: empty allowed orderby list + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_NoException_ForAllowedAndSortableUnlimitedProperty_OnNonEmptyAllowedPropertiesList() { @@ -92,6 +154,19 @@ public void ValidateOrderByQueryValidator_NoException_ForAllowedAndSortableUnlim ExceptionAssert.DoesNotThrow(() => _validator.Validate(new OrderByQueryOption("Name asc", _context), settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedAndSortableUnlimitedProperty_OnNonEmptyAllowedPropertiesList() + { + // Arrange: nonempty allowed orderby list + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Name"); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Theory] [InlineData("NotSortableProperty")] [InlineData("UnsortableProperty")] @@ -107,6 +182,23 @@ public void ValidateOrderByQueryValidator_ThrowsNotAllowedException_ForNotAllowe string.Format("Order by '{0}' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", property)); } + [Theory] + [InlineData("NotSortableProperty")] + [InlineData("UnsortableProperty")] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowedAndSortableLimitedProperty(string property) + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Name"); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal($"Order by '{property}' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_ThrowsNotAllowedException_ForNotAllowedAndSortableUnlimitedProperty() { @@ -119,6 +211,22 @@ public void ValidateOrderByQueryValidator_ThrowsNotAllowedException_ForNotAllowe "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowedAndSortableUnlimitedProperty() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Address"); + + // Act & Assert + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_WillNotAllowName() { @@ -132,6 +240,23 @@ public void ValidateOrderByQueryValidator_WillNotAllowName() "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_WillNotAllowName() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("Name", _context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Id"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_WillNotAllowMultipleProperties() { @@ -148,6 +273,24 @@ public void ValidateOrderByQueryValidator_WillNotAllowMultipleProperties() "Order by 'Id' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowedMultipleProperties() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("Name desc, Id asc", _context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Address"); + settings.AllowedOrderByProperties.Add("Name"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Order by 'Id' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_WillAllowId() { @@ -160,6 +303,20 @@ public void ValidateOrderByQueryValidator_WillAllowId() ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedId() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("Id", _context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("Id"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_AllowsOrderByIt() { @@ -171,6 +328,19 @@ public void ValidateOrderByQueryValidator_AllowsOrderByIt() ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForOrderByIt() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("$it", new ODataQueryContext(EdmCoreModel.Instance, typeof(int))); + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_AllowsOrderByIt_IfExplicitlySpecified() { @@ -182,6 +352,19 @@ public void ValidateOrderByQueryValidator_AllowsOrderByIt_IfExplicitlySpecified( ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForExplicitlyAllowedOrderByIt() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("$it", new ODataQueryContext(EdmCoreModel.Instance, typeof(int))); + ODataValidationSettings settings = new ODataValidationSettings { AllowedOrderByProperties = { "$it" } }; + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_DisallowsOrderByIt_IfTurnedOff() { @@ -197,6 +380,24 @@ public void ValidateOrderByQueryValidator_DisallowsOrderByIt_IfTurnedOff() "Order by '$it' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForDisallowedOrderByIt() + { + // Arrange + _context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); + OrderByQueryOption option = new OrderByQueryOption("$it", _context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("dummy"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Order by '$it' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator__ThrowsCountExceeded() { @@ -210,6 +411,22 @@ public void ValidateOrderByQueryValidator__ThrowsCountExceeded() "The number of clauses in $orderby query option exceeded the maximum number allowed. The maximum number of $orderby clauses allowed is 1."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_WhenCountExceeded() + { + // Arrange + OrderByQueryOption option = new OrderByQueryOption("Name desc, Id asc", _context); + ODataValidationSettings settings = new ODataValidationSettings { MaxOrderByNodeCount = 1 }; + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "The number of clauses in $orderby query option exceeded the maximum number allowed. The maximum number of $orderby clauses allowed is 1.", + errors.First().Message); + } + [Theory] // Works with complex properties [InlineData("ComplexProperty/Value", "LimitedEntity", @@ -245,6 +462,44 @@ public void ValidateOrderByQueryValidator_ThrowsIfTryingToValidateALimitedProper ExceptionAssert.Throws(() => validator.Validate(option, settings), message); } + [Theory] + // Works with complex properties + [InlineData("ComplexProperty/Value", "LimitedEntity", + "The property 'ComplexProperty' cannot be used in the $orderby query option.")] + // Works with simple properties + [InlineData("RelatedEntity/RelatedComplexProperty/NotSortableValue", "LimitedEntity", + "The property 'NotSortableValue' cannot be used in the $orderby query option.")] + [InlineData("RelatedEntity/RelatedComplexProperty/UnsortableValue", "LimitedEntity", + "The property 'UnsortableValue' cannot be used in the $orderby query option.")] + // Works with navigation properties + [InlineData("RelatedEntity/BackReference/Id", "LimitedEntity", + "The property 'BackReference' cannot be used in the $orderby query option.")] + // Works with inheritance + [InlineData("RelatedEntity/NS.LimitedSpecializedEntity/SpecializedComplexProperty/Value", "LimitedEntity", + "The property 'SpecializedComplexProperty' cannot be used in the $orderby query option.")] + // Works with multiple clauses + [InlineData("Id, ComplexProperty/NotSortableValue", "LimitedEntity", + "The property 'NotSortableValue' cannot be used in the $orderby query option.")] + [InlineData("Id, ComplexProperty/UnsortableValue", "LimitedEntity", + "The property 'UnsortableValue' cannot be used in the $orderby query option.")] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_IfTryingToValidateALimitedProperty(string query, string edmTypeName, string message) + { + // Arrange + IEdmModel model = GetEdmModel(); + IEdmEntityType edmType = model.SchemaElements.OfType().Single(t => t.Name == edmTypeName); + ODataQueryContext context = new ODataQueryContext(model, edmType); + context.DefaultQueryConfigurations.EnableOrderBy = true; + OrderByQueryOption option = new OrderByQueryOption(query, context); + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + OrderByQueryValidator validator = new OrderByQueryValidator(); + var result = validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal(message, errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_DoesntThrowIfTheLeafOfThePathIsWithinTheAllowedProperties() { @@ -262,6 +517,24 @@ public void ValidateOrderByQueryValidator_DoesntThrowIfTheLeafOfThePathIsWithinT ExceptionAssert.DoesNotThrow(() => validator.Validate(option, settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_IfTheLeafOfThePathIsWithinTheAllowedProperties() + { + // Arrange + IEdmModel model = GetEdmModel(); + IEdmEntityType edmType = model.SchemaElements.OfType().Single(t => t.Name == "LimitedEntity"); + ODataQueryContext context = new ODataQueryContext(model, edmType); + OrderByQueryOption option = new OrderByQueryOption("ComplexProperty/Value", context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("ComplexProperty"); + settings.AllowedOrderByProperties.Add("Value"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateOrderByQueryValidator_ThrowsIfTheLeafOfThePathIsntWithinTheAllowedProperties() { @@ -280,6 +553,26 @@ public void ValidateOrderByQueryValidator_ThrowsIfTheLeafOfThePathIsntWithinTheA "Order by 'Value' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings."); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_IfTheLeafOfThePathIsntWithinTheAllowedProperties() + { + // Arrange + IEdmModel model = GetEdmModel(); + IEdmEntityType edmType = model.SchemaElements.OfType().Single(t => t.Name == "LimitedEntity"); + ODataQueryContext context = new ODataQueryContext(model, edmType); + OrderByQueryOption option = new OrderByQueryOption("ComplexProperty/Value", context); + ODataValidationSettings settings = new ODataValidationSettings(); + settings.AllowedOrderByProperties.Add("NotSortableProperty"); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Order by 'Value' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } + [Fact] public void ValidateOrderByQueryValidator_NoException_ForParameterAlias() { @@ -306,6 +599,34 @@ public void ValidateOrderByQueryValidator_NoException_ForParameterAlias() ExceptionAssert.DoesNotThrow(() => _validator.Validate(option, settings)); } + [Fact] + public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForParameterAlias() + { + // Arrange + IEdmModel model = GetEdmModel(); + IEdmEntityType edmType = model.SchemaElements.OfType().Single(t => t.Name == "LimitedEntity"); + IEdmEntitySet entitySet = model.FindDeclaredEntitySet("LimitedEntities"); + Assert.NotNull(entitySet); + ODataQueryContext context = new ODataQueryContext(model, edmType); + context.DefaultQueryConfigurations.EnableOrderBy = true; + + OrderByQueryOption option = new OrderByQueryOption( + "@p,@q desc", + context, + new ODataQueryOptionParser( + model, + edmType, + entitySet, + new Dictionary { { "$orderby", "@p,@q desc" }, { "@p", "Id" }, { "@q", "RelatedEntity/Id" } })); + + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act & Assert + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + Assert.True(result); + Assert.Empty(errors); + } + private static IEdmModel GetEdmModel() { ODataModelBuilder builder = new ODataModelBuilder(); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs index 006c9c811..9be367731 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs @@ -6,6 +6,7 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using Microsoft.AspNetCore.OData.Query; @@ -48,6 +49,21 @@ public void ValidateSelectExpandQueryValidator_Throws_NullOption() ExceptionAssert.ThrowsArgumentNull(() => validator.Validate(null, new ODataValidationSettings()), "selectExpandQueryOption"); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_NullOption() + { + // Arrange + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + + // Act + var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'selectExpandQueryOption')", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_NullSettings() { @@ -60,6 +76,23 @@ public void ValidateSelectExpandQueryValidator_Throws_NullSettings() ExceptionAssert.ThrowsArgumentNull(() => validator.Validate(option, null), "validationSettings"); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_NullSettings() + { + // Arrange + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataQueryContext context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); + SelectExpandQueryOption option = new SelectExpandQueryOption("any", null, _queryContext); + + // Act + var result = validator.TryValidate(option, null, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Theory] [InlineData("Orders($expand=Customer)", 1)] [InlineData("Orders,Orders($expand=Customer)", 1)] @@ -82,6 +115,38 @@ public void ValidateSelectExpandQueryValidator_DepthChecks(string expand, int ma () => validator.Validate(selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 })); } + [Theory] + [InlineData("Orders($expand=Customer)", 1)] + [InlineData("Orders,Orders($expand=Customer)", 1)] + [InlineData("Orders($expand=Customer($expand=Orders))", 2)] + [InlineData("Orders($expand=Customer($expand=Orders($expand=Customer($expand=Orders($expand=Customer)))))", 5)] + [InlineData("Orders($expand=NS.SpecialOrder/SpecialCustomer)", 1)] + public void TryValidateSelectExpandQueryValidator_DepthChecks(string expand, int maxExpansionDepth) + { + // Arrange + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = 1; + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), errors.First().Message); + + result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 }, + out errors); + Assert.True(result); + Assert.Empty(errors); + } + [Theory] [InlineData("Orders($expand=Customer)", 1)] [InlineData("Orders,Orders($expand=Customer)", 1)] @@ -115,6 +180,47 @@ public void ValidateSelectExpandQueryValidator_DepthChecks_QuerySettings(string String.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth)); } + [Theory] + [InlineData("Orders($expand=Customer)", 1)] + [InlineData("Orders,Orders($expand=Customer)", 1)] + [InlineData("Orders($expand=Customer($expand=Orders))", 2)] + [InlineData("Orders($expand=Customer($expand=Orders($expand=Customer($expand=Orders($expand=Customer)))))", 5)] + [InlineData("Orders($expand=NS.SpecialOrder/SpecialCustomer)", 1)] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_DepthChecks_QuerySettings(string expand, int maxExpansionDepth) + { + // Arrange + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.Customer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.DefaultQueryConfigurations.EnableExpand = true; + queryContext.RequestContainer = new MockServiceProvider(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = 1; + IEdmStructuredType customerType = + model.Model.SchemaElements.First(e => e.Name.Equals("Customer")) as IEdmStructuredType; + ModelBoundQuerySettings querySettings = new ModelBoundQuerySettings(); + querySettings.ExpandConfigurations.Add("Orders", new ExpandConfiguration + { + ExpandType = SelectExpandType.Allowed, + MaxDepth = maxExpansionDepth + }); + model.Model.SetAnnotationValue(customerType, querySettings); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 }, + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), + errors.First().Message); + } + [Theory] [InlineData("Parent($levels=5)", 4)] [InlineData("Parent($expand=Parent($levels=4))", 4)] @@ -151,6 +257,45 @@ public void ValidateSelectExpandQueryValidator_DepthChecks_DollarLevels(string e new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 })); } + [Theory] + [InlineData("Parent($levels=5)", 4)] + [InlineData("Parent($expand=Parent($levels=4))", 4)] + [InlineData("Parent($expand=Parent($expand=Parent($levels=0)))", 1)] + [InlineData("Parent($expand=Parent($levels=4);$levels=5)", 8)] + [InlineData("Parent($levels=4),DerivedAncestors($levels=5)", 4)] + [InlineData("DerivedAncestors($levels=5),Parent($levels=4)", 4)] + public void TryValidateSelectExpandQueryValidator_DepthChecks_DollarLevels(string expand, int maxExpansionDepth) + { + // Arrange + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.DefaultQueryConfigurations.EnableExpand = true; + context.RequestContainer = new MockServiceProvider(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = 1; + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), errors.First().Message); + + result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 }, + out errors); + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZero_DollarLevels() { @@ -172,6 +317,31 @@ public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZe new ODataValidationSettings { MaxExpansionDepth = 0 })); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpansionDepthIsZero_DollarLevels() + { + // Arrange + string expand = "Parent($expand=Parent($expand=Parent($levels=10)))"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.DefaultQueryConfigurations.EnableExpand = true; + context.RequestContainer = new MockServiceProvider(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = 0 }, + out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_LevelsMaxLiteralExpansionDepthGreaterThanMaxExpansionDepth() { @@ -195,6 +365,33 @@ public void ValidateSelectExpandQueryValidator_Throws_LevelsMaxLiteralExpansionD "'LevelsMaxLiteralExpansionDepth' should be less than or equal to 'MaxExpansionDepth'."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_LevelsMaxLiteralExpansionDepthGreaterThanMaxExpansionDepth() + { + // Arrange + string expand = "Parent($levels=2)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.DefaultQueryConfigurations.EnableExpand = true; + context.RequestContainer = new MockServiceProvider(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = 4; + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = 3 }, + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("'LevelsMaxLiteralExpansionDepth' should be less than or equal to 'MaxExpansionDepth'.", errors.First().Message); + } + [Theory] [InlineData(1)] [InlineData(2)] @@ -219,6 +416,34 @@ public void ValidateSelectExpandQueryValidator_DoesNotThrow_DefaultLevelsMaxLite new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth })); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_DefaultLevelsMaxLiteralExpansionDepth(int maxExpansionDepth) + { + // Arrange + string expand = "Parent($levels=1)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.DefaultQueryConfigurations.EnableExpand = true; + context.RequestContainer = new MockServiceProvider(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, + out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throw_WithInvalidMaxExpansionDepth() { @@ -241,6 +466,30 @@ public void ValidateSelectExpandQueryValidator_Throw_WithInvalidMaxExpansionDept "Value must be greater than or equal to 0. (Parameter 'value')\r\nActual value was -1."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_Throws_WithInvalidMaxExpansionDepth() + { + // Arrange + int maxExpansionDepth = -1; + + string expand = "Parent($levels=1)"; + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.RequestContainer = new MockServiceProvider(); + var validator = context.GetSelectExpandQueryValidator(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + + // Act & Assert + ExceptionAssert.Throws( + () => validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, + out IEnumerable errors), + "Value must be greater than or equal to 0. (Parameter 'value')\r\nActual value was -1."); + } + [Theory] [InlineData(2, 3)] [InlineData(4, 4)] @@ -268,6 +517,37 @@ public void ValidateSelectExpandQueryValidator_DoesNotThrow_LevelsMaxLiteralExpa new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth })); } + [Theory] + [InlineData(2, 3)] + [InlineData(4, 4)] + [InlineData(3, 0)] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_LevelsMaxLiteralExpansionDepthAndMaxExpansionDepth( + int levelsMaxLiteralExpansionDepth, + int maxExpansionDepth) + { + // Arrange + string expand = "Parent($levels=2)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.EntitySet("Entities"); + IEdmModel model = builder.GetEdmModel(); + var context = new ODataQueryContext(model, typeof(ODataLevelsTest.LevelsEntity)); + context.DefaultQueryConfigurations.EnableExpand = true; + context.RequestContainer = new MockServiceProvider(); + var selectExpandQueryOption = new SelectExpandQueryOption(null, expand, context); + selectExpandQueryOption.LevelsMaxLiteralExpansionDepth = levelsMaxLiteralExpansionDepth; + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, + out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowTop() { @@ -284,6 +564,25 @@ public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowTop() "The limit of '2' for Top query has been exceeded. The value from the incoming request is '4'."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAllowTop() + { + // Arrange + string expand = "Orders($top=4)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + _queryContext.DefaultQueryConfigurations.EnableExpand = true; + _queryContext.DefaultQueryConfigurations.MaxTop = 2; + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The limit of '2' for Top query has been exceeded. The value from the incoming request is '4'.", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowCount() { @@ -300,6 +599,25 @@ public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowCount() "The property 'Orders' cannot be used for $count."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAllowCount() + { + // Arrange + string expand = "Orders($count=true)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + _queryContext.DefaultQueryConfigurations.EnableExpand = true; + _queryContext.DefaultQueryConfigurations.EnableCount = false; + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Orders' cannot be used for $count.", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowOrderby() { @@ -316,6 +634,28 @@ public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowOrderby() "The property 'Amount' cannot be used in the $orderby query option."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAllowOrderby() + { + // Arrange + string expand = "Orders($orderby=Amount)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + _queryContext.DefaultQueryConfigurations.EnableExpand = true; + _queryContext.DefaultQueryConfigurations.EnableOrderBy = false; + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings(), + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Amount' cannot be used in the $orderby query option.", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowFilter() { @@ -332,6 +672,28 @@ public void ValidateSelectExpandQueryValidator_Throws_IfNotAllowFilter() "The property 'Amount' cannot be used in the $filter query option."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAllowFilter() + { + // Arrange + string expand = "Orders($filter=Amount eq 42)"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + _queryContext.DefaultQueryConfigurations.EnableExpand = true; + _queryContext.DefaultQueryConfigurations.EnableFilter = false; + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings(), + out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Amount' cannot be used in the $filter query option.", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZero() { @@ -346,6 +708,26 @@ public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZe () => validator.Validate(selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 0 })); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpansionDepthIsZero() + { + // Arrange + string expand = "Orders($expand=Customer($expand=Orders($expand=Customer($expand=Orders($expand=Customer)))))"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + _queryContext.DefaultQueryConfigurations.EnableExpand = true; + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = 0 }, + out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZero_QuerySettings() { @@ -374,6 +756,39 @@ public void ValidateSelectExpandQueryValidator_DoesNotThrow_IfExpansionDepthIsZe () => validator.Validate(selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 0 })); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpansionDepthIsZero_QuerySettings() + { + // Arrange + string expand = "Orders($expand=Customer($expand=Orders($expand=Customer($expand=Orders($expand=Customer)))))"; + SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.Customer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.DefaultQueryConfigurations.EnableExpand = true; + queryContext.RequestContainer = new MockServiceProvider(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); + IEdmStructuredType customerType = + model.Model.SchemaElements.First(e => e.Name.Equals("Customer")) as IEdmStructuredType; + ModelBoundQuerySettings querySettings = new ModelBoundQuerySettings(); + querySettings.ExpandConfigurations.Add("Orders", new ExpandConfiguration + { + ExpandType = SelectExpandType.Allowed, + MaxDepth = 0 + }); + model.Model.SetAnnotationValue(customerType, querySettings); + + // Act + var result = validator.TryValidate( + selectExpandQueryOption, + new ODataValidationSettings { MaxExpansionDepth = 0 }, + out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSelectExpandQueryValidator_ThrowException_IfNotNavigable() { @@ -393,6 +808,31 @@ public void ValidateSelectExpandQueryValidator_ThrowException_IfNotNavigable() "The property 'Orders' cannot be used for navigation."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotNavigable() + { + // Arrange + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.Customer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.RequestContainer = new MockServiceProvider(); + model.Model.SetAnnotationValue( + model.Customer.FindProperty("Orders"), + new QueryableRestrictionsAnnotation(new QueryableRestrictions { NotNavigable = true })); + + string select = "Orders"; + ISelectExpandQueryValidator validator = queryContext.GetSelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(select, null, queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Orders' cannot be used for navigation.", errors.First().Message); + } + [Theory] [InlineData("Customer", "Orders")] [InlineData("SpecialCustomer", "SpecialOrders")] @@ -413,6 +853,34 @@ public void ValidateSelectExpandQueryValidator_ThrowException_IfBaseOrDerivedCla String.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used for navigation.", propertyName)); } + [Theory] + [InlineData("Customer", "Orders")] + [InlineData("SpecialCustomer", "SpecialOrders")] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfBaseOrDerivedClassPropertyNotNavigable(string className, string propertyName) + { + // Arrange + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.SpecialCustomer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.RequestContainer = new MockServiceProvider(); + EdmEntityType classType = (className == "Customer") ? model.Customer : model.SpecialCustomer; + model.Model.SetAnnotationValue(classType.FindProperty(propertyName), new QueryableRestrictionsAnnotation(new QueryableRestrictions { NotNavigable = true })); + + string select = "NS.SpecialCustomer/" + propertyName; + ISelectExpandQueryValidator validator = queryContext.GetSelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(select, null, queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + string.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used for navigation.", propertyName), + errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_ThrowException_IfNotExpandable() { @@ -430,6 +898,29 @@ public void ValidateSelectExpandQueryValidator_ThrowException_IfNotExpandable() "The property 'Orders' cannot be used in the $expand query option."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotExpandable() + { + // Arrange + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.Customer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.RequestContainer = new MockServiceProvider(); + model.Model.SetAnnotationValue(model.Customer.FindProperty("Orders"), new QueryableRestrictionsAnnotation(new QueryableRestrictions { NotExpandable = true })); + + string expand = "Orders"; + ISelectExpandQueryValidator validator = queryContext.GetSelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Orders' cannot be used in the $expand query option.", errors.First().Message); + } + [Fact] public void ValidateSelectExpandQueryValidator_ThrowException_IfNotExpandable_QuerySettings() { @@ -456,6 +947,35 @@ public void ValidateSelectExpandQueryValidator_ThrowException_IfNotExpandable_Qu "The property 'Orders' cannot be used in the $expand query option."); } + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotExpandable_QuerySettings() + { + // Arrange + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.Customer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.RequestContainer = new MockServiceProvider(); + ISelectExpandQueryValidator validator = queryContext.GetSelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, "Orders", queryContext); + IEdmStructuredType customerType = + model.Model.SchemaElements.First(e => e.Name.Equals("Customer")) as IEdmStructuredType; + ModelBoundQuerySettings querySettings = new ModelBoundQuerySettings(); + querySettings.ExpandConfigurations.Add("Orders", new ExpandConfiguration + { + ExpandType = SelectExpandType.Disabled, + MaxDepth = 0 + }); + model.Model.SetAnnotationValue(customerType, querySettings); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The property 'Orders' cannot be used in the $expand query option.", errors.First().Message); + } + [Theory] [InlineData("Customer", "Orders")] [InlineData("SpecialCustomer", "SpecialOrders")] @@ -476,6 +996,55 @@ public void ValidateSelectExpandQueryValidator_ThrowException_IfBaseOrDerivedCla string.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used in the $expand query option.", propertyName)); } + [Theory] + [InlineData("Customer", "Orders")] + [InlineData("SpecialCustomer", "SpecialOrders")] + public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfBaseOrDerivedClassPropertyNotExpandable(string className, string propertyName) + { + // Arrange + CustomersModelWithInheritance model = new CustomersModelWithInheritance(); + model.Model.SetAnnotationValue(model.SpecialCustomer, new ClrTypeAnnotation(typeof(Customer))); + ODataQueryContext queryContext = new ODataQueryContext(model.Model, typeof(Customer)); + queryContext.RequestContainer = new MockServiceProvider(); + EdmEntityType classType = (className == "Customer") ? model.Customer : model.SpecialCustomer; + model.Model.SetAnnotationValue(classType.FindProperty(propertyName), new QueryableRestrictionsAnnotation(new QueryableRestrictions { NotExpandable = true })); + + string expand = "NS.SpecialCustomer/" + propertyName; + ISelectExpandQueryValidator validator = queryContext.GetSelectExpandQueryValidator(); + SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); + + // Act + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + string.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used in the $expand query option.", propertyName), + errors.First().Message); + } + + [Fact] + public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_GetSelectExpandQueryValidator() + { + // Arrange + ODataQueryContext context = null; + + // Act & Assert + Assert.NotNull(context.GetSelectExpandQueryValidator()); + + // Arrange & Act & Assert + context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int)); + Assert.NotNull(context.GetSelectExpandQueryValidator()); + + // Arrange & Act & Assert + IServiceProvider services = new ServiceCollection() + .AddSingleton() + .AddSingleton().BuildServiceProvider(); + context.RequestContainer = services; + Assert.NotNull(context.GetSelectExpandQueryValidator()); + } + [Fact] public void GetSelectExpandQueryValidator_Returns_Validator() { diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs index 910f231d3..3475267a1 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs @@ -6,6 +6,8 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Tests.Commons; @@ -34,6 +36,21 @@ public void ValidateSkipQueryValidator_ThrowsOnNullOption() ExceptionAssert.Throws(() => _validator.Validate(null, new ODataValidationSettings())); } + [Fact] + public void TryValidateSkipQueryValidator_ReturnsFalseWithError_OnNullOption() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act + var result = _validator.TryValidate(null, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'skipQueryOption')", errors.First().Message); + } + [Fact] public void ValidateSkipQueryValidator_ThrowsOnNullSettings() { @@ -41,6 +58,21 @@ public void ValidateSkipQueryValidator_ThrowsOnNullSettings() ExceptionAssert.Throws(() => _validator.Validate(new SkipQueryOption("2", _context), null)); } + [Fact] + public void TryValidateSkipQueryValidator_ReturnsFalseWithError_OnNullSettings() + { + // Arrange + SkipQueryOption option = new SkipQueryOption("2", _context); + + // Act + var result = _validator.TryValidate(option, null, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Fact] public void ValidateSkipQueryValidator_ThrowsWhenLimitIsExceeded() { @@ -55,6 +87,25 @@ public void ValidateSkipQueryValidator_ThrowsWhenLimitIsExceeded() "The limit of '10' for Skip query has been exceeded. The value from the incoming request is '11'."); } + [Fact] + public void TryValidateSkipQueryValidator_ReturnsFalseWithError_WhenLimitIsExceeded() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxSkip = 10 + }; + SkipQueryOption option = new SkipQueryOption("11", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The limit of '10' for Skip query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + } + [Fact] public void ValidateSkipQueryValidator_PassWhenLimitIsReached() { @@ -68,6 +119,24 @@ public void ValidateSkipQueryValidator_PassWhenLimitIsReached() ExceptionAssert.DoesNotThrow(() => _validator.Validate(new SkipQueryOption("10", _context), settings)); } + [Fact] + public void TryValidateSkipQueryValidator_ReturnsTrueWithNoError_WhenLimitIsReached() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxSkip = 10 + }; + SkipQueryOption option = new SkipQueryOption("10", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateSkipQueryValidator_PassWhenLimitIsNotReached() { @@ -81,6 +150,24 @@ public void ValidateSkipQueryValidator_PassWhenLimitIsNotReached() ExceptionAssert.DoesNotThrow(() => _validator.Validate(new SkipQueryOption("9", _context), settings)); } + [Fact] + public void TryValidateSkipQueryValidator_ReturnsTrueWithNoError_WhenLimitIsNotReached() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxSkip = 10 + }; + SkipQueryOption option = new SkipQueryOption("9", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void GetSkipQueryValidator_Returns_Validator() { diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs index e78eb6b59..9bd3c14c3 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs @@ -6,6 +6,9 @@ //------------------------------------------------------------------------------ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Tests.Commons; @@ -25,6 +28,21 @@ public void ValidateSkipTokenQueryValidator_ThrowsOnNullOption() ExceptionAssert.Throws(() => validator.Validate(null, new ODataValidationSettings())); } + [Fact] + public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_OnNullOption() + { + // Arrange + SkipTokenQueryValidator validator = new SkipTokenQueryValidator(); + + // Act + var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'skipToken')", errors.First().Message); + } + [Fact] public void ValidateSkipTokenQueryValidator_ThrowsOnNullSettings() { @@ -38,6 +56,23 @@ public void ValidateSkipTokenQueryValidator_ThrowsOnNullSettings() ExceptionAssert.Throws(() => validator.Validate(query, null)); } + [Fact] + public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_OnNullSettings() + { + // Arrange + SkipTokenQueryValidator validator = new SkipTokenQueryValidator(); + ODataQueryContext context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int), null); + SkipTokenQueryOption query = new SkipTokenQueryOption("abc", context); + + // Act + var result = validator.TryValidate(query, null, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Fact] public void ValidateSkipTokenQueryValidator_ThrowsNotAllowedQueryOption() { @@ -54,4 +89,25 @@ public void ValidateSkipTokenQueryValidator_ThrowsNotAllowedQueryOption() ExceptionAssert.Throws(() => validator.Validate(query, settings), "Query option 'SkipToken' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings."); } + + [Fact] + public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_NotAllowedQueryOption() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + ODataQueryContext context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int), null); + context.DefaultQueryConfigurations.EnableSkipToken = false; + SkipTokenQueryOption query = new SkipTokenQueryOption("abc", context); + SkipTokenQueryValidator validator = new SkipTokenQueryValidator(); + + // Act + var result = validator.TryValidate(query, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal( + "Query option 'SkipToken' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", + errors.First().Message); + } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs index 7aaa60846..9669510f3 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs @@ -6,6 +6,8 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Tests.Commons; @@ -35,6 +37,21 @@ public void ValidateTopQueryValidator_ThrowsOnNullOption() ExceptionAssert.Throws(() => _validator.Validate(null, new ODataValidationSettings())); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsFalseWithError_OnNullOption() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings(); + + // Act + var result = _validator.TryValidate(null, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'topQueryOption')", errors.First().Message); + } + [Fact] public void ValidateTopQueryValidator_ThrowsOnNullSettings() { @@ -42,6 +59,21 @@ public void ValidateTopQueryValidator_ThrowsOnNullSettings() ExceptionAssert.Throws(() => _validator.Validate(new TopQueryOption("2", _context), null)); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsFalseWithError_OnNullSettings() + { + // Arrange + TopQueryOption option = new TopQueryOption("2", _context); + + // Act + var result = _validator.TryValidate(option, null, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + } + [Fact] public void ValidateTopQueryValidator_ThrowsWhenLimitIsExceeded() { @@ -56,6 +88,25 @@ public void ValidateTopQueryValidator_ThrowsWhenLimitIsExceeded() "The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'."); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsFalseWithError_WhenLimitIsExceeded() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxTop = 10 + }; + TopQueryOption option = new TopQueryOption("11", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + } + [Fact] public void ValidateTopQueryValidator_PassWhenLimitIsReached() { @@ -69,6 +120,24 @@ public void ValidateTopQueryValidator_PassWhenLimitIsReached() ExceptionAssert.DoesNotThrow(() => _validator.Validate(new TopQueryOption("10", _context), settings)); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenLimitIsReached() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxTop = 10 + }; + TopQueryOption option = new TopQueryOption("10", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateTopQueryValidator_PassWhenLimitIsNotReached() { @@ -82,6 +151,24 @@ public void ValidateTopQueryValidator_PassWhenLimitIsNotReached() ExceptionAssert.DoesNotThrow(() => _validator.Validate(new TopQueryOption("9", _context), settings)); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenLimitIsNotReached() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxTop = 10 + }; + TopQueryOption option = new TopQueryOption("9", _context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateTopQueryValidator_PassWhenQuerySettingsLimitIsNotReached() { @@ -99,6 +186,30 @@ public void ValidateTopQueryValidator_PassWhenQuerySettingsLimitIsNotReached() ExceptionAssert.DoesNotThrow(() => _validator.Validate(new TopQueryOption("20", context), settings)); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenQuerySettingsLimitIsNotReached() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxTop = 20 + }; + ModelBoundQuerySettings modelBoundQuerySettings = new ModelBoundQuerySettings + { + MaxTop = 20 + }; + ODataQueryContext context = ValidationTestHelper.CreateCustomerContext(); + context.Model.SetAnnotationValue(context.ElementType as IEdmStructuredType, modelBoundQuerySettings); + TopQueryOption option = new TopQueryOption("20", context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.True(result); + Assert.Empty(errors); + } + [Fact] public void ValidateTopQueryValidator_ThrowsWhenQuerySettingsLimitIsExceeded() { @@ -117,6 +228,32 @@ public void ValidateTopQueryValidator_ThrowsWhenQuerySettingsLimitIsExceeded() "The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'."); } + [Fact] + public void TryValidateTopQueryValidator_ReturnsFalseWithError_WhenQuerySettingsLimitIsExceeded() + { + // Arrange + ODataValidationSettings settings = new ODataValidationSettings + { + MaxTop = 20 + }; + ModelBoundQuerySettings modelBoundQuerySettings = new ModelBoundQuerySettings + { + MaxTop = 10 + }; + ODataQueryContext context = ValidationTestHelper.CreateCustomerContext(); + context.Model.SetAnnotationValue(context.ElementType as IEdmStructuredType, modelBoundQuerySettings); + TopQueryOption option = new TopQueryOption("11", context); + + // Act + var result = _validator.TryValidate(option, settings, out IEnumerable errors); + + // Assert + Assert.False(result); + Assert.Single(errors); + Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + } + + [Fact] public void GetTopQueryValidator_Returns_Validator() { From 5416bb88d2373328b13eea60a2c3fbdceaa4c1c3 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 12 May 2025 23:55:35 +0300 Subject: [PATCH 05/11] Add pass test --- .../Query/Validator/ComputeQueryValidatorTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs index 49b04e213..ac19cb097 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs @@ -104,4 +104,15 @@ public void TryValidateComputeQueryValidator_ReturnsValidationErrorIfUnknownProp Assert.Single(validationErrors); Assert.Equal("Could not find a property named 'test' on type 'Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomer'.", validationErrors.First().Message); } + + [Fact] + public void ValidateComputeQueryValidator_DoesNotThrow() + { + // Arrange & Act & Assert + var result = _validator.TryValidate( + new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), + new ODataValidationSettings(), out IEnumerable validationErrors); + Assert.True(result); + Assert.Empty(validationErrors); + } } From 87a28b1fa722342a1714180fe89109375d93a3b8 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 16:09:53 +0300 Subject: [PATCH 06/11] Refactor to use IEnumerable instead of IEnumerable --- .../Microsoft.AspNetCore.OData.xml | 112 +++++++-------- .../PublicAPI.Unshipped.txt | 53 +++---- .../Query/ODataQueryOptions.cs | 18 ++- .../Query/Query/ComputeQueryOption.cs | 17 ++- .../Query/Query/CountQueryOption.cs | 16 ++- .../Query/Query/FilterQueryOption.cs | 16 ++- .../Query/Query/OrderByQueryOption.cs | 15 +- .../Query/Query/SearchQueryOption.cs | 16 ++- .../Query/Query/SelectExpandQueryOption.cs | 16 ++- .../Query/Query/SkipQueryOption.cs | 15 +- .../Query/Query/SkipTokenQueryOption.cs | 16 ++- .../Query/Query/TopQueryOption.cs | 15 +- .../Query/Validator/ComputeQueryValidator.cs | 12 +- .../Query/Validator/CountQueryValidator.cs | 13 +- .../Query/Validator/FilterQueryValidator.cs | 12 +- .../Interfaces/IComputeQueryValidator.cs | 5 +- .../Interfaces/ICountQueryValidator.cs | 5 +- .../Interfaces/IFilterQueryValidator.cs | 5 +- .../Interfaces/IODataQueryValidator.cs | 3 +- .../Interfaces/IOrderByQueryValidator.cs | 5 +- .../Interfaces/ISearchQueryValidator.cs | 5 +- .../Interfaces/ISelectExpandQueryValidator.cs | 5 +- .../Interfaces/ISkipQueryValidator.cs | 5 +- .../Interfaces/ISkipTokenQueryValidator.cs | 5 +- .../Interfaces/ITopQueryValidator.cs | 5 +- .../Query/Validator/ODataQueryValidator.cs | 66 ++++----- .../Query/Validator/OrderByQueryValidator.cs | 12 +- .../Validator/SelectExpandQueryValidator.cs | 19 ++- .../Query/Validator/SkipQueryValidator.cs | 12 +- .../Validator/SkipTokenQueryValidator.cs | 12 +- .../Query/Validator/TopQueryValidator.cs | 14 +- .../Microsoft.AspNetCore.OData.PublicApi.bsl | 58 ++++---- .../Validator/ComputeQueryValidatorTests.cs | 18 +-- .../Validator/CountQueryValidatorTests.cs | 12 +- .../Validator/FilterQueryValidatorTests.cs | 132 +++++++++--------- .../Validator/ODataQueryValidatorTest.cs | 48 +++---- .../Validator/OrderByQueryValidatorTests.cs | 60 ++++---- .../Validator/SearchQueryValidatorTest.cs | 3 +- .../SelectExpandQueryValidatorTest.cs | 72 +++++----- .../Query/Validator/SkipQueryValidatorTest.cs | 16 +-- .../Validator/SkipTokenQueryValidatorTests.cs | 12 +- .../Query/Validator/TopQueryValidatorTest.cs | 22 +-- 42 files changed, 527 insertions(+), 471 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index e8a0493bd..bf263ed91 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -11272,12 +11272,12 @@ The instance which contains all the validation settings. - + Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing validation + When this method returns, contains a collection of instances describing validation errors, if any occurred; otherwise, if validation was successful or no validator is configured. if the validation was successful or no validator is configured; otherwise, The instance which contains all the validation settings. - + Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -11926,13 +11926,13 @@ The instance which contains all the validation settings. - + Attempts to validate the count query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12069,12 +12069,12 @@ The instance which contains all the validation settings. - + Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12228,12 +12228,12 @@ The instance which contains all the validation settings. - + Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12290,12 +12290,12 @@ The instance which contains all the validation settings. - + Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12382,12 +12382,12 @@ The instance which contains all the validation settings. - + Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12447,12 +12447,12 @@ The instance which contains all the validation settings. - + Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12548,12 +12548,12 @@ The instance which contains all the validation settings. - + Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12613,12 +12613,12 @@ The instance which contains all the validation settings. - + Attempts to validate the top query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12711,13 +12711,13 @@ The $compute query. The validation settings. - + Attempts to validate the . The $compute query. The validation settings. - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12734,13 +12734,13 @@ The $count query. The validation settings. - + Attempts to validate the . - When this method returns, contains a collection of instances describing any + When this method returns, contains a collection of instances describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12756,13 +12756,13 @@ The $filter query. The validation settings. - + Attempts to validate the . The $filter query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13062,13 +13062,13 @@ The $compute query. The validation settings. - + Attempts to validate the . The $compute query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13084,13 +13084,13 @@ The $count query. The validation settings. - + Attempts to validate the . - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13106,13 +13106,13 @@ The $filter query. The validation settings. - + Attempts to validate the . The $filter query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13128,7 +13128,7 @@ The OData query options to validate. The validation settings. - + Attempts to validate the . @@ -13150,13 +13150,13 @@ The $orderby query. The validation settings. - + Attempts to validate the . The $orderby query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13172,13 +13172,13 @@ The $search query. The validation settings. - + Attempts to validate the . - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13194,13 +13194,13 @@ The $select and $expand query. The validation settings. - + Attempts to validate the . The $select and $expand query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13216,13 +13216,13 @@ The $skip query. The validation settings. - + Attempts to validate the . The $skip query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13238,13 +13238,13 @@ The $skiptoken query. The validation settings. - + Attempts to validate the . The $skiptoken query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13260,13 +13260,13 @@ The $top query. The validation settings. - + Attempts to validate the . The $top query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13281,7 +13281,7 @@ The OData query to validate. The validation settings. - + Try validates the OData query. @@ -13394,13 +13394,13 @@ The $orderby query. The validation settings. - + Attempts to validate the . The $orderby query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13715,13 +13715,13 @@ The $select and $expand query. The validation settings. - + Attempts to validate the . The $select and $expand query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13875,13 +13875,13 @@ The $skip query. The validation settings. - + Attempts to validate the . The $skip query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13896,13 +13896,13 @@ The $skiptoken query. The validation settings. - + Attempts to validate the . The $skiptoken query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13917,13 +13917,13 @@ The $top query. The validation settings. - + Attempts to validate the . The $top query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 13ea3607a..bb1e5172f 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -1,26 +1,29 @@ -Microsoft.AspNetCore.OData.Query.ComputeQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.CountQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.FilterQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.OrderByQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.SearchQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.SkipQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.TopQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -Microsoft.AspNetCore.OData.Query.Validator.ITopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable errors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.CountQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.FilterQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.OrderByQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SearchQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.TopQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IOrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ITopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool virtual Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs index e591dd3ca..83a94c5f2 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs @@ -658,16 +658,24 @@ public virtual void Validate(ODataValidationSettings validationSettings) /// Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing validation + /// When this method returns, contains a collection of instances describing validation /// errors, if any occurred; otherwise, if validation was successful or no validator is /// configured. /// if the validation was successful or no validator is configured; otherwise, if validation failed. - public virtual bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable errors) + public virtual bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { + validationErrors = errors; return false; } @@ -675,10 +683,10 @@ public virtual bool TryValidate(ODataValidationSettings validationSettings, out if (Validator != null) { - return Validator.TryValidate(this, validationSettings, out errors); + return Validator.TryValidate(this, validationSettings, out validationErrors); } - errors = null; + validationErrors = null; return true; } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index f06e4c23a..93d39841d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -144,19 +143,27 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; + if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index 9696aa31d..821bd06bd 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -10,7 +10,6 @@ using System.Diagnostics.Contracts; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.UriParser; namespace Microsoft.AspNetCore.OData.Query; @@ -138,19 +137,26 @@ public void Validate(ODataValidationSettings validationSettings) /// It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs index b73b14393..1b5271dbd 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs @@ -11,7 +11,6 @@ using System.Linq; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -194,19 +193,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs index 65b7130e1..8f789b423 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs @@ -249,19 +249,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs index b57ea50c7..98d288c05 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs @@ -10,7 +10,6 @@ using System.Linq; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -178,19 +177,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; ISearchQueryValidator validator = Context.GetSearchQueryValidator(); if (validator != null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs index d5913ed91..28a6364dd 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs @@ -11,7 +11,6 @@ using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; using Microsoft.OData.ModelBuilder.Config; @@ -294,19 +293,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs index c8f7ecca4..f6365edb2 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs @@ -163,19 +163,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs index 2852208ce..af18cbae2 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; -using Microsoft.OData; using Microsoft.OData.Edm; namespace Microsoft.AspNetCore.OData.Query; @@ -107,19 +106,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs index 720c2be28..51c6bbb0c 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs @@ -163,19 +163,26 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the top query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { + List errors = new List(); + if (validationSettings == null) { - List errors = new List { new(Error.ArgumentNull(nameof(validationSettings)).Message) }; + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } + + // If there are parameter errors, return early + if (errors.Count != 0) + { validationErrors = errors; return false; } - validationErrors = Enumerable.Empty(); + validationErrors = errors; if (Validator != null) { Validator.TryValidate(this, validationSettings, out validationErrors); diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index 3d5131942..6ce122b3e 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -44,21 +44,21 @@ public virtual void Validate(ComputeQueryOption computeQueryOption, ODataValidat /// /// The $compute query. /// The validation settings. - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (computeQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(computeQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(computeQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -78,7 +78,7 @@ public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataVali } catch (ODataException ex) { - errors.Add(ex); + errors.Add(ex.Message); } validationErrors = errors; diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs index c2aedb0ac..8d4fb32ce 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.OData.Edm; -using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -65,21 +64,21 @@ public virtual void Validate(CountQueryOption countQueryOption, ODataValidationS /// /// /// - /// When this method returns, contains a collection of instances describing any + /// When this method returns, contains a collection of instances describing any /// validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (countQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(countQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(countQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -104,7 +103,7 @@ public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidati ? Error.Format(SRResources.NotCountableEntitySetUsedForCount, name) : Error.Format(SRResources.NotCountablePropertyUsedForCount, name); - errors.Add(new ODataException(errorMessage)); + errors.Add(errorMessage); } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs index 7f3446638..1acecdc3f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs @@ -56,21 +56,21 @@ public virtual void Validate(FilterQueryOption filterQueryOption, ODataValidatio /// /// The $filter query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); // Validate input parameters if (filterQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(filterQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(filterQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -98,7 +98,7 @@ public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValida } catch (Exception ex) { - errors.Add(new ODataException(ex.Message)); + errors.Add(ex.Message); } // Set the output parameter diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs index 667c16592..c0ba23903 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface IComputeQueryValidator /// /// The $compute query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs index ff348f289..54d325f2b 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ICountQueryValidator /// /// /// - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs index f1a9517ef..cce45d915 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface IFilterQueryValidator /// /// The $filter query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs index b56cc31ee..5b15f1439 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -30,5 +29,5 @@ public interface IODataQueryValidator /// The validation settings. /// The collection of validation errors. /// True if the validation succeeded; otherwise, false. - bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs index a53de52ab..a3afb8860 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface IOrderByQueryValidator /// /// The $orderby query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs index d80384a0f..03407ebca 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ISearchQueryValidator /// /// /// - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs index 1bf333396..de47ee961 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ISelectExpandQueryValidator /// /// The $select and $expand query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs index 3299a1424..3acff2c4f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ISkipQueryValidator /// /// The $skip query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs index 2fe81df87..c5777b3b4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ISkipTokenQueryValidator /// /// The $skiptoken query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs index 27b6acb45..e984abc32 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs @@ -6,7 +6,6 @@ //------------------------------------------------------------------------------ using System.Collections.Generic; -using Microsoft.OData; namespace Microsoft.AspNetCore.OData.Query.Validator; @@ -28,7 +27,7 @@ public interface ITopQueryValidator /// /// The $top query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); + bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 8646a95de..b4cce90d8 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -132,19 +132,19 @@ public virtual void Validate(ODataQueryOptions options, ODataValidationSettings /// The settings used for validation. /// The collection of validation errors. /// True if validation is successful; otherwise, false. - public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); // Validate input parameters if (options == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(options)).Message)); + errors.Add(Error.ArgumentNull(nameof(options)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -157,7 +157,7 @@ public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettin // To prevent duplicates in the `errors` list, ensure that each error is unique before adding it. // Modify the `AddValidationErrors` helper function to check for duplicates. - void AddValidationErrors(IEnumerable queryOptionErrors) + void AddValidationErrors(IEnumerable queryOptionErrors) { if (queryOptionErrors == null) { @@ -165,7 +165,7 @@ void AddValidationErrors(IEnumerable queryOptionErrors) } var uniqueErrors = queryOptionErrors - .Where(error => !errors.Any(e => e.Message == error.Message)); + .Where(error => !errors.Any(e => e == error)); errors.AddRange(uniqueErrors); } @@ -173,10 +173,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) // Validate each query option if (options.Compute != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out IEnumerable computeErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out IEnumerable computeErrors); AddValidationErrors(computeErrors); - if (!options.Compute.TryValidate(validationSettings, out IEnumerable computeValidationErrors)) + if (!options.Compute.TryValidate(validationSettings, out IEnumerable computeValidationErrors)) { AddValidationErrors(computeValidationErrors); } @@ -184,16 +184,16 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Apply?.ApplyClause != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out IEnumerable applyErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out IEnumerable applyErrors); AddValidationErrors(applyErrors); } if (options.Skip != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out IEnumerable skipErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out IEnumerable skipErrors); AddValidationErrors(skipErrors); - if (!options.Skip.TryValidate(validationSettings, out IEnumerable skipValidationErrors)) + if (!options.Skip.TryValidate(validationSettings, out IEnumerable skipValidationErrors)) { AddValidationErrors(skipValidationErrors); } @@ -201,10 +201,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Top != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out IEnumerable topErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out IEnumerable topErrors); AddValidationErrors(topErrors); - if (!options.Top.TryValidate(validationSettings, out IEnumerable topValidationErrors)) + if (!options.Top.TryValidate(validationSettings, out IEnumerable topValidationErrors)) { AddValidationErrors(topValidationErrors); } @@ -212,10 +212,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.OrderBy != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out IEnumerable orderByErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out IEnumerable orderByErrors); AddValidationErrors(orderByErrors); - if (!options.OrderBy.TryValidate(validationSettings, out IEnumerable orderByValidationErrors)) + if (!options.OrderBy.TryValidate(validationSettings, out IEnumerable orderByValidationErrors)) { AddValidationErrors(orderByValidationErrors); } @@ -223,10 +223,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Filter != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out IEnumerable filterErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out IEnumerable filterErrors); AddValidationErrors(filterErrors); - if (!options.Filter.TryValidate(validationSettings, out IEnumerable filterValidationErrors)) + if (!options.Filter.TryValidate(validationSettings, out IEnumerable filterValidationErrors)) { AddValidationErrors(filterValidationErrors); } @@ -234,10 +234,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Search != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out IEnumerable searchErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out IEnumerable searchErrors); AddValidationErrors(searchErrors); - if (!options.Search.TryValidate(validationSettings, out IEnumerable searchValidationErrors)) + if (!options.Search.TryValidate(validationSettings, out IEnumerable searchValidationErrors)) { AddValidationErrors(searchValidationErrors); } @@ -245,10 +245,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Count != null || options.Request.IsCountRequest()) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out IEnumerable countErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out IEnumerable countErrors); AddValidationErrors(countErrors); - if (options.Count?.TryValidate(validationSettings, out IEnumerable countValidationErrors) == false) + if (options.Count?.TryValidate(validationSettings, out IEnumerable countValidationErrors) == false) { AddValidationErrors(countValidationErrors); } @@ -256,10 +256,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.SkipToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out IEnumerable skipTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out IEnumerable skipTokenErrors); AddValidationErrors(skipTokenErrors); - if (!options.SkipToken.TryValidate(validationSettings, out IEnumerable skipTokenValidationErrors)) + if (!options.SkipToken.TryValidate(validationSettings, out IEnumerable skipTokenValidationErrors)) { AddValidationErrors(skipTokenValidationErrors); } @@ -268,20 +268,20 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Expand != null) { TryValidateNotEmptyOrWhitespace(options.RawValues.Expand, errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out IEnumerable expandErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out IEnumerable expandErrors); AddValidationErrors(expandErrors); } if (options.RawValues.Select != null) { TryValidateNotEmptyOrWhitespace(options.RawValues.Select, errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out IEnumerable selectErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out IEnumerable selectErrors); AddValidationErrors(selectErrors); } if (options.SelectExpand != null) { - if (!options.SelectExpand.TryValidate(validationSettings, out IEnumerable selectExpandValidationErrors)) + if (!options.SelectExpand.TryValidate(validationSettings, out IEnumerable selectExpandValidationErrors)) { AddValidationErrors(selectExpandValidationErrors); } @@ -289,13 +289,13 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Format != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out IEnumerable formatErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out IEnumerable formatErrors); AddValidationErrors(formatErrors); } if (options.RawValues.DeltaToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out IEnumerable deltaTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out IEnumerable deltaTokenErrors); AddValidationErrors(deltaTokenErrors); } @@ -312,12 +312,12 @@ private static void ValidateQueryOptionAllowed(AllowedQueryOptions queryOption, } } - private static void TryValidateQueryOptionAllowed(AllowedQueryOptions queryOption, AllowedQueryOptions allowed, out IEnumerable validationErrors) + private static void TryValidateQueryOptionAllowed(AllowedQueryOptions queryOption, AllowedQueryOptions allowed, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if ((queryOption & allowed) == AllowedQueryOptions.None) { - errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions)))); + errors.Add(Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions))); } validationErrors = errors; @@ -331,11 +331,11 @@ private static void ValidateNotEmptyOrWhitespace(string rawValue) } } - private static void TryValidateNotEmptyOrWhitespace(string rawValue, List validationErrors) + private static void TryValidateNotEmptyOrWhitespace(string rawValue, List validationErrors) { if (rawValue != null && string.IsNullOrWhiteSpace(rawValue)) { - validationErrors.Add(new ODataException(SRResources.SelectExpandEmptyOrWhitespace)); + validationErrors.Add(SRResources.SelectExpandEmptyOrWhitespace); } } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs index 13a2716fa..eb72f7924 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs @@ -62,20 +62,20 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe /// /// The $orderby query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (orderByOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(orderByOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(orderByOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -106,7 +106,7 @@ public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidatio } catch(Exception ex) { - errors.Add(new ODataException(ex.Message)); + errors.Add(ex.Message); } clause = clause.ThenBy; diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs index 0aee3c5be..58d4d924a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs @@ -76,20 +76,20 @@ public virtual void Validate(SelectExpandQueryOption selectExpandQueryOption, OD /// /// The $select and $expand query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (selectExpandQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(selectExpandQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(selectExpandQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -121,10 +121,9 @@ public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, } else if (selectExpandQueryOption.LevelsMaxLiteralExpansionDepth > validationSettings.MaxExpansionDepth) { - throw new ODataException(Error.Format( - SRResources.InvalidExpansionDepthValue, - "LevelsMaxLiteralExpansionDepth", - "MaxExpansionDepth")); + errors.Add(Error.Format(SRResources.InvalidExpansionDepthValue, "LevelsMaxLiteralExpansionDepth", "MaxExpansionDepth")); + validationErrors = errors; + return false; } ValidateDepth(selectExpandQueryOption.SelectExpandClause, validationSettings.MaxExpansionDepth); @@ -132,7 +131,7 @@ public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, } catch (Exception ex) { - errors.Add(new ODataException(ex.Message)); + errors.Add(ex.Message); } // If there are any errors, return false diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index 8b2dce283..32be9d6ce 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -43,20 +43,20 @@ public virtual void Validate(SkipQueryOption skipQueryOption, ODataValidationSet /// /// The $skip query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (skipQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(skipQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(skipQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -68,7 +68,7 @@ public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidation if (skipQueryOption.Value > validationSettings.MaxSkip) { - errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value))); + errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value)); } // If there are any errors, return false diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs index 525fd57e0..bc4cf35ac 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs @@ -47,20 +47,20 @@ public virtual void Validate(SkipTokenQueryOption skipToken, ODataValidationSett /// /// The $skiptoken query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (skipToken == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(skipToken)).Message)); + errors.Add(Error.ArgumentNull(nameof(skipToken)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -75,7 +75,7 @@ public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationS DefaultQueryConfigurations defaultConfigs = skipToken.Context.DefaultQueryConfigurations; if (!defaultConfigs.EnableSkipToken) { - errors.Add(new ODataException(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, nameof(AllowedQueryOptions)))); + errors.Add(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, nameof(AllowedQueryOptions))); } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs index f58c784bb..b23af8df1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs @@ -61,20 +61,20 @@ public virtual void Validate(TopQueryOption topQueryOption, ODataValidationSetti /// /// The $top query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = new List(); if (topQueryOption == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(topQueryOption)).Message)); + errors.Add(Error.ArgumentNull(nameof(topQueryOption)).Message); } if (validationSettings == null) { - errors.Add(new ODataException(Error.ArgumentNull(nameof(validationSettings)).Message)); + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } // If there are parameter errors, return early @@ -86,7 +86,7 @@ public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSe if (topQueryOption != null && validationSettings != null && topQueryOption.Value > validationSettings.MaxTop) { - errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value))); + errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value)); } // If there are parameter errors, return early @@ -107,7 +107,7 @@ public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSe topQueryOption.Value, topQueryOption.Context.DefaultQueryConfigurations, out maxTop)) { - errors.Add(new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value))); + errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value)); } // If there are any errors, return false diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index 04de3e00c..f722615a9 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -1265,7 +1265,7 @@ public class Microsoft.AspNetCore.OData.Query.ComputeQueryOption { System.Type ResultClrType { public get; } Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator Validator { public get; public set; } - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1278,7 +1278,7 @@ public class Microsoft.AspNetCore.OData.Query.CountQueryOption { bool Value { public get; } public System.Nullable`1[[System.Int64]] GetEntityCount (System.Linq.IQueryable query) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1373,7 +1373,7 @@ public class Microsoft.AspNetCore.OData.Query.FilterQueryOption { Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator Validator { public get; public set; } public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1427,7 +1427,7 @@ public class Microsoft.AspNetCore.OData.Query.ODataQueryOptions { public static bool IsSystemQueryOption (string queryOptionName, bool isDollarSignOptional) public static IQueryable`1 LimitResults (IQueryable`1 queryable, int limit, out System.Boolean& resultsLimited) public static IQueryable`1 LimitResults (IQueryable`1 queryable, int limit, bool parameterize, out System.Boolean& resultsLimited) - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& errors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1534,7 +1534,7 @@ public class Microsoft.AspNetCore.OData.Query.OrderByQueryOption { public System.Linq.IOrderedQueryable ApplyTo (System.Linq.IQueryable query) public IOrderedQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IOrderedQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1557,7 +1557,7 @@ public class Microsoft.AspNetCore.OData.Query.SearchQueryOption { Microsoft.OData.UriParser.SearchClause SearchClause { public get; } public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1574,7 +1574,7 @@ public class Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption { public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable queryable, Microsoft.AspNetCore.OData.Query.ODataQuerySettings settings) public object ApplyTo (object entity, Microsoft.AspNetCore.OData.Query.ODataQuerySettings settings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1588,7 +1588,7 @@ public class Microsoft.AspNetCore.OData.Query.SkipQueryOption { public IQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1602,7 +1602,7 @@ public class Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption { public virtual IQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) public virtual System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -1616,7 +1616,7 @@ public class Microsoft.AspNetCore.OData.Query.TopQueryOption { public IOrderedQueryable`1 ApplyTo (IQueryable`1 query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) public System.Linq.IQueryable ApplyTo (System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) - public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public bool TryValidate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3039,52 +3039,52 @@ public class Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder : M } public interface Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.IOrderByQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ISkipTokenQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public interface Microsoft.AspNetCore.OData.Query.Validator.ITopQueryValidator { - bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) void Validate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3102,21 +3102,21 @@ public abstract class Microsoft.AspNetCore.OData.Query.Validator.QueryValidatorC public class Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator : IComputeQueryValidator { public ComputeQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator : ICountQueryValidator { public CountQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator : IFilterQueryValidator { public FilterQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings settings) protected virtual void ValidateAllNode (Microsoft.OData.UriParser.AllNode allNode, Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext validatorContext) protected virtual void ValidateAnyNode (Microsoft.OData.UriParser.AnyNode anyNode, Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext validatorContext) @@ -3159,7 +3159,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext : public class Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator : IODataQueryValidator { public ODataQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } @@ -3182,7 +3182,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings public class Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator : IOrderByQueryValidator { public OrderByQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) protected virtual void ValidateAllNode (Microsoft.OData.UriParser.AllNode allNode, Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext validatorContext) protected virtual void ValidateAnyNode (Microsoft.OData.UriParser.AnyNode anyNode, Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext validatorContext) @@ -3222,7 +3222,7 @@ public class Microsoft.AspNetCore.OData.Query.Validator.OrderByValidatorContext public class Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator : ISelectExpandQueryValidator { public SelectExpandQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) protected virtual void ValidateExpandedCountSelectItem (Microsoft.OData.UriParser.ExpandedCountSelectItem expandCountItem, Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorContext validatorContext) protected virtual void ValidateExpandedNavigationSelectItem (Microsoft.OData.UriParser.ExpandedNavigationSelectItem expandItem, Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorContext validatorContext) @@ -3254,21 +3254,21 @@ public class Microsoft.AspNetCore.OData.Query.Validator.SelectExpandValidatorCon public class Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator : ISkipQueryValidator { public SkipQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator : ISkipTokenQueryValidator { public SkipTokenQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator : ITopQueryValidator { public TopQueryValidator () - public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.ODataException, Microsoft.OData.Core, Version=8.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]& validationErrors) + public virtual bool TryValidate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable`1[[System.String]]& validationErrors) public virtual void Validate (Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs index ac19cb097..2379be8a3 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ComputeQueryValidatorTests.cs @@ -32,10 +32,10 @@ public void ValidateComputeQueryValidator_ThrowsOnNullOption() public void TryValidateComputeQueryValidator_ReturnsValidationErrorOnNullOption() { // Arrange & Act & Assert - var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'computeQueryOption')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'computeQueryOption')", validationErrors.First()); } [Fact] @@ -50,10 +50,10 @@ public void ValidateComputeQueryValidator_ThrowsOnNullSettings() public void TryValidateComputeQueryValidator_ReturnsValidationErrorOnNullSettings() { // Arrange & Act & Assert - var result = _validator.TryValidate(new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), null, out IEnumerable validationErrors); + var result = _validator.TryValidate(new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), null, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First()); } [Fact] @@ -74,11 +74,11 @@ public void TryValidateComputeQueryValidator_ReturnsValidationErrorIfWithoutAsIn var result = _validator.TryValidate( new ComputeQueryOption("test add p12m", _context), new ODataValidationSettings(), - out IEnumerable validationErrors); + out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("'as' expected at position 13 in 'test add p12m'.", validationErrors.First().Message); + Assert.Equal("'as' expected at position 13 in 'test add p12m'.", validationErrors.First()); } [Fact] @@ -99,10 +99,10 @@ public void TryValidateComputeQueryValidator_ReturnsValidationErrorIfUnknownProp var result = _validator.TryValidate( new ComputeQueryOption("test add p12m as Any", _context), new ODataValidationSettings(), - out IEnumerable validationErrors); + out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Could not find a property named 'test' on type 'Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomer'.", validationErrors.First().Message); + Assert.Equal("Could not find a property named 'test' on type 'Microsoft.AspNetCore.OData.Tests.Query.Models.QueryCompositionCustomer'.", validationErrors.First()); } [Fact] @@ -111,7 +111,7 @@ public void ValidateComputeQueryValidator_DoesNotThrow() // Arrange & Act & Assert var result = _validator.TryValidate( new ComputeQueryOption("substring(Name, 0, 1) as FirstChar", _context), - new ODataValidationSettings(), out IEnumerable validationErrors); + new ODataValidationSettings(), out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs index 1035a6d2b..0fbe60fa8 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/CountQueryValidatorTests.cs @@ -40,10 +40,10 @@ public void ValidateCountQueryValidator_Throws_NullOption() public void TryValidateCountQueryValidator_ReturnsFalseAndValidationError_NullOption() { // Arrange & Act & Assert - var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'countQueryOption')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'countQueryOption')", validationErrors.First()); } [Fact] @@ -65,10 +65,10 @@ public void TryValidateCountQueryValidator_ReturnsFalseAndValidationError_NullSe CountQueryOption option = new CountQueryOption("true", context); // Act & Assert - var result = _validator.TryValidate(option, null, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, null, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First()); } [Theory] @@ -124,10 +124,10 @@ public void TryValidate_ReturnsFalseAndValidationError_DollarCountAppliedOnNotCo ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(message, validationErrors.First().Message); + Assert.Equal(message, validationErrors.First()); } private static IEdmModel GetEdmModel() diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs index d8f989129..b0f308e5d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs @@ -46,13 +46,13 @@ public void ValidateFilterQueryValidator_ThrowsOnNullOption() public void TryValidateFilterQueryValidator_ReturnsFalseOnNullOption() { // Arrange & Act - bool result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); + bool result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable validationErrors); // Assert Assert.False(result); Assert.NotNull(validationErrors); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'filterQueryOption')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'filterQueryOption')", validationErrors.First()); } [Fact] @@ -66,10 +66,10 @@ public void ValidateFilterQueryValidator_ThrowsOnNullSettings() public void TryValidateFilterQueryValidator_ReturnsFalseOnNullSettings() { // Arrange & Act & Assert - var result = _validator.TryValidate(new FilterQueryOption("Name eq 'abc'", _context), null, out IEnumerable validationErrors); + var result = _validator.TryValidate(new FilterQueryOption("Name eq 'abc'", _context), null, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", validationErrors.First()); } // want to test if all the virtual methods are being invoked correctly @@ -100,7 +100,7 @@ public void TryValidateFilterQueryValidator_VisitAll() FilterQueryOption option = new FilterQueryOption("Tags/all(t: t eq '42')", _context); // Act - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); // Assert Assert.True(result); @@ -142,7 +142,7 @@ public void TryValidateFilterQueryValidator_VisitAny() FilterQueryOption option = new FilterQueryOption("Tags/any(t: t eq '42')", _context); // Act - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); // Assert Assert.True(result); @@ -179,10 +179,10 @@ public void TryValidateFilterQueryValidator_ReturnsFalseIfNotFilterableProperty( var result = _validator.TryValidate( new FilterQueryOption(string.Format("{0} eq 'David'", property), _context), new ODataValidationSettings(), - out IEnumerable validationErrors); + out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First()); } [Theory] @@ -207,11 +207,11 @@ public void TryValidateFilterQueryValidator_ReturnsFalseIfNotFilterableNavigatio var result = _validator.TryValidate( new FilterQueryOption(string.Format("{0}/Name eq 'Seattle'", property), _context), new ODataValidationSettings(), - out IEnumerable validationErrors); + out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First()); } [Theory] @@ -236,11 +236,11 @@ public void TryValidateFilterQueryValidator_ReturnsFalseIfNavigationHasNotFilter var result = _validator.TryValidate( new FilterQueryOption(string.Format("NavigationWithNotFilterableProperty/{0} eq 'David'", property), _context), new ODataValidationSettings(), - out IEnumerable validationErrors); + out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First().Message); + Assert.Equal(string.Format("The property '{0}' cannot be used in the $filter query option.", property), validationErrors.First()); } public static TheoryDataSet NestedAnyAllInputs @@ -280,10 +280,10 @@ public void MaxAnyAllExpressionDepthLimitExceeded_WithTryValidate(string filter) settings.MaxAnyAllExpressionDepth = 1; // Act & Assert - var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("The Any/All nesting limit of '1' has been exceeded. 'MaxAnyAllExpressionDepth' can be configured on ODataQuerySettings or EnableQueryAttribute.", validationErrors.First().Message); + Assert.Equal("The Any/All nesting limit of '1' has been exceeded. 'MaxAnyAllExpressionDepth' can be configured on ODataQuerySettings or EnableQueryAttribute.", validationErrors.First()); } [Theory] @@ -307,7 +307,7 @@ public void IncreaseMaxAnyAllExpressionDepthWillAllowNestedAnyAllInputs_WithTryV settings.MaxAnyAllExpressionDepth = 2; // Act & Assert - var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(new FilterQueryOption(filter, _productContext), settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -350,10 +350,10 @@ public void LongInputs_CauseMaxNodeCountExceededException_WithTryValidate(string FilterQueryOption option = new FilterQueryOption(filter, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal("The node count limit of '100' has been exceeded. To increase the limit, set the 'MaxNodeCount' property on EnableQueryAttribute or ODataValidationSettings.", validationErrors.First().Message); + Assert.Equal("The node count limit of '100' has been exceeded. To increase the limit, set the 'MaxNodeCount' property on EnableQueryAttribute or ODataValidationSettings.", validationErrors.First()); } [Theory] @@ -387,7 +387,7 @@ public void IncreaseMaxNodeCountWillAllowLongInputs_WithTryValidate(string filte FilterQueryOption option = new FilterQueryOption(filter, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -418,7 +418,7 @@ public void AlmostLongInputs_DonotCauseMaxNodeCountExceededExceptionOrTimeoutDur [Theory] [MemberData(nameof(CloseToLongInputs))] - public void AlmostLongInputs_DonotCauseMaxNodeCountExceededExceptionOrTimeoutDuringCompilation_WithTryValidate(string filter) + public void AlmostLongInputs_DoNotCauseMaxNodeCountExceededExceptionOrTimeoutDuringCompilation_WithTryValidate(string filter) { // Arrange ODataValidationSettings settings = new ODataValidationSettings @@ -429,7 +429,7 @@ public void AlmostLongInputs_DonotCauseMaxNodeCountExceededExceptionOrTimeoutDur FilterQueryOption option = new FilterQueryOption(filter, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -509,7 +509,7 @@ public void AllowedArithmeticOperators_WithTryValidate_SucceedIfAllowed(AllowedA // Act & Assert Assert.NotNull(unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -550,10 +550,10 @@ public void AllowedArithmeticOperators_WithTryValidate_ReturnsFalseIfNotAllowed( var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -593,10 +593,10 @@ public void AllowedArithmeticOperators_WithTryValidate_ReturnsFalseIfNoneAllowed // Act & Assert Assert.NotEqual(unused, settings.AllowedArithmeticOperators); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } public static TheoryDataSet ArithmeticOperators_CheckArguments @@ -646,7 +646,7 @@ public void ArithmeticOperators_CheckArguments_WithTryValidate_SucceedIfAllowed( var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -684,10 +684,10 @@ public void ArithmeticOperators_CheckArguments_WithTryValidate_ReturnsFalseIfNot var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } // No support for OData v4 functions: @@ -959,7 +959,7 @@ public void AllowedFunctions_WithTryValidate_SucceedIfAllowed(AllowedFunctions a // Act & Assert Assert.NotNull(unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1005,10 +1005,10 @@ public void AllowedFunctions_WithTryValidate_ReturnsFalseIfNotAllowed(AllowedFun var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -1054,10 +1054,10 @@ public void AllowedFunctions_WithTryValidate_ReturnsFalseIfNoneAllowed(AllowedFu // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -1091,7 +1091,7 @@ public void DateTimeFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunct // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1134,10 +1134,10 @@ public void DateTimeFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(Allo // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -1172,7 +1172,7 @@ public void MathFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunctions Assert.NotEqual(AllowedFunctions.None, unused); Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1215,10 +1215,10 @@ public void MathFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(AllowedF // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -1253,7 +1253,7 @@ public void StringFunctions_WithTryValidate_SucceedIfGroupAllowed(AllowedFunctio Assert.NotEqual(AllowedFunctions.None, unused); Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1296,10 +1296,10 @@ public void StringFunctions_WithTryValidate_ReturnsFalseIfGroupNotAllowed(Allowe // Act & Assert Assert.NotEqual(AllowedFunctions.None, unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } public static TheoryDataSet OtherFunctions_SomeSingleParameterCasts @@ -1349,10 +1349,10 @@ public void OtherFunctions_SomeSingleParameterCasts_WithTryValidate_ReturnsFalse Assert.NotEqual(AllowedFunctions.None, unused); Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } public static TheoryDataSet OtherFunctions_SomeTwoParameterCasts @@ -1442,7 +1442,7 @@ public void OtherFunctions_SomeQuotedTwoParameterCasts_WithTryValidate_ReturnsTr Assert.NotEqual(AllowedFunctions.None, unused); Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1515,7 +1515,7 @@ public void Functions_CheckArguments_WithTryValidate_SucceedIfAllowed(AllowedFun // Act & Assert Assert.NotNull(unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1558,9 +1558,9 @@ public void Functions_CheckArguments_WithTryValidate_ReturnsFalseIfNotAllowed(Al // Act & Assert Assert.NotEqual(AllowedFunctions.None, inner); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); - Assert.Single(validationErrors, e => e.Message == expectedMessage); + Assert.Single(validationErrors, e => e == expectedMessage); } public static TheoryDataSet Functions_CheckNotFilterable @@ -1619,10 +1619,10 @@ public void Functions_CheckNotFilterable_WithTryValidate_ReturnsFalseODataExcept var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } public static TheoryDataSet LogicalOperators @@ -1706,7 +1706,7 @@ public void AllowedLogicalOperators_WithTryValidate_SucceedIfAllowed(AllowedLogi // Act & Assert Assert.NotNull(unused); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1746,10 +1746,10 @@ public void AllowedLogicalOperators_WithTryValidate_ReturnsFalseIfNotAllowed(All var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Theory] @@ -1789,10 +1789,10 @@ public void AllowedLogicalOperators_WithTryValidate_ReturnsFalseIfNoneAllowed(Al // Act & Assert Assert.NotEqual(unused, settings.AllowedLogicalOperators); - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } public static TheoryDataSet LogicalOperators_CheckArguments @@ -1850,7 +1850,7 @@ public void LogicalOperators_CheckArguments_WithTryValidate_SucceedIfAllowed(str var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1888,10 +1888,10 @@ public void LogicalOperators_CheckArguments_WithTryValidate_ReturnsFalseIfNotAll var option = new FilterQueryOption(query, _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Fact] @@ -1919,7 +1919,7 @@ public void ArithmeticNegation_WithTryValidate_SucceedsIfLogicalNotIsAllowed() var option = new FilterQueryOption("-UnitPrice lt 0", _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -1956,10 +1956,10 @@ public void ArithmeticNegation_WithTryValidate_ReturnsFalseIfLogicalNotIsNotAllo var option = new FilterQueryOption("-UnitPrice lt 0", _productContext); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, settings, out IEnumerable validationErrors); Assert.False(result); Assert.Single(validationErrors); - Assert.Equal(expectedMessage, validationErrors.First().Message); + Assert.Equal(expectedMessage, validationErrors.First()); } [Fact] @@ -1988,7 +1988,7 @@ public void ValidateVisitLogicalOperatorEqual_WithTryValidate() FilterQueryOption option = new FilterQueryOption("Id eq 1", _context); // Act - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); // Assert Assert.True(result); @@ -2027,7 +2027,7 @@ public void ValidateVisitLogicalOperatorHas_WithTryValidate() FilterQueryOption option = new FilterQueryOption("FavoriteColor has Microsoft.AspNetCore.OData.Tests.Models.Color'Red'", _context); // Act - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); // Assert Assert.True(result); @@ -2127,7 +2127,7 @@ public void Validator_WithTryValidate_ReturnsTrueWithNoError_For_ValidQueries(st FilterQueryOption option = new FilterQueryOption(filter, _context); // Act & Assert - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); } @@ -2170,7 +2170,7 @@ public void Validator__WithTryValidate_ReturnsTrueWithNoError_For_ParameterAlias new Dictionary { { "$filter", "Id eq @p" }, { "@p", "1" } })); // Act & Assert - var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); + var result = _validator.TryValidate(option, _settings, out IEnumerable validationErrors); Assert.True(result); Assert.Empty(validationErrors); Assert.Equal(6, _validator.Times.Keys.Count); @@ -2214,7 +2214,7 @@ public override void Validate(FilterQueryOption filterQueryOption, ODataValidati base.Validate(filterQueryOption, settings); } - public override bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public override bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { IncrementCount("TryValidate"); return base.TryValidate(filterQueryOption, validationSettings, out validationErrors); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs index df38989ea..cd4654aa9 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs @@ -74,12 +74,12 @@ public void ValidateThrowsOnNullOption() public void TryValidate_ReturnsFalseAndErrors_WhenOptionsIsNull() { // Arrange & Act - var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.NotNull(errors); - Assert.Equal("Value cannot be null. (Parameter 'options')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'options')", errors.First()); } [Fact] @@ -98,12 +98,12 @@ public void TryValidate_ReturnsFalseAndErrors_WhenValidationSettingsIsNull() var message = RequestFactory.Create(); // Act - var result = _validator.TryValidate(new ODataQueryOptions(_context, message), null, out IEnumerable errors); + var result = _validator.TryValidate(new ODataQueryOptions(_context, message), null, out IEnumerable errors); // Assert Assert.False(result); Assert.NotNull(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First()); } [Fact] @@ -174,7 +174,7 @@ public void AllowedQueryOptions_WithTryValidate_SucceedIfAllowed(AllowedQueryOpt // Act & Assert Assert.NotNull(unused); - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -218,11 +218,11 @@ public void AllowedQueryOptions_ReturnsFalseWithErrors_IfNotAllowed_UsingTryVali }; // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.False(result); - Assert.Equal(expectedMessage, errors?.First().Message); + Assert.Equal(expectedMessage, errors?.First()); } [Theory] @@ -265,14 +265,14 @@ public void TryValidate_ReturnsFalseAndErrors_WhenQueryOptionIsNotAllowed(Allowe }; // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); Assert.False(result); Assert.NotEmpty(errors); - Assert.Equal(expectedMessage, errors.First().Message); + Assert.Equal(expectedMessage, errors.First()); } [Theory] @@ -290,15 +290,15 @@ public void TryValidate_ReturnsFalseAndMultipleErrors_WhenQueryOptionIsNotAllowe }; // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); Assert.False(result); Assert.Equal(2, errors.Count()); - Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First().Message); - Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last().Message); + Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First()); + Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last()); } [Theory] @@ -331,7 +331,7 @@ public void TryValidate_ReturnsTrue_WhenQueryOptionIsAllowed(AllowedQueryOptions AllowedQueryOptions = AllowedQueryOptions.Supported, }; - IEnumerable errors; + IEnumerable errors; // Act var result = _validator.TryValidate(option, settings, out errors); @@ -383,9 +383,9 @@ public void SupportedQueryOptions_ReturnsFalseWithValidationError_IfGroupNotAllo // Act & Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); - Assert.Equal(expectedMessage, errors?.First().Message); + Assert.Equal(expectedMessage, errors?.First()); } [Theory] @@ -405,11 +405,11 @@ public void SupportedQueryOptions_ReturnsFalseWithManyValidationErrors_IfGroupNo // Act & Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Equal(2, errors.Count()); - Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First().Message); - Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last().Message); + Assert.Equal($"Query option '{optionName}' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", errors.First()); + Assert.Equal($"The property '{propertyName}' cannot be used in the ${optionName.ToLower()} query option.", errors.Last()); } [Theory] @@ -445,7 +445,7 @@ public void UnsupportedQueryOptions_WithTryValidate_SucceedIfGroupAllowed(Allowe // Act & Assert Assert.Equal(unused, settings.AllowedQueryOptions); //Equal because only Delta token is unsupported. Assert.NotNull(unusedName); - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -490,10 +490,10 @@ public void UnsupportedQueryOptions_ReturnsFalseWithValidationError_IfGroupNotAl // Act & Assert Assert.NotEqual(unused, settings.AllowedQueryOptions); - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); - Assert.Equal(expectedMessage, errors.First().Message); + Assert.Equal(expectedMessage, errors.First()); } [Fact] @@ -526,7 +526,7 @@ public void TryValidate_ValidatesSelectExpandQueryOption_IfItIsNotNull() ODataValidationSettings settings = new ODataValidationSettings(); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); @@ -572,11 +572,11 @@ public void TryValidate_ReturnsFalseAndErrors_WhenSelectExpandIsEmptyOrWhitespac // Act - var result = _validator.TryValidate(options, settings, out IEnumerable errors); + var result = _validator.TryValidate(options, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.NotNull(errors); - Assert.Equal(expectedMessage, errors.First().Message); + Assert.Equal(expectedMessage, errors.First()); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs index 7ac0b16ba..5c3af41be 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/OrderByQueryValidatorTests.cs @@ -41,10 +41,10 @@ public void ValidateOrderByQueryValidator_ThrowsOnNullOption() public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_OnNullOption() { // Arrange & Act & Assert - var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + var result = _validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'orderByOption')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'orderByOption')", errors.First()); } [Fact] @@ -58,10 +58,10 @@ public void ValidateOrderByQueryValidator_ThrowsOnNullSettings() public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_OnNullSettings() { // Arrange & Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption("Name eq 'abc'", _context), null, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption("Name eq 'abc'", _context), null, out IEnumerable errors); Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')",errors.First()); } [Theory] @@ -87,10 +87,10 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotSortabl ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); - Assert.Equal($"The property '{property}' cannot be used in the $orderby query option.", errors.First().Message); + Assert.Equal($"The property '{property}' cannot be used in the $orderby query option.",errors.First()); } [Theory] @@ -116,7 +116,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForNotSortab settings.AllowedOrderByProperties.Add(property); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -138,7 +138,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedAn ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -162,7 +162,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedAn settings.AllowedOrderByProperties.Add("Name"); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -192,11 +192,11 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowed settings.AllowedOrderByProperties.Add("Name"); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption(string.Format("{0} asc", property), _context), settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal($"Order by '{property}' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -219,12 +219,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowed settings.AllowedOrderByProperties.Add("Address"); // Act & Assert - var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); + var result = _validator.TryValidate(new OrderByQueryOption("Name asc", _context), settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -249,12 +249,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_WillNotAllowN settings.AllowedOrderByProperties.Add("Id"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "Order by 'Name' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -283,12 +283,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForNotAllowed settings.AllowedOrderByProperties.Add("Name"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "Order by 'Id' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -312,7 +312,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForAllowedId settings.AllowedOrderByProperties.Add("Id"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -336,7 +336,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForOrderByIt ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -360,7 +360,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForExplicitl ODataValidationSettings settings = new ODataValidationSettings { AllowedOrderByProperties = { "$it" } }; // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -390,12 +390,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_ForDisallowed settings.AllowedOrderByProperties.Add("dummy"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "Order by '$it' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -419,12 +419,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_WhenCountExce ODataValidationSettings settings = new ODataValidationSettings { MaxOrderByNodeCount = 1 }; // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "The number of clauses in $orderby query option exceeded the maximum number allowed. The maximum number of $orderby clauses allowed is 1.", - errors.First().Message); + errors.First()); } [Theory] @@ -494,10 +494,10 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_IfTryingToVal // Act & Assert OrderByQueryValidator validator = new OrderByQueryValidator(); - var result = validator.TryValidate(option, settings, out IEnumerable errors); + var result = validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); - Assert.Equal(message, errors.First().Message); + Assert.Equal(message,errors.First()); } [Fact] @@ -530,7 +530,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_IfTheLeafOfT settings.AllowedOrderByProperties.Add("Value"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } @@ -565,12 +565,12 @@ public void TryValidateOrderByQueryValidator_ReturnsFalseWithError_IfTheLeafOfTh settings.AllowedOrderByProperties.Add("NotSortableProperty"); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.False(result); Assert.Single(errors); Assert.Equal( "Order by 'Value' is not allowed. To allow it, set the 'AllowedOrderByProperties' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } [Fact] @@ -622,7 +622,7 @@ public void TryValidateOrderByQueryValidator_ReturnsTrueWithNoError_ForParameter ODataValidationSettings settings = new ODataValidationSettings(); // Act & Assert - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); Assert.True(result); Assert.Empty(errors); } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs index 2cd695242..9de1492b6 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SearchQueryValidatorTest.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OData; using Microsoft.OData.Edm; using Xunit; @@ -69,7 +68,7 @@ public void Validate(SearchQueryOption searchQueryOption, ODataValidationSetting Verify?.Invoke(); } - public bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + public bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { Verify?.Invoke(); validationErrors = null; diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs index 9be367731..d94484bfe 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SelectExpandQueryValidatorTest.cs @@ -56,12 +56,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_NullOpti SelectExpandQueryValidator validator = new SelectExpandQueryValidator(); // Act - var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'selectExpandQueryOption')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'selectExpandQueryOption')",errors.First()); } [Fact] @@ -85,12 +85,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_NullSett SelectExpandQueryOption option = new SelectExpandQueryOption("any", null, _queryContext); // Act - var result = validator.TryValidate(option, null, out IEnumerable errors); + var result = validator.TryValidate(option, null, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')",errors.First()); } [Theory] @@ -132,12 +132,12 @@ public void TryValidateSelectExpandQueryValidator_DepthChecks(string expand, int var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), errors.First().Message); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth),errors.First()); result = validator.TryValidate( selectExpandQueryOption, @@ -211,14 +211,14 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_DepthChe var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth + 1 }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); Assert.Equal( string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), - errors.First().Message); + errors.First()); } [Theory] @@ -281,12 +281,12 @@ public void TryValidateSelectExpandQueryValidator_DepthChecks_DollarLevels(strin var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth), errors.First().Message); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, MaxExpandDepthExceededErrorString, maxExpansionDepth),errors.First()); result = validator.TryValidate( selectExpandQueryOption, @@ -335,7 +335,7 @@ public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpan var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 0 }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.True(result); @@ -384,12 +384,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_LevelsMa var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 3 }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("'LevelsMaxLiteralExpansionDepth' should be less than or equal to 'MaxExpansionDepth'.", errors.First().Message); + Assert.Equal("'LevelsMaxLiteralExpansionDepth' should be less than or equal to 'MaxExpansionDepth'.",errors.First()); } [Theory] @@ -437,7 +437,7 @@ public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_Default var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.True(result); @@ -486,7 +486,7 @@ public void TryValidateSelectExpandQueryValidator_Throws_WithInvalidMaxExpansion () => validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, - out IEnumerable errors), + out IEnumerable errors), "Value must be greater than or equal to 0. (Parameter 'value')\r\nActual value was -1."); } @@ -541,7 +541,7 @@ public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_LevelsM var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = maxExpansionDepth }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.True(result); @@ -575,12 +575,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAll SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The limit of '2' for Top query has been exceeded. The value from the incoming request is '4'.", errors.First().Message); + Assert.Equal("The limit of '2' for Top query has been exceeded. The value from the incoming request is '4'.",errors.First()); } [Fact] @@ -610,12 +610,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAll SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, _queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Orders' cannot be used for $count.", errors.First().Message); + Assert.Equal("The property 'Orders' cannot be used for $count.",errors.First()); } [Fact] @@ -648,12 +648,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAll var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings(), - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Amount' cannot be used in the $orderby query option.", errors.First().Message); + Assert.Equal("The property 'Amount' cannot be used in the $orderby query option.",errors.First()); } [Fact] @@ -686,12 +686,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotAll var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings(), - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Amount' cannot be used in the $filter query option.", errors.First().Message); + Assert.Equal("The property 'Amount' cannot be used in the $filter query option.",errors.First()); } [Fact] @@ -721,7 +721,7 @@ public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpan var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 0 }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.True(result); @@ -782,7 +782,7 @@ public void TryValidateSelectExpandQueryValidator_ReturnsTrueWithNoError_IfExpan var result = validator.TryValidate( selectExpandQueryOption, new ODataValidationSettings { MaxExpansionDepth = 0 }, - out IEnumerable errors); + out IEnumerable errors); // Assert Assert.True(result); @@ -825,12 +825,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotNav SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(select, null, queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Orders' cannot be used for navigation.", errors.First().Message); + Assert.Equal("The property 'Orders' cannot be used for navigation.",errors.First()); } [Theory] @@ -871,14 +871,14 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfBaseOr SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(select, null, queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); Assert.Equal( string.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used for navigation.", propertyName), - errors.First().Message); + errors.First()); } [Fact] @@ -913,12 +913,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotExp SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Orders' cannot be used in the $expand query option.", errors.First().Message); + Assert.Equal("The property 'Orders' cannot be used in the $expand query option.",errors.First()); } [Fact] @@ -968,12 +968,12 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfNotExp model.Model.SetAnnotationValue(customerType, querySettings); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The property 'Orders' cannot be used in the $expand query option.", errors.First().Message); + Assert.Equal("The property 'Orders' cannot be used in the $expand query option.",errors.First()); } [Theory] @@ -1014,14 +1014,14 @@ public void TryValidateSelectExpandQueryValidator_ReturnsFalseWithError_IfBaseOr SelectExpandQueryOption selectExpandQueryOption = new SelectExpandQueryOption(null, expand, queryContext); // Act - var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(selectExpandQueryOption, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); Assert.Equal( string.Format(CultureInfo.InvariantCulture, "The property '{0}' cannot be used in the $expand query option.", propertyName), - errors.First().Message); + errors.First()); } [Fact] diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs index 3475267a1..cf5f72200 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipQueryValidatorTest.cs @@ -43,12 +43,12 @@ public void TryValidateSkipQueryValidator_ReturnsFalseWithError_OnNullOption() ODataValidationSettings settings = new ODataValidationSettings(); // Act - var result = _validator.TryValidate(null, settings, out IEnumerable errors); + var result = _validator.TryValidate(null, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'skipQueryOption')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'skipQueryOption')",errors.First()); } [Fact] @@ -65,12 +65,12 @@ public void TryValidateSkipQueryValidator_ReturnsFalseWithError_OnNullSettings() SkipQueryOption option = new SkipQueryOption("2", _context); // Act - var result = _validator.TryValidate(option, null, out IEnumerable errors); + var result = _validator.TryValidate(option, null, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')",errors.First()); } [Fact] @@ -98,12 +98,12 @@ public void TryValidateSkipQueryValidator_ReturnsFalseWithError_WhenLimitIsExcee SkipQueryOption option = new SkipQueryOption("11", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The limit of '10' for Skip query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + Assert.Equal("The limit of '10' for Skip query has been exceeded. The value from the incoming request is '11'.",errors.First()); } [Fact] @@ -130,7 +130,7 @@ public void TryValidateSkipQueryValidator_ReturnsTrueWithNoError_WhenLimitIsReac SkipQueryOption option = new SkipQueryOption("10", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.True(result); @@ -161,7 +161,7 @@ public void TryValidateSkipQueryValidator_ReturnsTrueWithNoError_WhenLimitIsNotR SkipQueryOption option = new SkipQueryOption("9", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.True(result); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs index 9bd3c14c3..eee2b4a4d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/SkipTokenQueryValidatorTests.cs @@ -35,12 +35,12 @@ public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_OnNullOptio SkipTokenQueryValidator validator = new SkipTokenQueryValidator(); // Act - var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); + var result = validator.TryValidate(null, new ODataValidationSettings(), out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'skipToken')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'skipToken')",errors.First()); } [Fact] @@ -65,12 +65,12 @@ public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_OnNullSetti SkipTokenQueryOption query = new SkipTokenQueryOption("abc", context); // Act - var result = validator.TryValidate(query, null, out IEnumerable errors); + var result = validator.TryValidate(query, null, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')",errors.First()); } [Fact] @@ -101,13 +101,13 @@ public void TryValidateSkipTokenQueryValidator_ReturnsFalseWithError_NotAllowedQ SkipTokenQueryValidator validator = new SkipTokenQueryValidator(); // Act - var result = validator.TryValidate(query, settings, out IEnumerable errors); + var result = validator.TryValidate(query, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); Assert.Equal( "Query option 'SkipToken' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.", - errors.First().Message); + errors.First()); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs index 9669510f3..a71872288 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/TopQueryValidatorTest.cs @@ -44,12 +44,12 @@ public void TryValidateTopQueryValidator_ReturnsFalseWithError_OnNullOption() ODataValidationSettings settings = new ODataValidationSettings(); // Act - var result = _validator.TryValidate(null, settings, out IEnumerable errors); + var result = _validator.TryValidate(null, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'topQueryOption')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'topQueryOption')",errors.First()); } [Fact] @@ -66,12 +66,12 @@ public void TryValidateTopQueryValidator_ReturnsFalseWithError_OnNullSettings() TopQueryOption option = new TopQueryOption("2", _context); // Act - var result = _validator.TryValidate(option, null, out IEnumerable errors); + var result = _validator.TryValidate(option, null, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("Value cannot be null. (Parameter 'validationSettings')", errors.First().Message); + Assert.Equal("Value cannot be null. (Parameter 'validationSettings')",errors.First()); } [Fact] @@ -99,12 +99,12 @@ public void TryValidateTopQueryValidator_ReturnsFalseWithError_WhenLimitIsExceed TopQueryOption option = new TopQueryOption("11", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.",errors.First()); } [Fact] @@ -131,7 +131,7 @@ public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenLimitIsReach TopQueryOption option = new TopQueryOption("10", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.True(result); @@ -162,7 +162,7 @@ public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenLimitIsNotRe TopQueryOption option = new TopQueryOption("9", _context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.True(result); @@ -203,7 +203,7 @@ public void TryValidateTopQueryValidator_ReturnsTrueWithNoError_WhenQuerySetting TopQueryOption option = new TopQueryOption("20", context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.True(result); @@ -245,12 +245,12 @@ public void TryValidateTopQueryValidator_ReturnsFalseWithError_WhenQuerySettings TopQueryOption option = new TopQueryOption("11", context); // Act - var result = _validator.TryValidate(option, settings, out IEnumerable errors); + var result = _validator.TryValidate(option, settings, out IEnumerable errors); // Assert Assert.False(result); Assert.Single(errors); - Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.", errors.First().Message); + Assert.Equal("The limit of '10' for Top query has been exceeded. The value from the incoming request is '11'.",errors.First()); } From 8f1079fedb93f191bd697946b75aa579cc48fab9 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 17:37:17 +0300 Subject: [PATCH 07/11] Ensure errors collection is allocated memory when needed --- .../PublicAPI.Unshipped.txt | 2 +- .../Query/Query/ComputeQueryOption.cs | 19 ++-- .../Query/Query/CountQueryOption.cs | 18 ++-- .../Query/Query/FilterQueryOption.cs | 18 ++-- .../Query/Query/OrderByQueryOption.cs | 18 ++-- .../Query/Query/SearchQueryOption.cs | 22 ++--- .../Query/Query/SelectExpandQueryOption.cs | 18 ++-- .../Query/Query/SkipQueryOption.cs | 18 ++-- .../Query/Query/SkipTokenQueryOption.cs | 19 ++-- .../Query/Query/TopQueryOption.cs | 18 ++-- .../Query/Validator/ComputeQueryValidator.cs | 38 ++++---- .../Query/Validator/CountQueryValidator.cs | 31 +++---- .../Query/Validator/FilterQueryValidator.cs | 33 +++---- .../Query/Validator/ODataQueryValidator.cs | 87 ++++++++++--------- .../Query/Validator/OrderByQueryValidator.cs | 31 ++++--- .../Validator/SelectExpandQueryValidator.cs | 34 ++++---- .../Query/Validator/SkipQueryValidator.cs | 33 ++++--- .../Validator/SkipTokenQueryValidator.cs | 32 +++---- .../Query/Validator/TopQueryValidator.cs | 42 ++++----- 19 files changed, 234 insertions(+), 297 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index bb1e5172f..6a4bf52b5 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -26,4 +26,4 @@ virtual Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.TryVali virtual Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool virtual Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool virtual Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool -virtual Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool \ No newline at end of file +virtual Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index 93d39841d..8525ba0f9 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -148,27 +148,18 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index 821bd06bd..55866689b 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -142,27 +142,19 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs index 1b5271dbd..bda215133 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs @@ -198,26 +198,18 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs index 8f789b423..fbe2a82f1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs @@ -254,27 +254,19 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } private IOrderedQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs index 98d288c05..9aa0ec339 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs @@ -182,29 +182,21 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - ISearchQueryValidator validator = Context.GetSearchQueryValidator(); - if (validator != null) + + // If the developer doesn't provide the search validator, let's ignore the $search validation. + if (validator != null && !validator.TryValidate(this, validationSettings, out validationErrors)) { - // If the developer doesn't provide the search validator, let's ignore the $search validation. - validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs index 28a6364dd..d92fb6649 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs @@ -298,27 +298,19 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } internal SelectExpandClause ProcessLevels() diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs index f6365edb2..3a7cb5f29 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs @@ -168,27 +168,19 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } private IQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs index af18cbae2..e5f36edd1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.OData.Query.Validator; @@ -111,26 +112,18 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs index 51c6bbb0c..76d8ebd7a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs @@ -168,27 +168,19 @@ public void Validate(ODataValidationSettings validationSettings) /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - if (validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } - validationErrors = errors; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - return !validationErrors.Any(); + validationErrors = Array.Empty(); + return true; } private IQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index 6ce122b3e..8b8528c99 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using Microsoft.OData; @@ -49,39 +50,38 @@ public virtual void Validate(ComputeQueryOption computeQueryOption, ODataValidat /// if the validation succeeded; otherwise, . public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (computeQueryOption == null) + if (computeQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(computeQueryOption)).Message); - } + // Use a single allocation for the error list only when needed + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (computeQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(computeQueryOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count > 0) - { validationErrors = errors; return false; } try { - // so far, we don't have validation rules here for $compute - // because 'DefaultQuerySetting' doesn't have configuration for $compute - // we can only let ODL to parse and verify the compute clause, - // however, developer can override this method add his own rules + // Let ODL parse and verify the compute clause _ = computeQueryOption.ComputeClause; } catch (ODataException ex) { - errors.Add(ex.Message); + validationErrors = new List { ex.Message }; + return false; } - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs index 8d4fb32ce..1f392f927 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs @@ -69,21 +69,21 @@ public virtual void Validate(CountQueryOption countQueryOption, ODataValidationS /// if the validation succeeded; otherwise, . public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (countQueryOption == null) + if(countQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(countQueryOption)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (countQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(countQueryOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } @@ -103,11 +103,12 @@ public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidati ? Error.Format(SRResources.NotCountableEntitySetUsedForCount, name) : Error.Format(SRResources.NotCountablePropertyUsedForCount, name); - errors.Add(errorMessage); + validationErrors = new[] { errorMessage }; + return false; } } - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs index 1acecdc3f..07f0bdd0d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs @@ -60,22 +60,22 @@ public virtual void Validate(FilterQueryOption filterQueryOption, ODataValidatio /// if the validation succeeded; otherwise, . public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - // Validate input parameters - if (filterQueryOption == null) + if(filterQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(filterQueryOption)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + // Validate input parameters + if (filterQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(filterQueryOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } @@ -98,12 +98,13 @@ public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValida } catch (Exception ex) { - errors.Add(ex.Message); + validationErrors = new[] { ex.Message }; + return false; } // Set the output parameter - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index b4cce90d8..39e5bc74f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -134,29 +134,30 @@ public virtual void Validate(ODataQueryOptions options, ODataValidationSettings /// True if validation is successful; otherwise, false. public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = null; - // Validate input parameters - if (options == null) + if (options == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(options)).Message); - } + // Preallocate with a reasonable default capacity. + errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + // Validate input parameters + if (options == null) + { + errors.Add(Error.ArgumentNull(nameof(options)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count > 0) - { validationErrors = errors; return false; } // To prevent duplicates in the `errors` list, ensure that each error is unique before adding it. // Modify the `AddValidationErrors` helper function to check for duplicates. - void AddValidationErrors(IEnumerable queryOptionErrors) { if (queryOptionErrors == null) @@ -164,19 +165,23 @@ void AddValidationErrors(IEnumerable queryOptionErrors) return; } + // Preallocate with a reasonable default capacity. + errors ??= new List(4); + + // If errors list is not empty, we need to ensure uniqueness. var uniqueErrors = queryOptionErrors .Where(error => !errors.Any(e => e == error)); errors.AddRange(uniqueErrors); } - // Validate each query option + // Validate each query option and add errors if any. if (options.Compute != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out IEnumerable computeErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out var computeErrors); AddValidationErrors(computeErrors); - if (!options.Compute.TryValidate(validationSettings, out IEnumerable computeValidationErrors)) + if (!options.Compute.TryValidate(validationSettings, out var computeValidationErrors)) { AddValidationErrors(computeValidationErrors); } @@ -184,16 +189,16 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Apply?.ApplyClause != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out IEnumerable applyErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out var applyErrors); AddValidationErrors(applyErrors); } if (options.Skip != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out IEnumerable skipErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out var skipErrors); AddValidationErrors(skipErrors); - if (!options.Skip.TryValidate(validationSettings, out IEnumerable skipValidationErrors)) + if (!options.Skip.TryValidate(validationSettings, out var skipValidationErrors)) { AddValidationErrors(skipValidationErrors); } @@ -201,10 +206,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Top != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out IEnumerable topErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out var topErrors); AddValidationErrors(topErrors); - if (!options.Top.TryValidate(validationSettings, out IEnumerable topValidationErrors)) + if (!options.Top.TryValidate(validationSettings, out var topValidationErrors)) { AddValidationErrors(topValidationErrors); } @@ -212,10 +217,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.OrderBy != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out IEnumerable orderByErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out var orderByErrors); AddValidationErrors(orderByErrors); - if (!options.OrderBy.TryValidate(validationSettings, out IEnumerable orderByValidationErrors)) + if (!options.OrderBy.TryValidate(validationSettings, out var orderByValidationErrors)) { AddValidationErrors(orderByValidationErrors); } @@ -223,10 +228,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Filter != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out IEnumerable filterErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out var filterErrors); AddValidationErrors(filterErrors); - if (!options.Filter.TryValidate(validationSettings, out IEnumerable filterValidationErrors)) + if (!options.Filter.TryValidate(validationSettings, out var filterValidationErrors)) { AddValidationErrors(filterValidationErrors); } @@ -234,10 +239,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Search != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out IEnumerable searchErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out var searchErrors); AddValidationErrors(searchErrors); - if (!options.Search.TryValidate(validationSettings, out IEnumerable searchValidationErrors)) + if (!options.Search.TryValidate(validationSettings, out var searchValidationErrors)) { AddValidationErrors(searchValidationErrors); } @@ -245,10 +250,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Count != null || options.Request.IsCountRequest()) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out IEnumerable countErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out var countErrors); AddValidationErrors(countErrors); - if (options.Count?.TryValidate(validationSettings, out IEnumerable countValidationErrors) == false) + if (options.Count?.TryValidate(validationSettings, out var countValidationErrors) == false) { AddValidationErrors(countValidationErrors); } @@ -256,10 +261,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.SkipToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out IEnumerable skipTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out var skipTokenErrors); AddValidationErrors(skipTokenErrors); - if (!options.SkipToken.TryValidate(validationSettings, out IEnumerable skipTokenValidationErrors)) + if (!options.SkipToken.TryValidate(validationSettings, out var skipTokenValidationErrors)) { AddValidationErrors(skipTokenValidationErrors); } @@ -267,21 +272,21 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Expand != null) { - TryValidateNotEmptyOrWhitespace(options.RawValues.Expand, errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out IEnumerable expandErrors); + TryValidateNotEmptyOrWhitespace(options.RawValues.Expand, ref errors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out var expandErrors); AddValidationErrors(expandErrors); } if (options.RawValues.Select != null) { - TryValidateNotEmptyOrWhitespace(options.RawValues.Select, errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out IEnumerable selectErrors); + TryValidateNotEmptyOrWhitespace(options.RawValues.Select, ref errors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out var selectErrors); AddValidationErrors(selectErrors); } if (options.SelectExpand != null) { - if (!options.SelectExpand.TryValidate(validationSettings, out IEnumerable selectExpandValidationErrors)) + if (!options.SelectExpand.TryValidate(validationSettings, out var selectExpandValidationErrors)) { AddValidationErrors(selectExpandValidationErrors); } @@ -289,18 +294,18 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Format != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out IEnumerable formatErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out var formatErrors); AddValidationErrors(formatErrors); } if (options.RawValues.DeltaToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out IEnumerable deltaTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out var deltaTokenErrors); AddValidationErrors(deltaTokenErrors); } - validationErrors = errors; - return errors.Count == 0; + validationErrors = errors ?? Enumerable.Empty(); // Avoid allocating a new empty list. + return errors == null || errors.Count == 0; } @@ -331,7 +336,7 @@ private static void ValidateNotEmptyOrWhitespace(string rawValue) } } - private static void TryValidateNotEmptyOrWhitespace(string rawValue, List validationErrors) + private static void TryValidateNotEmptyOrWhitespace(string rawValue, ref List validationErrors) { if (rawValue != null && string.IsNullOrWhiteSpace(rawValue)) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs index eb72f7924..94e9978b1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.OData.Edm; using Microsoft.OData; using Microsoft.OData.Edm; @@ -66,21 +67,23 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe /// if the validation succeeded; otherwise, . public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); + List errors = null; - if (orderByOption == null) + if (orderByOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(orderByOption)).Message); - } + // Preallocate with a reasonable default capacity. + errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (orderByOption == null) + { + errors.Add(Error.ArgumentNull(nameof(orderByOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } @@ -106,6 +109,8 @@ public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidatio } catch(Exception ex) { + // Preallocate with a reasonable default capacity. + errors ??= new List(4); errors.Add(ex.Message); } @@ -113,8 +118,8 @@ public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidatio } // If there are any errors, return false - validationErrors = errors; - return errors.Count == 0; + validationErrors = errors ?? Enumerable.Empty(); + return errors == null; } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs index 58d4d924a..4b75f8168 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs @@ -80,21 +80,21 @@ public virtual void Validate(SelectExpandQueryOption selectExpandQueryOption, OD /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (selectExpandQueryOption == null) + if (selectExpandQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(selectExpandQueryOption)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (selectExpandQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(selectExpandQueryOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } @@ -121,8 +121,7 @@ public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, } else if (selectExpandQueryOption.LevelsMaxLiteralExpansionDepth > validationSettings.MaxExpansionDepth) { - errors.Add(Error.Format(SRResources.InvalidExpansionDepthValue, "LevelsMaxLiteralExpansionDepth", "MaxExpansionDepth")); - validationErrors = errors; + validationErrors = new[] { Error.Format(SRResources.InvalidExpansionDepthValue, "LevelsMaxLiteralExpansionDepth", "MaxExpansionDepth") }; return false; } @@ -131,12 +130,13 @@ public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, } catch (Exception ex) { - errors.Add(ex.Message); + validationErrors = new[] { ex.Message }; + return false; } // If there are any errors, return false - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index 32be9d6ce..4de09dfb1 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using Microsoft.OData; @@ -47,32 +48,30 @@ public virtual void Validate(SkipQueryOption skipQueryOption, ODataValidationSet /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (skipQueryOption == null) + if(skipQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(skipQueryOption)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (skipQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(skipQueryOption)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; - return false; + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } } if (skipQueryOption.Value > validationSettings.MaxSkip) { - errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value)); + validationErrors = new[] { Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxSkip, AllowedQueryOptions.Skip, skipQueryOption.Value) }; + return false; } // If there are any errors, return false - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs index bc4cf35ac..44fbf00da 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using Microsoft.OData; @@ -51,21 +52,21 @@ public virtual void Validate(SkipTokenQueryOption skipToken, ODataValidationSett /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (skipToken == null) + if(skipToken == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(skipToken)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (skipToken == null) + { + errors.Add(Error.ArgumentNull(nameof(skipToken)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } @@ -75,12 +76,13 @@ public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationS DefaultQueryConfigurations defaultConfigs = skipToken.Context.DefaultQueryConfigurations; if (!defaultConfigs.EnableSkipToken) { - errors.Add(Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, nameof(AllowedQueryOptions))); + validationErrors = new[] { Error.Format(SRResources.NotAllowedQueryOption, AllowedQueryOptions.SkipToken, nameof(AllowedQueryOptions)) }; + return false; } } // If there are any errors, return false - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs index b23af8df1..50ace5146 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using Microsoft.AspNetCore.OData.Edm; using Microsoft.OData; @@ -65,34 +66,28 @@ public virtual void Validate(TopQueryOption topQueryOption, ODataValidationSetti /// if the validation succeeded; otherwise, . public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (topQueryOption == null) + if (topQueryOption == null || validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(topQueryOption)).Message); - } + // Preallocate with a reasonable default capacity. + List errors = new List(2); - if (validationSettings == null) - { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } + if (topQueryOption == null) + { + errors.Add(Error.ArgumentNull(nameof(topQueryOption)).Message); + } + + if (validationSettings == null) + { + errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); + } - // If there are parameter errors, return early - if (errors.Count != 0) - { validationErrors = errors; return false; } - if (topQueryOption != null && validationSettings != null && topQueryOption.Value > validationSettings.MaxTop) - { - errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value)); - } - - // If there are parameter errors, return early - if (errors.Count != 0) + if (topQueryOption.Value > validationSettings.MaxTop) { - validationErrors = errors; + validationErrors = new[] { Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value) }; return false; } @@ -107,11 +102,12 @@ public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSe topQueryOption.Value, topQueryOption.Context.DefaultQueryConfigurations, out maxTop)) { - errors.Add(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value)); + validationErrors = new[] { Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value) }; + return false; } // If there are any errors, return false - validationErrors = errors; - return errors.Count == 0; + validationErrors = Array.Empty(); + return true; } } From 75db22e2f7943767669a3b8f60c85b681be62a98 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 17:56:23 +0300 Subject: [PATCH 08/11] Refactoring --- .../Query/ODataQueryOptions.cs | 18 +++++------------- .../Query/Query/CountQueryOption.cs | 2 +- .../Query/Validator/ComputeQueryValidator.cs | 2 +- .../Query/Validator/OrderByQueryValidator.cs | 4 ++-- .../Query/Validator/SkipQueryValidator.cs | 3 +++ .../Validator/FilterQueryValidatorTests.cs | 1 + 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs index 83a94c5f2..fd5c99a0a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs @@ -665,28 +665,20 @@ public virtual void Validate(ODataValidationSettings validationSettings) /// langword="false"/> if validation failed. public virtual bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { - List errors = new List(); - - if (validationSettings == null) + if(validationSettings == null) { - errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); - } - - // If there are parameter errors, return early - if (errors.Count != 0) - { - validationErrors = errors; + validationErrors = new[] { Error.ArgumentNull(nameof(validationSettings)).Message }; return false; } this.Context.ValidationSettings = validationSettings; - if (Validator != null) + if (Validator != null && !Validator.TryValidate(this, validationSettings, out validationErrors)) { - return Validator.TryValidate(this, validationSettings, out validationErrors); + return false; } - validationErrors = null; + validationErrors = Array.Empty(); return true; } diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index 55866689b..4afe2611c 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -123,7 +123,7 @@ public void Validate(ODataValidationSettings validationSettings) { if (validationSettings == null) { - throw Error.ArgumentNull(nameof(validationSettings)); + throw Error.ArgumentNull("validationSettings"); } if (Validator != null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index 8b8528c99..b6ed108d5 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -77,7 +77,7 @@ public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataVali } catch (ODataException ex) { - validationErrors = new List { ex.Message }; + validationErrors = new[] { ex.Message }; return false; } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs index 94e9978b1..5a0fd3b81 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs @@ -29,12 +29,12 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe { if (orderByOption == null) { - throw Error.ArgumentNull(nameof(orderByOption)); + throw Error.ArgumentNull("orderByOption"); } if (validationSettings == null) { - throw Error.ArgumentNull(nameof(validationSettings)); + throw Error.ArgumentNull("validationSettings"); } OrderByValidatorContext validatorContext = new OrderByValidatorContext diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index 4de09dfb1..ef8ae66e0 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -62,6 +62,9 @@ public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidation { errors.Add(Error.ArgumentNull(nameof(validationSettings)).Message); } + + validationErrors = errors; + return false; } if (skipQueryOption.Value > validationSettings.MaxSkip) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs index b0f308e5d..d8eade9c8 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.OData.Tests.Query.Validator; +[CollectionDefinition("FilterQueryValidatorTests", DisableParallelization = false)] public class FilterQueryValidatorTests { private MyFilterValidator _validator; From 7c4a239f5e0d2bd98738be57c87ce432f7a88388 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 18:26:33 +0300 Subject: [PATCH 09/11] nit --- .../Microsoft.AspNetCore.OData.xml | 71 ++++++++----------- .../PublicAPI.Unshipped.txt | 29 +++++++- .../Query/ODataQueryOptions.cs | 4 +- .../Query/Query/ComputeQueryOption.cs | 3 +- .../Query/Query/CountQueryOption.cs | 3 +- .../Query/Query/FilterQueryOption.cs | 3 +- .../Query/Query/OrderByQueryOption.cs | 3 +- .../Query/Query/SearchQueryOption.cs | 15 ++-- .../Query/Query/SelectExpandQueryOption.cs | 15 ++-- .../Query/Query/SkipQueryOption.cs | 15 ++-- .../Query/Query/SkipTokenQueryOption.cs | 15 ++-- .../Query/Query/TopQueryOption.cs | 15 ++-- .../Query/Validator/ComputeQueryValidator.cs | 19 +++-- .../Query/Validator/CountQueryValidator.cs | 19 +++-- .../Query/Validator/FilterQueryValidator.cs | 4 +- .../Interfaces/IComputeQueryValidator.cs | 2 +- .../Interfaces/ICountQueryValidator.cs | 2 +- .../Interfaces/IFilterQueryValidator.cs | 2 +- .../Interfaces/IODataQueryValidator.cs | 2 +- .../Interfaces/IOrderByQueryValidator.cs | 2 +- .../Interfaces/ISearchQueryValidator.cs | 2 +- .../Interfaces/ISelectExpandQueryValidator.cs | 2 +- .../Interfaces/ISkipQueryValidator.cs | 2 +- .../Interfaces/ISkipTokenQueryValidator.cs | 2 +- .../Interfaces/ITopQueryValidator.cs | 2 +- .../Query/Validator/ODataQueryValidator.cs | 6 +- .../Query/Validator/OrderByQueryValidator.cs | 6 +- .../Validator/SelectExpandQueryValidator.cs | 4 +- .../Query/Validator/SkipQueryValidator.cs | 4 +- .../Validator/SkipTokenQueryValidator.cs | 4 +- .../Query/Validator/TopQueryValidator.cs | 4 +- 31 files changed, 141 insertions(+), 140 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 97bce4dea..1591a12a0 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -11790,9 +11790,7 @@ Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing validation - errors, if any occurred; otherwise, if validation was successful or no validator is - configured. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation was successful or no validator is configured; otherwise, if validation failed. @@ -12409,8 +12407,7 @@ Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12460,8 +12457,7 @@ It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12602,8 +12598,7 @@ Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12761,8 +12756,7 @@ Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12823,8 +12817,7 @@ Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12915,8 +12908,7 @@ Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -12980,8 +12972,7 @@ Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13081,8 +13072,7 @@ Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13146,8 +13136,7 @@ Attempts to validate the top query based on the given . It throws an ODataException if validation failed. The instance which contains all the validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13245,8 +13234,7 @@ The $compute query. The validation settings. - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13268,8 +13256,7 @@ - When this method returns, contains a collection of instances describing any - validation errors encountered, or an empty collection if validation succeeds. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13290,7 +13277,7 @@ The $filter query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13596,7 +13583,7 @@ The $compute query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13618,7 +13605,7 @@ - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13640,7 +13627,7 @@ The $filter query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13662,7 +13649,7 @@ The OData query options to validate. The validation settings. - The collection of validation errors. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. True if the validation succeeded; otherwise, false. @@ -13684,7 +13671,7 @@ The $orderby query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13706,7 +13693,7 @@ - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13728,7 +13715,7 @@ The $select and $expand query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13750,7 +13737,7 @@ The $skip query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13772,7 +13759,7 @@ The $skiptoken query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13794,7 +13781,7 @@ The $top query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -13815,7 +13802,7 @@ The OData query to validate. The settings used for validation. - The collection of validation errors. + When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. True if validation is successful; otherwise, false. @@ -13928,7 +13915,7 @@ The $orderby query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -14249,7 +14236,7 @@ The $select and $expand query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -14409,7 +14396,7 @@ The $skip query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -14430,7 +14417,7 @@ The $skiptoken query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . @@ -14451,7 +14438,7 @@ The $top query. The validation settings. - Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + Contains a collection of validation errors encountered, or an empty collection if validation succeeds. if the validation succeeded; otherwise, . diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 6b14c10a4..8f73d8c5b 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -42,6 +42,9 @@ Microsoft.AspNetCore.OData.ODataMiniOptions.SetNoDollarQueryOptions(bool enableN Microsoft.AspNetCore.OData.ODataMiniOptions.SetVersion(Microsoft.OData.ODataVersion version) -> Microsoft.AspNetCore.OData.ODataMiniOptions Microsoft.AspNetCore.OData.ODataMiniOptions.SkipToken() -> Microsoft.AspNetCore.OData.ODataMiniOptions Microsoft.AspNetCore.OData.ODataMiniOptions.Version.get -> Microsoft.OData.ODataVersion +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.CountQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.FilterQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool Microsoft.AspNetCore.OData.Query.IODataQueryEndpointFilter Microsoft.AspNetCore.OData.Query.IODataQueryEndpointFilter.OnFilterExecutedAsync(object responseValue, Microsoft.AspNetCore.OData.Extensions.ODataQueryFilterInvocationContext context) -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.OData.Query.IODataQueryEndpointFilter.OnFilterExecutingAsync(Microsoft.AspNetCore.OData.Extensions.ODataQueryFilterInvocationContext context) -> System.Threading.Tasks.ValueTask @@ -49,6 +52,20 @@ Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ODataQueryEndpointFilter() -> void Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.QuerySettings.get -> Microsoft.AspNetCore.OData.Query.ODataQuerySettings Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ValidationSettings.get -> Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings +Microsoft.AspNetCore.OData.Query.SearchQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.TopQueryOption.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ICountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.IOrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISearchQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SearchQueryOption searchQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +Microsoft.AspNetCore.OData.Query.Validator.ISkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool Microsoft.AspNetCore.OData.Query.Wrapper.SelectExpandWrapperConverter Microsoft.AspNetCore.OData.Query.Wrapper.SelectExpandWrapperConverter.SelectExpandWrapperConverter() -> void Microsoft.AspNetCore.OData.Results.IODataResult @@ -85,4 +102,14 @@ virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.GetModel(Syste virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.InvokeAsync(Microsoft.AspNetCore.Http.EndpointFilterInvocationContext invocationContext, Microsoft.AspNetCore.Http.EndpointFilterDelegate next) -> System.Threading.Tasks.ValueTask virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.OnFilterExecutedAsync(object responseValue, Microsoft.AspNetCore.OData.Extensions.ODataQueryFilterInvocationContext context) -> System.Threading.Tasks.ValueTask virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.OnFilterExecutingAsync(Microsoft.AspNetCore.OData.Extensions.ODataQueryFilterInvocationContext context) -> System.Threading.Tasks.ValueTask -virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ValidateQuery(Microsoft.AspNetCore.Http.HttpContext httpContext, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) -> void \ No newline at end of file +virtual Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ValidateQuery(Microsoft.AspNetCore.Http.HttpContext httpContext, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) -> void +virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.TryValidate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.ODataQueryOptions options, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.OrderByQueryOption orderByOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SelectExpandQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SelectExpandQueryOption selectExpandQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipQueryOption skipQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.SkipTokenQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption skipToken, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool +virtual Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator.TryValidate(Microsoft.AspNetCore.OData.Query.TopQueryOption topQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings, out System.Collections.Generic.IEnumerable validationErrors) -> bool \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs index fd5c99a0a..5f3873fa9 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs @@ -658,9 +658,7 @@ public virtual void Validate(ODataValidationSettings validationSettings) /// Attempts to validate all OData queries, including $skip, $top, $orderby and $filter, based on the given . /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing validation - /// errors, if any occurred; otherwise, if validation was successful or no validator is - /// configured. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation was successful or no validator is configured; otherwise, if validation failed. public virtual bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index 8525ba0f9..d0454cedd 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -143,8 +143,7 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the $compute query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index 4afe2611c..da65312a4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -137,8 +137,7 @@ public void Validate(ODataValidationSettings validationSettings) /// It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs index bda215133..a3e09f65a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs @@ -193,8 +193,7 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the filter query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs index fbe2a82f1..f51a1e401 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs @@ -249,8 +249,7 @@ public void Validate(ODataValidationSettings validationSettings) /// Attempts to validate the orderby query based on the given . It throws an ODataException if validation failed. /// /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs index 9aa0ec339..b0c65b1f6 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs @@ -173,14 +173,13 @@ public void Validate(ODataValidationSettings validationSettings) } } - /// - /// Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. - /// - /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the $search query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (validationSettings == null) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs index 9542370bc..de00a88f9 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs @@ -291,14 +291,13 @@ public void Validate(ODataValidationSettings validationSettings) } } - /// - /// Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. - /// - /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the $select and $expand query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (validationSettings == null) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs index 3a7cb5f29..0a8cb47f6 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs @@ -159,14 +159,13 @@ public void Validate(ODataValidationSettings validationSettings) } } - /// - /// Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. - /// - /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate validate the skip query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (validationSettings == null) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs index e5f36edd1..315b5094a 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs @@ -103,14 +103,13 @@ public void Validate(ODataValidationSettings validationSettings) } } - /// - /// Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. - /// - /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the skiptoken query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (validationSettings == null) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs index 76d8ebd7a..26439199f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/TopQueryOption.cs @@ -159,14 +159,13 @@ public void Validate(ODataValidationSettings validationSettings) } } - /// - /// Attempts to validate the top query based on the given . It throws an ODataException if validation failed. - /// - /// The instance which contains all the validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the top query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public bool TryValidate(ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (validationSettings == null) { diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs index b6ed108d5..657ebdfed 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -40,20 +40,19 @@ public virtual void Validate(ComputeQueryOption computeQueryOption, ODataValidat _ = computeQueryOption.ComputeClause; } - /// - /// Attempts to validate the . - /// - /// The $compute query. - /// The validation settings. - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the . + /// + /// The $compute query. + /// The validation settings. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (computeQueryOption == null || validationSettings == null) { // Use a single allocation for the error list only when needed - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (computeQueryOption == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs index 1f392f927..cce2fddb4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs @@ -59,19 +59,18 @@ public virtual void Validate(CountQueryOption countQueryOption, ODataValidationS } } - /// - /// Attempts to validate the . - /// - /// - /// - /// When this method returns, contains a collection of instances describing any - /// validation errors encountered, or an empty collection if validation succeeds. - /// if the validation succeeded; otherwise, . - public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) + /// + /// Attempts to validate the . + /// + /// + /// + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. + /// if the validation succeeded; otherwise, . + public virtual bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if(countQueryOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (countQueryOption == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs index 07f0bdd0d..17cb3de84 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/FilterQueryValidator.cs @@ -56,13 +56,13 @@ public virtual void Validate(FilterQueryOption filterQueryOption, ODataValidatio /// /// The $filter query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if(filterQueryOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); // Validate input parameters diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs index c0ba23903..84e218362 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IComputeQueryValidator.cs @@ -27,7 +27,7 @@ public interface IComputeQueryValidator /// /// The $compute query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs index 54d325f2b..8a9dd4c05 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ICountQueryValidator.cs @@ -27,7 +27,7 @@ public interface ICountQueryValidator /// /// /// - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs index cce45d915..25d663a67 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IFilterQueryValidator.cs @@ -27,7 +27,7 @@ public interface IFilterQueryValidator /// /// The $filter query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(FilterQueryOption filterQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs index 5b15f1439..558fac00e 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IODataQueryValidator.cs @@ -27,7 +27,7 @@ public interface IODataQueryValidator /// /// The OData query options to validate. /// The validation settings. - /// The collection of validation errors. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// True if the validation succeeded; otherwise, false. bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs index a3afb8860..ba3760957 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/IOrderByQueryValidator.cs @@ -27,7 +27,7 @@ public interface IOrderByQueryValidator /// /// The $orderby query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs index 03407ebca..8a6a77faf 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISearchQueryValidator.cs @@ -27,7 +27,7 @@ public interface ISearchQueryValidator /// /// /// - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(SearchQueryOption searchQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs index de47ee961..402b5f2f4 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISelectExpandQueryValidator.cs @@ -27,7 +27,7 @@ public interface ISelectExpandQueryValidator /// /// The $select and $expand query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs index 3acff2c4f..791766a04 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipQueryValidator.cs @@ -27,7 +27,7 @@ public interface ISkipQueryValidator /// /// The $skip query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs index c5777b3b4..13c5d3f8f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ISkipTokenQueryValidator.cs @@ -27,7 +27,7 @@ public interface ISkipTokenQueryValidator /// /// The $skiptoken query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs index e984abc32..9f93bd614 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/Interfaces/ITopQueryValidator.cs @@ -27,7 +27,7 @@ public interface ITopQueryValidator /// /// The $top query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 39e5bc74f..542a93ca2 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -130,7 +130,7 @@ public virtual void Validate(ODataQueryOptions options, ODataValidationSettings /// /// The OData query to validate. /// The settings used for validation. - /// The collection of validation errors. + /// When this method returns, contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// True if validation is successful; otherwise, false. public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { @@ -138,7 +138,7 @@ public virtual bool TryValidate(ODataQueryOptions options, ODataValidationSettin if (options == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. errors = new List(2); // Validate input parameters @@ -165,7 +165,7 @@ void AddValidationErrors(IEnumerable queryOptionErrors) return; } - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. errors ??= new List(4); // If errors list is not empty, we need to ensure uniqueness. diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs index 5a0fd3b81..8dacb953f 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/OrderByQueryValidator.cs @@ -63,7 +63,7 @@ public virtual void Validate(OrderByQueryOption orderByOption, ODataValidationSe /// /// The $orderby query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { @@ -71,7 +71,7 @@ public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidatio if (orderByOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. errors = new List(2); if (orderByOption == null) @@ -109,7 +109,7 @@ public virtual bool TryValidate(OrderByQueryOption orderByOption, ODataValidatio } catch(Exception ex) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. errors ??= new List(4); errors.Add(ex.Message); } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs index 4b75f8168..108b20732 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SelectExpandQueryValidator.cs @@ -76,13 +76,13 @@ public virtual void Validate(SelectExpandQueryOption selectExpandQueryOption, OD /// /// The $select and $expand query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (selectExpandQueryOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (selectExpandQueryOption == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs index ef8ae66e0..0d609bbcb 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipQueryValidator.cs @@ -44,13 +44,13 @@ public virtual void Validate(SkipQueryOption skipQueryOption, ODataValidationSet /// /// The $skip query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SkipQueryOption skipQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if(skipQueryOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (skipQueryOption == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs index 44fbf00da..02920c567 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/SkipTokenQueryValidator.cs @@ -48,13 +48,13 @@ public virtual void Validate(SkipTokenQueryOption skipToken, ODataValidationSett /// /// The $skiptoken query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(SkipTokenQueryOption skipToken, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if(skipToken == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (skipToken == null) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs index 50ace5146..f277cd42d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/TopQueryValidator.cs @@ -62,13 +62,13 @@ public virtual void Validate(TopQueryOption topQueryOption, ODataValidationSetti /// /// The $top query. /// The validation settings. - /// Contains a collection of describing any validation errors encountered, or an empty collection if validation succeeds. + /// Contains a collection of validation errors encountered, or an empty collection if validation succeeds. /// if the validation succeeded; otherwise, . public virtual bool TryValidate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings, out IEnumerable validationErrors) { if (topQueryOption == null || validationSettings == null) { - // Preallocate with a reasonable default capacity. + // Pre-allocate with a reasonable default capacity. List errors = new List(2); if (topQueryOption == null) From cc305d6527eb16c9c4159f5f9d15e62451d0a8af Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 18:46:22 +0300 Subject: [PATCH 10/11] resolve null exception --- .../Query/Validator/ODataQueryValidator.cs | 54 ++++++++++--------- .../Validator/FilterQueryValidatorTests.cs | 2 +- .../Validator/ODataQueryValidatorTest.cs | 1 - 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 542a93ca2..93bda8327 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.OData.Extensions; @@ -178,10 +179,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) // Validate each query option and add errors if any. if (options.Compute != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out var computeErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions, out IEnumerable computeErrors); AddValidationErrors(computeErrors); - if (!options.Compute.TryValidate(validationSettings, out var computeValidationErrors)) + if (!options.Compute.TryValidate(validationSettings, out IEnumerable computeValidationErrors)) { AddValidationErrors(computeValidationErrors); } @@ -189,16 +190,16 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Apply?.ApplyClause != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out var applyErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions, out IEnumerable applyErrors); AddValidationErrors(applyErrors); } if (options.Skip != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out var skipErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Skip, validationSettings.AllowedQueryOptions, out IEnumerable skipErrors); AddValidationErrors(skipErrors); - if (!options.Skip.TryValidate(validationSettings, out var skipValidationErrors)) + if (!options.Skip.TryValidate(validationSettings, out IEnumerable skipValidationErrors)) { AddValidationErrors(skipValidationErrors); } @@ -206,10 +207,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Top != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out var topErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Top, validationSettings.AllowedQueryOptions, out IEnumerable topErrors); AddValidationErrors(topErrors); - if (!options.Top.TryValidate(validationSettings, out var topValidationErrors)) + if (!options.Top.TryValidate(validationSettings, out IEnumerable topValidationErrors)) { AddValidationErrors(topValidationErrors); } @@ -217,10 +218,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.OrderBy != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out var orderByErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.OrderBy, validationSettings.AllowedQueryOptions, out IEnumerable orderByErrors); AddValidationErrors(orderByErrors); - if (!options.OrderBy.TryValidate(validationSettings, out var orderByValidationErrors)) + if (!options.OrderBy.TryValidate(validationSettings, out IEnumerable orderByValidationErrors)) { AddValidationErrors(orderByValidationErrors); } @@ -228,10 +229,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Filter != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out var filterErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Filter, validationSettings.AllowedQueryOptions, out IEnumerable filterErrors); AddValidationErrors(filterErrors); - if (!options.Filter.TryValidate(validationSettings, out var filterValidationErrors)) + if (!options.Filter.TryValidate(validationSettings, out IEnumerable filterValidationErrors)) { AddValidationErrors(filterValidationErrors); } @@ -239,10 +240,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Search != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out var searchErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Search, validationSettings.AllowedQueryOptions, out IEnumerable searchErrors); AddValidationErrors(searchErrors); - if (!options.Search.TryValidate(validationSettings, out var searchValidationErrors)) + if (!options.Search.TryValidate(validationSettings, out IEnumerable searchValidationErrors)) { AddValidationErrors(searchValidationErrors); } @@ -250,10 +251,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.Count != null || options.Request.IsCountRequest()) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out var countErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Count, validationSettings.AllowedQueryOptions, out IEnumerable countErrors); AddValidationErrors(countErrors); - if (options.Count?.TryValidate(validationSettings, out var countValidationErrors) == false) + if (options.Count?.TryValidate(validationSettings, out IEnumerable countValidationErrors) == false) { AddValidationErrors(countValidationErrors); } @@ -261,10 +262,10 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.SkipToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out var skipTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.SkipToken, validationSettings.AllowedQueryOptions, out IEnumerable skipTokenErrors); AddValidationErrors(skipTokenErrors); - if (!options.SkipToken.TryValidate(validationSettings, out var skipTokenValidationErrors)) + if (!options.SkipToken.TryValidate(validationSettings, out IEnumerable skipTokenValidationErrors)) { AddValidationErrors(skipTokenValidationErrors); } @@ -273,20 +274,20 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Expand != null) { TryValidateNotEmptyOrWhitespace(options.RawValues.Expand, ref errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out var expandErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Expand, validationSettings.AllowedQueryOptions, out IEnumerable expandErrors); AddValidationErrors(expandErrors); } if (options.RawValues.Select != null) { TryValidateNotEmptyOrWhitespace(options.RawValues.Select, ref errors); - TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out var selectErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Select, validationSettings.AllowedQueryOptions, out IEnumerable selectErrors); AddValidationErrors(selectErrors); } if (options.SelectExpand != null) { - if (!options.SelectExpand.TryValidate(validationSettings, out var selectExpandValidationErrors)) + if (!options.SelectExpand.TryValidate(validationSettings, out IEnumerable selectExpandValidationErrors)) { AddValidationErrors(selectExpandValidationErrors); } @@ -294,13 +295,13 @@ void AddValidationErrors(IEnumerable queryOptionErrors) if (options.RawValues.Format != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out var formatErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.Format, validationSettings.AllowedQueryOptions, out IEnumerable formatErrors); AddValidationErrors(formatErrors); } if (options.RawValues.DeltaToken != null) { - TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out var deltaTokenErrors); + TryValidateQueryOptionAllowed(AllowedQueryOptions.DeltaToken, validationSettings.AllowedQueryOptions, out IEnumerable deltaTokenErrors); AddValidationErrors(deltaTokenErrors); } @@ -319,13 +320,11 @@ private static void ValidateQueryOptionAllowed(AllowedQueryOptions queryOption, private static void TryValidateQueryOptionAllowed(AllowedQueryOptions queryOption, AllowedQueryOptions allowed, out IEnumerable validationErrors) { - List errors = new List(); + validationErrors = Array.Empty(); if ((queryOption & allowed) == AllowedQueryOptions.None) { - errors.Add(Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions))); + validationErrors = new[] { Error.Format(SRResources.NotAllowedQueryOption, queryOption, nameof(AllowedQueryOptions)) }; } - - validationErrors = errors; } private static void ValidateNotEmptyOrWhitespace(string rawValue) @@ -340,6 +339,9 @@ private static void TryValidateNotEmptyOrWhitespace(string rawValue, ref List(1); validationErrors.Add(SRResources.SelectExpandEmptyOrWhitespace); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs index d8eade9c8..c5e1de92e 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.OData.Tests.Query.Validator; -[CollectionDefinition("FilterQueryValidatorTests", DisableParallelization = false)] +[Collection(nameof(FilterQueryValidatorTests))] public class FilterQueryValidatorTests { private MyFilterValidator _validator; diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs index cd4654aa9..2428a6715 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/ODataQueryValidatorTest.cs @@ -570,7 +570,6 @@ public void TryValidate_ReturnsFalseAndErrors_WhenSelectExpandIsEmptyOrWhitespac var settings = new ODataValidationSettings(); var expectedMessage = "'select' and 'expand' cannot be empty or whitespace. Omit the parameter from the query if it is not used."; - // Act var result = _validator.TryValidate(options, settings, out IEnumerable errors); From d03e5f91e069038bce8a037c5a46a3dd0772934e Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 2 Jun 2025 19:07:46 +0300 Subject: [PATCH 11/11] nit --- .../Query/Validator/FilterQueryValidatorTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs index c5e1de92e..b0f308e5d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Validator/FilterQueryValidatorTests.cs @@ -19,7 +19,6 @@ namespace Microsoft.AspNetCore.OData.Tests.Query.Validator; -[Collection(nameof(FilterQueryValidatorTests))] public class FilterQueryValidatorTests { private MyFilterValidator _validator;