Skip to content

Commit d380666

Browse files
Chris Martinezcommonsensesoftware
Chris Martinez
authored andcommitted
Add OData query option support for non-OData actions. Resolves #522
1 parent e3ad94b commit d380666

File tree

11 files changed

+48
-84
lines changed

11 files changed

+48
-84
lines changed

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/Builder/DefaultODataQueryOptionDescriptionProvider.cs

+10-18
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,18 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio
3030
throw new ArgumentException( SR.MultipleQueryOptionsNotAllowed, nameof( queryOption ) );
3131
}
3232

33-
switch ( queryOption )
33+
return queryOption switch
3434
{
35-
case Filter:
36-
return DescribeFilter( context );
37-
case Expand:
38-
return DescribeExpand( context );
39-
case Select:
40-
return DescribeSelect( context );
41-
case OrderBy:
42-
return DescribeOrderBy( context );
43-
case Top:
44-
return DescribeTop( context );
45-
case Skip:
46-
return DescribeSkip( context );
47-
case Count:
48-
return DescribeCount( context );
49-
}
50-
35+
Filter => DescribeFilter( context ),
36+
Expand => DescribeExpand( context ),
37+
Select => DescribeSelect( context ),
38+
OrderBy => DescribeOrderBy( context ),
39+
Top => DescribeTop( context ),
40+
Skip => DescribeSkip( context ),
41+
Count => DescribeCount( context ),
5142
#pragma warning disable CA1308 // Normalize strings to uppercase
52-
throw new ArgumentException( SR.UnsupportedQueryOption.FormatDefault( queryOption.ToString().ToLowerInvariant() ), nameof( queryOption ) );
43+
_ => throw new ArgumentException( SR.UnsupportedQueryOption.FormatDefault( queryOption.ToString().ToLowerInvariant() ), nameof( queryOption ) ),
44+
};
5345
#pragma warning restore CA1308
5446
}
5547

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/Builder/ODataActionQueryOptionsConventionBuilderExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public static ODataActionQueryOptionsConventionBuilder Action( this IODataAction
137137
return builder.Action( methods[0] );
138138
}
139139

140-
argumentTypes = argumentTypes ?? Type.EmptyTypes;
140+
argumentTypes ??= Type.EmptyTypes;
141141
methods = methods.Where( m => SignatureMatches( m, argumentTypes ) ).ToArray();
142142

143143
if ( methods.Length == 1 )

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/Builder/ODataAttributeVisitor.cs

-22
Original file line numberDiff line numberDiff line change
@@ -68,28 +68,6 @@ internal void Visit( ApiDescription apiDescription )
6868
}
6969
}
7070

71-
static void ClearCollectionIfItContainsAllProperties( IList<string> list, ISet<string> set )
72-
{
73-
Contract.Requires( list != null );
74-
Contract.Requires( set != null );
75-
76-
if ( list.Count != set.Count )
77-
{
78-
return;
79-
}
80-
81-
for ( var i = 0; i < list.Count; i++ )
82-
{
83-
if ( !set.Contains( list[i] ) )
84-
{
85-
return;
86-
}
87-
}
88-
89-
// avoid verbose messages by clearing the list when it contains all possible properties
90-
list.Clear();
91-
}
92-
9371
void VisitModel( IEdmStructuredType modelType )
9472
{
9573
Contract.Requires( modelType != null );

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/Builder/ODataQueryOptionsConventionBuilder.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,13 @@ public virtual void ApplyTo( IEnumerable<ApiDescription> apiDescriptions, ODataQ
137137
{
138138
var controller = GetController( description );
139139

140-
if ( !controller.IsODataController() )
141-
{
142-
continue;
143-
}
144-
145140
if ( !conventions.TryGetValue( controller, out var convention ) )
146141
{
142+
if ( !controller.IsODataController() && !IsODataLike( description ) )
143+
{
144+
continue;
145+
}
146+
147147
if ( !ConventionBuilders.TryGetValue( controller, out var builder ) )
148148
{
149149
builder = new ODataControllerQueryOptionsConventionBuilder( controller );

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/Routing/ODataRouteBuilder.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ string FixUpArrayParameters( string template, IEdmOperation operation )
354354
return template;
355355
}
356356

357-
int IndexOfToken( StringBuilder builder, string token )
357+
static int IndexOfToken( StringBuilder builder, string token )
358358
{
359359
var index = -1;
360360

@@ -393,7 +393,7 @@ int IndexOfToken( StringBuilder builder, string token )
393393
return index;
394394
}
395395

396-
void InsertBrackets( StringBuilder builder, string token )
396+
static void InsertBrackets( StringBuilder builder, string token )
397397
{
398398
var index = IndexOfToken( builder, token );
399399

Diff for: src/Common.OData.ApiExplorer/AspNet.OData/StructuredTypeResolver.cs

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ internal IEdmStructuredType GetStructuredType( Type type )
1616
{
1717
Contract.Requires( type != null );
1818

19+
if ( model == null )
20+
{
21+
return default;
22+
}
23+
1924
var structuredTypes = model.SchemaElements.OfType<IEdmStructuredType>();
2025
var structuredType = structuredTypes.FirstOrDefault( t => type.Equals( t.GetClrType( model ) ) );
2126

Diff for: src/Microsoft.AspNet.OData.Versioning.ApiExplorer/AspNet.OData/Builder/ODataQueryOptionsConventionBuilder.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public partial class ODataQueryOptionsConventionBuilder
1010
{
1111
static Type GetKey( Type type ) => type;
1212

13-
static Type GetController( ApiDescription apiDescription ) => apiDescription.ActionDescriptor.ControllerDescriptor.ControllerType;
13+
static Type GetController( ApiDescription apiDescription ) =>
14+
apiDescription.ActionDescriptor.ControllerDescriptor.ControllerType;
15+
16+
static bool IsODataLike( ApiDescription description ) =>
17+
description.ActionDescriptor.GetCustomAttributes<EnableQueryAttribute>( inherit: true ).Count > 0;
1418
}
1519
}

Diff for: src/Microsoft.AspNet.OData.Versioning.ApiExplorer/AspNet.OData/Builder/ODataValidationSettingsConvention.cs

+5-10
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,12 @@ static bool IsSupported( ApiDescription apiDescription )
157157
{
158158
Contract.Requires( apiDescription != null );
159159

160-
switch ( apiDescription.HttpMethod.Method.ToUpperInvariant() )
160+
return apiDescription.HttpMethod.Method.ToUpperInvariant() switch
161161
{
162-
case "GET":
163-
// query or function
164-
return true;
165-
case "POST":
166-
// action
167-
return apiDescription.Operation()?.IsAction() == true;
168-
}
169-
170-
return false;
162+
"GET" => true,
163+
"POST" => apiDescription.Operation()?.IsAction() == true,
164+
_ => false,
165+
};
171166
}
172167
}
173168
}

Diff for: src/Microsoft.AspNetCore.OData.Versioning.ApiExplorer/AspNet.OData/Builder/ODataQueryOptionsConventionBuilder.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
namespace Microsoft.AspNet.OData.Builder
22
{
3-
using Microsoft.AspNet.OData.Query;
43
using Microsoft.AspNetCore.Mvc.ApiExplorer;
54
using Microsoft.AspNetCore.Mvc.Controllers;
6-
using Microsoft.AspNetCore.Mvc.ModelBinding;
75
using System;
86
using System.Reflection;
97

@@ -24,5 +22,15 @@ static TypeInfo GetController( ApiDescription apiDescription )
2422

2523
return typeof( object ).GetTypeInfo();
2624
}
25+
26+
static bool IsODataLike( ApiDescription description )
27+
{
28+
if ( description.ActionDescriptor is ControllerActionDescriptor action )
29+
{
30+
return Attribute.IsDefined( action.ControllerTypeInfo, typeof( EnableQueryAttribute ), inherit: true );
31+
}
32+
33+
return false;
34+
}
2735
}
2836
}

