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

Cherry-pick (a77ed76): Fix AutoExpand Issues with Capitalized $Expand System Query Options #1309

Merged
merged 2 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ internal void AddAutoSelectExpandProperties()

private IDictionary<string, string> GetODataQueryParameters()
{
Dictionary<string, string> result = new Dictionary<string, string>();
Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is case-insensitivity always supported, or does the user need to toggle some option?

Copy link
Contributor Author

@WanjohiSammy WanjohiSammy Oct 8, 2024

Choose a reason for hiding this comment

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

@habbes EnableCaseInsensitive is by default true:
IServiceProvider BuildRouteContainer(..)

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

.AddRouteComponents(..) will call BuildRouteContainer(..) to build service provider with IEdmModel and service collections.

ODataOptions AddRouteComponents(..)

builder.Services.AddControllers().AddOData(
    opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5).AddRouteComponents("odata",EdmModelBuilder.GetEdmModel()));


foreach (var query in Request.Query)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,22 @@ public IActionResult Put(int key, [FromBody] Customer customer)
public class PeopleController : ODataController
{
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<People> Get()
public IQueryable<Person> Get()
{
return AutoExpandDataSource.People.AsQueryable();
}

[EnableQuery]
public IActionResult Get(int key)
{
Person person = AutoExpandDataSource.People.FirstOrDefault(c => c.Id == key);
if (person == null)
{
return NotFound($"Cannot find person with key = {key}");
}

return Ok(person);
}
}

public class NormalOrdersController : ODataController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ public class ZipCodeInfo
public string Code { get; set; }
}

public class People
public class Person
{
public int Id { get; set; }

[AutoExpand]
public Order Order { get; set; }

public People Friend { get; set; }
public Person Friend { get; set; }
WanjohiSammy marked this conversation as resolved.
Show resolved Hide resolved
}

