Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #1645 Interface Support Mock Testing #2215

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
34c5ec9
#1645 okay this is the easy part, now I get to hunt down where the in…
kccarter76 Jun 29, 2020
74a4d3a
Create dotnet-core.yml
kccarter76 Jun 29, 2020
97340aa
Update dotnet-core.yml
kccarter76 Jun 29, 2020
06f2992
Update dotnet-core.yml
kccarter76 Jun 29, 2020
4bc104a
Update dotnet-core.yml
kccarter76 Jun 29, 2020
7bd2fe9
Update dotnet-core.yml
kccarter76 Jun 29, 2020
4998dc8
Update dotnet-core.yml
kccarter76 Jun 29, 2020
9a1c79d
trying to get the github integration server building this code
kccarter76 Jun 29, 2020
c42f738
#1645
kccarter76 Jun 29, 2020
4f0872d
linux is very picky about names
kccarter76 Jun 29, 2020
a774ae3
Update dotnet-core.yml
kccarter76 Jun 29, 2020
8d08fbe
Rename ProductsCSDL.xml to ProductsCsdl.xml
kccarter76 Jun 30, 2020
090f996
no message
kccarter76 Jun 30, 2020
691a0ef
local test run completes succesful
kccarter76 Jun 30, 2020
f80cb57
Merge pull request #5 from kccarter76/dev/issue-1645
kccarter76 Jun 30, 2020
fa89897
#1645 unit test added and all unit tests passing
kccarter76 Jun 30, 2020
d6f43e4
Merge pull request #6 from kccarter76/dev/issue-1645
kccarter76 Jun 30, 2020
304be77
Delete dotnet-core.yml
kccarter76 Jun 30, 2020
8b1f5a2
remove the code that the designer did not
kccarter76 Jun 30, 2020
ed6d0b9
#1645 moved the interfaces to the shared project and adjusted things …
kccarter76 Jul 1, 2020
0c22e8e
#1645 updated the EnableQueryAttribute for AspNet
kccarter76 Jul 1, 2020
00da586
#1645 finally was able to generate the bsl
kccarter76 Jul 1, 2020
66e8f9a
update code files based on comments by marabooy
kccarter76 Sep 3, 2020
b8fc39e
per maraboy removed the notsupported exception
kccarter76 Sep 3, 2020
0198ea4
seperated the interface and class name space for this sut into sepera…
kccarter76 Jan 15, 2021
7ce2341
Merge branch 'master' of https://github.com/OData/WebApi into OData-m…
kccarter76 Jan 15, 2021
127a7d9
resolved conflicts
kccarter76 Jan 15, 2021
6567621
no message
kccarter76 Jan 15, 2021
ddffb5d
resolve conflct
kccarter76 Jan 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions sln/WebApiOData.Performance.Official.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio Version 16
VisualStudioVersion = 16.0.30204.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiPerformance.Service", "..\test\PerfTest\WebApiPerformance.Service\WebApiPerformance.Service.csproj", "{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}"
EndProject
Expand All @@ -19,12 +19,12 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Debug|Any CPU.Build.0 = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Release|Any CPU.Build.0 = Release|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{F7B5A5D5-0B2F-493B-94B2-039B881FD4D3}.Release|Any CPU.Build.0 = Debug|Any CPU
{5C4C506B-9BFE-429D-A48C-77CEC613877A}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
{5C4C506B-9BFE-429D-A48C-77CEC613877A}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{5C4C506B-9BFE-429D-A48C-77CEC613877A}.Debug|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -35,4 +35,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07010717-A406-417E-B0C8-612DEF0AD8ED}
EndGlobalSection
EndGlobal
16 changes: 8 additions & 8 deletions src/Microsoft.AspNet.OData.Shared/EnableQueryAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ private object OnActionExecuted(
IWebApiActionDescriptor actionDescriptor,
IWebApiRequestMessage request,
Func<Type, IEdmModel> modelFunction,
Func<ODataQueryContext, ODataQueryOptions> createQueryOptionFunction,
Func<ODataQueryContext, IODataQueryOptions> createQueryOptionFunction,
Action<HttpStatusCode> createResponseAction,
Action<HttpStatusCode, string, Exception> createErrorAction)
{
Expand Down Expand Up @@ -466,9 +466,9 @@ private object OnActionExecuted(
/// </summary>
/// <param name="queryable">The original queryable instance from the response message.</param>
/// <param name="queryOptions">
/// The <see cref="ODataQueryOptions"/> instance constructed based on the incoming request.
/// The <see cref="IODataQueryOptions"/> instance constructed based on the incoming request.
/// </param>
public virtual IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
public virtual IQueryable ApplyQuery(IQueryable queryable, IODataQueryOptions queryOptions)
{
if (queryable == null)
{
Expand All @@ -487,10 +487,10 @@ public virtual IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions que
/// </summary>
/// <param name="entity">The original entity from the response message.</param>
/// <param name="queryOptions">
/// The <see cref="ODataQueryOptions"/> instance constructed based on the incoming request.
/// The <see cref="IODataQueryOptions"/> instance constructed based on the incoming request.
/// </param>
/// <returns>The new entity after the $select and $expand query has been applied to.</returns>
public virtual object ApplyQuery(object entity, ODataQueryOptions queryOptions)
public virtual object ApplyQuery(object entity, IODataQueryOptions queryOptions)
{
if (entity == null)
{
Expand Down Expand Up @@ -588,12 +588,12 @@ private object ExecuteQuery(
IWebApiActionDescriptor actionDescriptor,
Func<Type, IEdmModel> modelFunction,
IWebApiRequestMessage request,
Func<ODataQueryContext, ODataQueryOptions> createQueryOptionFunction)
Func<ODataQueryContext, IODataQueryOptions> createQueryOptionFunction)
{
ODataQueryContext queryContext = GetODataQueryContext(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path);

// Create and validate the query options.
ODataQueryOptions queryOptions = createQueryOptionFunction(queryContext);
IODataQueryOptions queryOptions = createQueryOptionFunction(queryContext);

// apply the query
IEnumerable enumerable = responseValue as IEnumerable;
Expand Down Expand Up @@ -718,7 +718,7 @@ internal static object SingleOrDefault(
/// Validate the select and expand options.
/// </summary>
/// <param name="queryOptions">The query options.</param>
internal static void ValidateSelectExpandOnly(ODataQueryOptions queryOptions)
internal static void ValidateSelectExpandOnly(IODataQueryOptions queryOptions)
{
if (queryOptions.Filter != null || queryOptions.Count != null || queryOptions.OrderBy != null
|| queryOptions.Skip != null || queryOptions.Top != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ internal ExpandedReferenceSelectItem CurrentExpandedSelectItem
internal SelectItem CurrentSelectItem { get; set; }

/// <summary>
/// Gets or sets the <see cref="ODataQueryOptions"/>.
/// Gets or sets the <see cref="IODataQueryOptions"/>.
/// </summary>
public ODataQueryOptions QueryOptions { get; internal set; }
public IODataQueryOptions QueryOptions { get; internal set; }

/// <summary>
/// Gets or sets the relative path to the resouce being serialized
Expand Down
151 changes: 151 additions & 0 deletions src/Microsoft.AspNet.OData.Shared/Interfaces/IODataQueryOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using Microsoft.AspNet.OData.Formatter;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Query.Validators;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Microsoft.AspNet.OData.Query
{
/// <summary>
/// This defines a composite OData query options that can be used to perform query composition.
/// Currently this only supports $filter, $orderby, $top, $skip, and $count.
/// </summary>
[ODataQueryParameterBinding]
[NonValidatingParameterBinding]
public interface IODataQueryOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more of a discussion than feedback: I came into this review not really knowing what query options were, and saw that the purpose was to convert it to an interface for mocking. I wondered to myself "how many ways really are there to implement a set of options? won't that just be a data type with a bunch of data type properties? there should just be a constructor overload where anything that needs to be mocked can be set directly instead". Seeing this interface, though, I see the many methods that are useful for the query options. I'm wondering, though, if it makes sense to separate those two things out. In this case, I would think the ApplyTo overloads belong on some other type rather than the options themselves. Do you have any thoughts on this?

{
/// <summary>
/// Gets the given <see cref="ODataQueryContext"/>
/// </summary>
ODataQueryContext Context { get; }
/// <summary>
/// Gets the raw string of all the OData query options
/// </summary>
ODataRawQueryOptions RawValues { get; }
/// <summary>
/// Gets the <see cref="SelectExpandQueryOption"/>.
/// </summary>
SelectExpandQueryOption SelectExpand { get; }
/// <summary>
/// Gets the <see cref="ApplyQueryOption"/>.
/// </summary>
ApplyQueryOption Apply { get; }
/// <summary>
/// Gets the <see cref="FilterQueryOption"/>.
/// </summary>
FilterQueryOption Filter { get; }
/// <summary>
/// Gets the <see cref="OrderByQueryOption"/>.
/// </summary>
OrderByQueryOption OrderBy { get; }
/// <summary>
/// Gets the <see cref="SkipQueryOption"/>.
/// </summary>
SkipQueryOption Skip { get; }

/// <summary>
/// Gets the <see cref="SkipTokenQueryOption"/>.
/// </summary>
SkipTokenQueryOption SkipToken { get; }

/// <summary>
/// Gets the <see cref="TopQueryOption"/>.
/// </summary>
TopQueryOption Top { get; }

/// <summary>
/// Gets the <see cref="CountQueryOption"/>.
/// </summary>
CountQueryOption Count { get; }

/// <summary>
/// Gets or sets the query validator.
/// </summary>
ODataQueryValidator Validator { get; set; }

/// <summary>
/// Gets the <see cref="ETag"/> from IfMatch header.
/// </summary>
ETag IfMatch { get; }

/// <summary>
/// Gets the <see cref="ETag"/> from IfNoneMatch header.
/// </summary>
ETag IfNoneMatch { get; }

/// <summary>
/// Apply the individual query to the given IQueryable in the right order.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
IQueryable ApplyTo(IQueryable query);

/// <summary>
/// Apply the individual query to the given IQueryable in the right order.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <param name="ignoreQueryOptions">The query parameters that are already applied in queries.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
IQueryable ApplyTo(IQueryable query, AllowedQueryOptions ignoreQueryOptions);

/// <summary>
/// Apply the individual query to the given IQueryable in the right order.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <param name="querySettings">The settings to use in query composition.</param>
/// <param name="ignoreQueryOptions">The query parameters that are already applied in queries.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, AllowedQueryOptions ignoreQueryOptions);

/// <summary>
/// Apply the individual query to the given IQueryable in the right order.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <param name="querySettings">The settings to use in query composition.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple conversion function and cannot be split up.")]
IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings);

/// <summary>
/// Apply the individual query to the given IQueryable in the right order.
/// </summary>
/// <param name="entity">The original entity.</param>
/// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param>
/// <param name="ignoreQueryOptions">The query parameters that are already applied in queries.</param>
/// <returns>The new entity after the $select and $expand query has been applied to.</returns>
/// <remarks>Only $select and $expand query options can be applied on single entities. This method throws if the query contains any other
/// query options.</remarks>
object ApplyTo(object entity, ODataQuerySettings querySettings, AllowedQueryOptions ignoreQueryOptions);

/// <summary>
/// Applies the query to the given entity using the given <see cref="ODataQuerySettings"/>.
/// </summary>
/// <param name="entity">The original entity.</param>
/// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param>
/// <returns>The new entity after the $select and $expand query has been applied to.</returns>
/// <remarks>Only $select and $expand query options can be applied on single entities. This method throws if the query contains any other
/// query options.</remarks>
object ApplyTo(object entity, ODataQuerySettings querySettings);

/// <summary>
/// Generates the Stable OrderBy query option based on the existing OrderBy and other query options.
/// </summary>
/// <returns>An order by query option that ensures stable ordering of the results.</returns>
OrderByQueryOption GenerateStableOrder();

/// <summary>
/// Check if the given query option is the supported query option.
/// </summary>
/// <param name="queryOptionName">The name of the query option.</param>
/// <returns>Returns <c>true</c> if the query option is the supported query option.</returns>
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Need lower case string here.")]
bool IsSupportedQueryOption(string queryOptionName);

/// <summary>
/// Validate all OData queries, including $skip, $top, $orderby and $filter, based on the given <paramref name="validationSettings"/>.
/// It throws an ODataException if validation failed.
/// </summary>
/// <param name="validationSettings">The <see cref="ODataValidationSettings"/> instance which contains all the validation settings.</param>
void Validate(ODataValidationSettings validationSettings);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNet.OData.Formatter;
using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.AspNet.OData.Query
{
/// <summary>
/// This defines a composite OData query options that can be used to perform query composition.
/// Currently this only supports $filter, $orderby, $top, $skip.
/// </summary>
[ODataQueryParameterBinding]
public interface IODataQueryOptions<TEntity>
: IODataQueryOptions
{
/// <summary>
/// Gets the <see cref="ETag{TEntity}"/> from IfMatch header.
/// </summary>
new ETag<TEntity> IfMatch { get; }

/// <summary>
/// Gets the <see cref="ETag{TEntity}"/> from IfNoneMatch header.
/// </summary>
new ETag<TEntity> IfNoneMatch { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

using System;
kccarter76 marked this conversation as resolved.
Show resolved Hide resolved
using System.Collections.Generic;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Routing.Conventions;
using Microsoft.OData.UriParser;
using Microsoft.AspNet.OData.Query;
using Microsoft.OData.UriParser.Aggregation;
using Microsoft.OData.UriParser;
using ODataPath = Microsoft.AspNet.OData.Routing.ODataPath;

namespace Microsoft.AspNet.OData.Interfaces
Expand Down Expand Up @@ -63,9 +63,9 @@ internal interface IWebApiContext
SelectExpandClause ProcessedSelectExpandClause { get; set; }

/// <summary>
/// Gets or sets the <see cref="ODataQueryOptions"/> of the request.
/// Gets or sets the <see cref="IODataQueryOptions"/> of the request.
/// </summary>
ODataQueryOptions QueryOptions { get; set; }
IODataQueryOptions QueryOptions { get; set; }

/// <summary>
/// Gets or sets the total count for the OData response.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Formatter\QueryStringMediaTypeMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Formatter\Serialization\ODataSerializerProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GetNextPageHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IODataQueryOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IODataQueryOptionsOfTEntity.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IPerRouteContainer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompatibilityOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ODataNullValueMessageHandler.cs" />
Expand Down Expand Up @@ -169,7 +171,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Builder\DynamicPropertyDictionaryAnnotation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\AggregationBinder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\ComputeBinder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\TransformationBinderBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\TransformationBinderBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\ApplyQueryOption.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\DynamicTypeWrapper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Query\Expressions\ExpressionBinderBase.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Diagnostics.Contracts;
using System.Linq;
using Microsoft.AspNet.OData.Interfaces;
using Microsoft.AspNet.OData.Query;

namespace Microsoft.AspNet.OData
Expand All @@ -18,7 +19,8 @@ internal static Type GetEntityClrTypeFromParameterType(Type parameterType)
Contract.Assert(parameterType != null);

if (parameterType.IsGenericType &&
parameterType.GetGenericTypeDefinition() == typeof(ODataQueryOptions<>))
(parameterType.GetGenericTypeDefinition() == typeof(ODataQueryOptions<>) ||
parameterType.GetGenericTypeDefinition() == typeof(IODataQueryOptions<>)))
{
return parameterType.GetGenericArguments().Single();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.AspNet.OData.Common;
using Microsoft.AspNet.OData.Formatter;
using Microsoft.AspNet.OData.Formatter.Serialization;
using Microsoft.AspNet.OData.Interfaces;
using Microsoft.AspNet.OData.Query.Expressions;
using Microsoft.OData;
using Microsoft.OData.Edm;
Expand Down Expand Up @@ -181,7 +182,7 @@ public override IQueryable ApplyTo(IQueryable query, SkipTokenQueryOption skipTo
}

ODataQuerySettings querySettings = skipTokenQueryOption.QuerySettings;
ODataQueryOptions queryOptions = skipTokenQueryOption.QueryOptions;
IODataQueryOptions queryOptions = skipTokenQueryOption.QueryOptions;
IList<OrderByNode> orderByNodes = null;

if (queryOptions != null)
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.AspNet.OData.Shared/Query/ODataQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Microsoft.AspNet.OData.Query
[ODataQueryParameterBinding]
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Relies on many ODataLib classes.")]
public partial class ODataQueryOptions
: IODataQueryOptions
{
private static readonly MethodInfo _limitResultsGenericMethod = typeof(ODataQueryOptions).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Single(mi => mi.Name == "LimitResults" && mi.ContainsGenericParameters && mi.GetParameters().Length == 4);
Expand All @@ -51,7 +52,7 @@ public partial class ODataQueryOptions
private OrderByQueryOption _stableOrderBy;

/// <summary>
/// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the incoming request and some metadata information from
/// Initializes a new instance of the <see cref="IODataQueryOptions"/> class based on the incoming request and some metadata information from
/// the <see cref="ODataQueryContext"/>.
/// </summary>
/// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information.</param>
Expand Down
Loading