Skip to content

Commit

Permalink
Fixes #1379: Enable to validate query option on unbound functions (#1380
Browse files Browse the repository at this point in the history
)

* Fixes #1379: Enable to validate query option on unbound functions

* Update src/Microsoft.AspNetCore.OData/Routing/ODataPathExtensions.cs

Co-authored-by: Clément Habinshuti <[email protected]>

---------

Co-authored-by: Clément Habinshuti <[email protected]>
  • Loading branch information
xuzhg and habbes authored Jan 9, 2025
1 parent c62784c commit 095846a
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/Microsoft.AspNetCore.OData/Routing/ODataPathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,20 @@ internal static (IEdmProperty, IEdmStructuredType, string) GetPropertyAndStructu
return (null, structuredType, name);
}

if (segment is OperationImportSegment operationImportSegment)
{
IEdmOperationImport operationImport = operationImportSegment.OperationImports.First();
IEdmTypeReference edmType = operationImport.Operation.ReturnType;
if (edmType == null)
{
return (null, null, operationImport.Name);
}

edmType = edmType.Definition.AsElementType();

return (null, edmType.Definition as IEdmStructuredType, operationImport.Name);
}

if (segment is TypeSegment typeSegment)
{
structuredType = typeSegment.EdmType.AsElementType() as IEdmStructuredType;
Expand Down
34 changes: 34 additions & 0 deletions test/Microsoft.AspNetCore.OData.Tests/ODataPathExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,40 @@ public void GetPathString_Returns_Path()
Assert.Equal("$metadata", segments.GetPathString());
}

[Fact]
public void GetPropertyAndStructuredTypeFromPath_Returns_CorrectInformation()
{
// Arrange
var model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
var customers = container.AddEntitySet("Customers", customer);
model.AddElement(container);

// function with entity set path
EdmFunction getTopCustomers = new EdmFunction("NS", "GetTopCustomers",
new EdmCollectionTypeReference(new EdmCollectionType(new EdmEntityTypeReference(customer, false))),
isBound: false,
entitySetPathExpression: customers.Path,
isComposable: false
);
var getTopCustomersImport = container.AddFunctionImport("GetTopCustomers", getTopCustomers, customers.Path, includeInServiceDocument: true);

OperationImportSegment importSegment = new OperationImportSegment(getTopCustomersImport, customers);
ODataPath path = new ODataPath(importSegment);

// Act
(IEdmProperty p, IEdmStructuredType t, string n) = path.GetPropertyAndStructuredTypeFromPath();

// Assert
Assert.Null(p);
Assert.NotNull(t);
Assert.Same(customer, t);
Assert.Equal("GetTopCustomers", n);
}

/// <summary>
/// Test path segment used to test handling of unknown path segments.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,38 @@ public void Validate_ThrowsValidationErrors_ForOrderBy()
"The number of clauses in $orderby query option exceeded the maximum number allowed. The maximum number of $orderby clauses allowed is 1.");
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void Validate_Works_ForSelectOnReturnOfUnboundFunctionCall_WithOrWithoutSelectEnabled(bool enableSelect)
{
// Arrange
IEdmModel model = GetEdmModel(c => c.CustomerId, builder =>
{
builder.Function("GetTopCustomers").ReturnsCollectionFromEntitySet<Customer>("Customers");
});

HttpRequest request = RequestFactory.Create(HttpMethods.Get, "http://server/service/GetTopCustomers()?$select=*");

ODataQueryContext queryContext = new ODataQueryContext(model, typeof(Customer));
queryContext.DefaultQueryConfigurations.EnableSelect = enableSelect;
var options = new ODataQueryOptions(queryContext, request);
ODataValidationSettings validationSettings = new ODataValidationSettings { MaxOrderByNodeCount = 1 };

// Act & Assert
Exception exception = Record.Exception(() => options.Validate(validationSettings));

if (enableSelect)
{
Assert.Null(exception);
}
else
{
Assert.NotNull(exception);
Assert.Equal("The property 'CustomerId' cannot be used in the $select query option.", exception.Message);
}
}

[Theory]
[InlineData("$orderby")]
[InlineData("$filter")]
Expand Down Expand Up @@ -1274,7 +1306,7 @@ private static IEdmModel GetEdmModelWithoutKey()
return GetEdmModel<int>(null);
}

private static IEdmModel GetEdmModel<TKey>(Expression<Func<Customer, TKey>> keyDefinitionExpression)
private static IEdmModel GetEdmModel<TKey>(Expression<Func<Customer, TKey>> keyDefinitionExpression, Action<ODataModelBuilder> moreConfigs = null)
{
Mock<ODataModelBuilder> mock = new Mock<ODataModelBuilder>();
mock.Setup(b => b.ValidateModel(It.IsAny<IEdmModel>())).Callback(() => { });
Expand All @@ -1292,6 +1324,12 @@ private static IEdmModel GetEdmModel<TKey>(Expression<Func<Customer, TKey>> keyD
customer.Property(c => c.SharePrice);
customer.Property(c => c.ShareSymbol);
builder.EntitySet<Customer>("Customers");

if (moreConfigs != null)
{
moreConfigs(builder);
}

return builder.GetEdmModel();
}

Expand Down

0 comments on commit 095846a

Please sign in to comment.