public class Order
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.OData.E2E.Tests.AutoExpand;
public class AutoExpandDataSource
{
private static IList<Customer> _customers;
private static IList<People> _people;
private static IList<Person> _people;
private static IList<NormalOrder> _normalOrders;

static AutoExpandDataSource()
Expand All @@ -26,7 +26,7 @@ static AutoExpandDataSource()

public static IList<Customer> Customers => _customers;

public static IList<People> People => _people;
public static IList<Person> People => _people;

public static IList<NormalOrder> NormalOrders => _normalOrders;

Expand Down Expand Up @@ -129,12 +129,12 @@ private static void GenerateCustomers()

public static void GeneratePeople()
{
_people = new List<People>();
_people = new List<Person>();

People previousPeople = null;
Person previousPerson = null;
for (int i = 1; i < 10; i++)
{
var people = new People
var person = new Person
{
Id = i,
Order = new Order
Expand All @@ -150,11 +150,11 @@ public static void GeneratePeople()

if (i > 1)
{
people.Friend = previousPeople;
person.Friend = previousPerson;
}

_people.Add(people);
previousPeople = people;
_people.Add(person);
previousPerson = person;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static IEdmModel GetEdmModel()
builder.EntityType<DerivedOrder>();
builder.EntityType<DerivedOrder2>();
builder.EntitySet<OrderDetail>("OrderDetails");
builder.EntitySet<People>("People");
builder.EntitySet<Person>("People");
builder.EntitySet<Menu>("EnableQueryMenus");
builder.EntitySet<Menu>("QueryOptionsOfTMenus");
builder.EntitySet<Tab>("Tabs");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
Expand Down Expand Up @@ -69,7 +70,7 @@ public async Task QueryForAnResource_Includes_AutoExpandNavigationProperty(strin
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var customer = await response.Content.ReadAsObject<JObject>();
var customer = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
this.output.WriteLine(customer.ToString());
Assert.Equal(customer.Properties().Count(), propCount);
VerifyOrderAndChoiceOrder(customer);
Expand All @@ -86,6 +87,150 @@ public async Task QueryForAnResource_Includes_AutoExpandNavigationProperty(strin
Assert.Null(friend["Order"]);
}

[Theory]
[InlineData("$select=Order", 1)]
[InlineData("$Select=Order", 1)]
[InlineData("$SELECT=Order", 1)]
[InlineData("select=Order", 1)]
public async Task QueryForResources_WithSelectQueryParam_SelectAutoExpandProperty(string queryParams, int propCount)
{
// Arrange
string queryUrl = $"autoexpand/People(1)?{queryParams}";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var person = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
Assert.Equal(person.Properties().Count(), propCount); // Only Order is selected
Assert.NotNull(person["Order"]);

VerifyOrderAndChoiceOrder(person);
}

[Theory]
[InlineData("$select=Id", 2)]
[InlineData("$Select=Id", 2)]
[InlineData("$SELECT=Id", 2)]
[InlineData("select=Id", 2)]
public async Task QueryForResources_WithSelectQueryParam_IncludesAutoSelectProperty(string queryParams, int propCount)
{
// Arrange
string queryUrl = $"autoexpand/People(1)?{queryParams}";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var person = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
Assert.Equal(person.Properties().Count(), propCount); // Id and Order are selected
Assert.NotNull(person["Order"]); // Order is auto-expanded
Assert.NotNull(person["Id"]);

VerifyOrderAndChoiceOrder(person);
}

[Theory]
[InlineData("$expand=Order", 2)]
[InlineData("$Expand=Order", 2)]
[InlineData("$EXPAND=Order", 2)]
[InlineData("expand=Order", 2)]
public async Task QueryForResources_WithExpandQueryParam_IncludesAutoExpandNavigationProperty(string queryParams, int propCount)
{
// Arrange
string queryUrl = $"autoexpand/People(1)?{queryParams}";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var person = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
Assert.Equal(person.Properties().Count(), propCount); // Id and Order are selected
Assert.NotNull(person["Order"]); // Order is auto-expanded
Assert.NotNull(person["Id"]);

VerifyOrderAndChoiceOrder(person);
}

[Theory]
[InlineData("$expand=Friend", 3)]
[InlineData("$Expand=Friend", 3)]
[InlineData("$EXPAND=Friend", 3)]
[InlineData("expand=Friend", 3)]
public async Task QueryForResources_WithExpandQueryParam_IncludesNonAutoExpandNavigationProperty(string queryParams, int propCount)
{
// Arrange
string queryUrl = $"autoexpand/People(1)?{queryParams}";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var person = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
Assert.Equal(person.Properties().Count(), propCount); // Id and Order are selected
Assert.NotNull(person["Order"]); // Order is auto-expanded
Assert.NotNull($"{person["Friend"]}"); // Friend is expanded
Assert.NotNull(person["Id"]);

VerifyOrderAndChoiceOrder(person);
}

[Theory]
[InlineData("$Expand=Order&Select=Friend", 3)]
[InlineData("Expand=Order&$Select=Order", 3)]
[InlineData("$Expand=Order,HomeAddress/CountryOrRegion($Select=Name)", 4)]
[InlineData("$Expand=HomeAddress/CountryOrRegion,HomeAddress/Microsoft.AspNetCore.OData.E2E.Tests.AutoExpand.UsAddress/ZipCode,Order($Expand=Choice)", 4)]
[InlineData("Expand=HomeAddress/Microsoft.AspNetCore.OData.E2E.Tests.AutoExpand.UsAddress/ZipCode", 4)]
[InlineData("Expand=Friend(Select=Order)&$Select=Order", 3)]
[InlineData("Select=Order&Expand=Order(Expand=Choice(Select=Amount))", 3)]
public async Task QueryForResources_WithExpandSelectQueryParamsCapitalized_IncludesAutoExpandNavigationProperty(string queryParams, int propCount)
{
// Arrange
string queryUrl = $"autoexpand/Customers(5)?{queryParams}";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=none"));
HttpClient client = CreateClient();

// Act
HttpResponseMessage response = await client.SendAsync(request);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);

var customer = await response.Content.ReadAsObject<JObject>().ConfigureAwait(false);
VerifyNavigationProperties(customer, propCount);
}

private static void VerifyHomeAddress(JObject customer)
{
JObject homeAddress = customer["HomeAddress"] as JObject;
Expand Down Expand Up @@ -488,4 +633,20 @@ public async Task PutCustomer_AutoExpandNavigationProperties()
}
}
}
private void VerifyNavigationProperties(JObject responseContent, int propCount)
{
Assert.Equal(responseContent.Properties().Count(), propCount);
VerifyOrderAndChoiceOrder(responseContent);
VerifyHomeAddress(responseContent);

// level one
JObject friend = responseContent["Friend"] as JObject;
JObject order = friend["Order"] as JObject;
Assert.NotNull(order);
Assert.Null(order["Choice"]);

// level two
friend = friend["Friend"] as JObject;
Assert.Null(friend["Order"]);
}
}