Diff for: src/Microsoft.AspNetCore.OData.Versioning.ApiExplorer/AspNet.OData/Builder/ODataValidationSettingsConvention.cs

+5-10
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,12 @@ static bool IsSupported( ApiDescription apiDescription )
165165
{
166166
Contract.Requires( apiDescription != null );
167167

168-
switch ( apiDescription.HttpMethod.ToUpperInvariant() )
168+
return apiDescription.HttpMethod.ToUpperInvariant() switch
169169
{
170-
case "GET":
171-
// query or function
172-
return true;
173-
case "POST":
174-
// action
175-
return apiDescription.Operation()?.IsAction() == true;
176-
}
177-
178-
return false;
170+
"GET" => true,
171+
"POST" => apiDescription.Operation()?.IsAction() == true,
172+
_ => false,
173+
};
179174
}
180175
}
181176
}

Diff for: src/Microsoft.AspNetCore.OData.Versioning.ApiExplorer/AspNetCore.Mvc.ApiExplorer/ODataApiDescriptionProvider.cs

-13
Original file line numberDiff line numberDiff line change
@@ -330,18 +330,6 @@ static Type GetDeclaredReturnType( ControllerActionDescriptor action )
330330

331331
static Type GetRuntimeReturnType( Type declaredReturnType ) => declaredReturnType == typeof( object ) ? default : declaredReturnType;
332332

333-
static IReadOnlyList<IApiRequestMetadataProvider> GetRequestMetadataAttributes( ControllerActionDescriptor action )
334-
{
335-
Contract.Requires( action != null );
336-
337-
if ( action.FilterDescriptors == null )
338-
{
339-
return default;
340-
}
341-
342-
return action.FilterDescriptors.Select( fd => fd.Filter ).OfType<IApiRequestMetadataProvider>().ToArray();
343-
}
344-
345333
static IReadOnlyList<IApiResponseMetadataProvider> GetResponseMetadataAttributes( ControllerActionDescriptor action )
346334
{
347335
Contract.Requires( action != null );
@@ -378,7 +366,6 @@ IEnumerable<ApiDescription> NewODataApiDescriptions( ControllerActionDescriptor
378366
Contract.Requires( mapping != null );
379367
Contract.Ensures( Contract.Result<ApiDescription>() != null );
380368

381-
var requestMetadataAttributes = GetRequestMetadataAttributes( action );
382369
var responseMetadataAttributes = GetResponseMetadataAttributes( action );
383370
var declaredReturnType = GetDeclaredReturnType( action );
384371
var runtimeReturnType = GetRuntimeReturnType( declaredReturnType );

0 commit comments

Comments
 (0)