Skip to content

Commit

Permalink
Support FromBody parameters with actions and functions
Browse files Browse the repository at this point in the history
  • Loading branch information
gathogojr authored and xuzhg committed Apr 27, 2022
1 parent eea3a95 commit 3b0b4a2
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ internal static async Task WriteToStreamAsync(
IODataSerializer serializer = GetSerializer(type, value, request, serializerProvider);

ODataPath path = request.ODataFeature().Path;
IEdmNavigationSource targetNavigationSource = GetTargetNavigationSource(path, model);
IEdmNavigationSource targetNavigationSource = path.GetNavigationSource();
HttpResponse response = request.HttpContext.Response;

// serialize a response
Expand Down Expand Up @@ -196,30 +196,6 @@ internal static IODataSerializer GetSerializer(Type type, object value, HttpRequ
return serializer;
}

private static IEdmNavigationSource GetTargetNavigationSource(ODataPath path, IEdmModel model)
{
if (path == null)
{
return null;
}

Contract.Assert(model != null);

OperationSegment operationSegment = path.LastSegment as OperationSegment;
if (operationSegment != null)
{
// OData model builder uses an annotation to save the function returned entity set.
// TODO: we need to refactor it later.
ReturnedEntitySetAnnotation entitySetAnnotation = model.GetAnnotationValue<ReturnedEntitySetAnnotation>(operationSegment.Operations.Single());
if (entitySetAnnotation != null)
{
return model.EntityContainer.FindEntitySet(entitySetAnnotation.EntitySetName);
}
}

return path.GetNavigationSource();
}

private static string GetRootElementName(ODataPath path)
{
if (path != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Routing.Template
Expand Down Expand Up @@ -104,7 +105,15 @@ public override bool TryTranslate(ODataTemplateTranslateContext context)
throw Error.ArgumentNull(nameof(context));
}

context.Segments.Add(Segment);
if (NavigationSource != null)
{
context.Segments.Add(Segment);
return true;
}

IEdmNavigationSource navigationSource = SegmentTemplateHelpers.GetNavigationSourceFromEdmOperation(context.Model, Action);
OperationSegment actionSegment = new OperationSegment(Action, navigationSource as IEdmEntitySetBase);
context.Segments.Add(actionSegment);
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.AspNetCore.OData.Edm;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Routing.Template
Expand Down Expand Up @@ -144,10 +145,16 @@ public override bool TryTranslate(ODataTemplateTranslateContext context)
throw Error.ArgumentNull(nameof(context));
}

IEdmNavigationSource navigationSource = NavigationSource;
if (navigationSource == null)
{
navigationSource = SegmentTemplateHelpers.GetNavigationSourceFromEdmOperation(context.Model, Function);
}

// If the function has no parameter, we don't need to do anything and just return an operation segment.
if (ParameterMappings.Count == 0)
{
context.Segments.Add(new OperationSegment(Function, NavigationSource as IEdmEntitySetBase));
context.Segments.Add(new OperationSegment(Function, navigationSource as IEdmEntitySetBase));
return true;
}

Expand All @@ -172,7 +179,7 @@ public override bool TryTranslate(ODataTemplateTranslateContext context)
return false;
}

context.Segments.Add(new OperationSegment(Function, parameters, NavigationSource as IEdmEntitySetBase));
context.Segments.Add(new OperationSegment(Function, parameters, navigationSource as IEdmEntitySetBase));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.AspNetCore.Routing;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Routing.Template
Expand Down Expand Up @@ -173,5 +174,25 @@ internal static bool IsMatchParameters(RouteValueDictionary routeValues, IDictio
// 3) now the parsedKeyValues (p1, p3) is not equal to actualParameters (p1, p2, p3)
return parameterMappings.Count == parsedKeyValues.Count;
}

/// <summary>
/// Gets the navigation source from an Edm operation.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="operation">The Edm operation.</param>
/// <returns>
/// The navigation source or null if the annotation indicating the mapping from an Edm operation to an entity set is not found.
/// </returns>
internal static IEdmNavigationSource GetNavigationSourceFromEdmOperation(IEdmModel model, IEdmOperation operation)
{
ReturnedEntitySetAnnotation entitySetAnnotation = model?.GetAnnotationValue<ReturnedEntitySetAnnotation>(operation);

if (entitySetAnnotation != null)
{
return model.EntityContainer.FindEntitySet(entitySetAnnotation.EntitySetName);
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// </copyright>
//------------------------------------------------------------------------------

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.OData.Extensions;
using Microsoft.AspNetCore.OData.Formatter.MediaType;
Expand All @@ -13,6 +14,7 @@
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;
using Xunit;

namespace Microsoft.AspNetCore.OData.Tests.Formatter.Serialization
Expand All @@ -26,6 +28,8 @@ public async Task ComplexTypeSerializesAsOData()
string routeName = "OData";
IEdmModel model = GetSampleModel();
var request = RequestFactory.Create("Get", "http://localhost/property", opt => opt.AddRouteComponents(routeName, model));
var addressComplexType = model.SchemaElements.OfType<IEdmComplexType>().Single(d => d.Name.Equals("Address"));
request.ODataFeature().Path = new ODataPath(new ValueSegment(addressComplexType));
request.ODataFeature().Model = model;
request.ODataFeature().RoutePrefix = routeName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Routing;
using Microsoft.AspNetCore.OData.Routing.Template;
using Microsoft.AspNetCore.OData.Tests.Commons;
using Microsoft.AspNetCore.Routing;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;
using Moq;
using Xunit;

namespace Microsoft.AspNetCore.OData.Tests.Routing.Template
Expand Down Expand Up @@ -142,5 +147,38 @@ public void TryTranslateActionSegmentTemplate_ReturnsODataActionImportSegment()
OperationSegment actionSegment = Assert.IsType<OperationSegment>(actual);
Assert.Same(action, actionSegment.Operations.First());
}

[Fact]
public void TryTranslateActionSegmentTemplate_ReturnsODataActionSegment_WithReturnedEntitySet()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var endpoint = new Endpoint(c => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test");
var routeValues = new RouteValueDictionary();

var model = new EdmModel();
var entityType = new EdmEntityType("NS", "Entity");
entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
model.AddElement(entityType);
EdmAction action = new EdmAction("NS", "Action", new EdmEntityTypeReference(entityType, true), true, null);
model.AddElement(action);
var entityContainer = new EdmEntityContainer("NS", "Default");
var entitySet = entityContainer.AddEntitySet("EntitySet", entityType);
model.AddElement(entityContainer);
model.SetAnnotationValue(action, new ReturnedEntitySetAnnotation("EntitySet"));

var template = new ActionSegmentTemplate(action, null);
var translateContext = new ODataTemplateTranslateContext(httpContext, endpoint, routeValues, model);

// Act
bool ok = template.TryTranslate(translateContext);

// Assert
Assert.True(ok);
var actual = Assert.Single(translateContext.Segments);
var actionSegment = Assert.IsType<OperationSegment>(actual);
Assert.Equal(actionSegment.EdmType, entityType);
Assert.Equal(actionSegment.EntitySet, entitySet);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Routing;
using Microsoft.AspNetCore.OData.Routing.Template;
using Microsoft.AspNetCore.OData.Tests.Commons;
using Microsoft.AspNetCore.Routing;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.UriParser;
using Moq;
using Xunit;
Expand Down Expand Up @@ -404,5 +407,38 @@ public void TryTranslateFunctionSegmentTemplate_ReturnsODataFunctionSegment_Usin
Assert.Equal("name", parameter.Name);
Assert.Equal("Ji/Change# T", parameter.Value.ToString());
}

[Fact]
public void TryTranslateFunctionSegmentTemplate_ReturnsODataFunctionSegment_WithReturnedEntitySet()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var endpoint = new Endpoint(c => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test");
var routeValues = new RouteValueDictionary();

var model = new EdmModel();
var entityType = new EdmEntityType("NS", "Entity");
entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
model.AddElement(entityType);
EdmFunction function = new EdmFunction("NS", "Function", new EdmEntityTypeReference(entityType, true), true, null, true);
model.AddElement(function);
var entityContainer = new EdmEntityContainer("NS", "Default");
var entitySet = entityContainer.AddEntitySet("EntitySet", entityType);
model.AddElement(entityContainer);
model.SetAnnotationValue(function, new ReturnedEntitySetAnnotation("EntitySet"));

var template = new FunctionSegmentTemplate(function, null);
var translateContext = new ODataTemplateTranslateContext(httpContext, endpoint, routeValues, model);

// Act
bool ok = template.TryTranslate(translateContext);

// Assert
Assert.True(ok);
var actual = Assert.Single(translateContext.Segments);
var functionSegment = Assert.IsType<OperationSegment>(actual);
Assert.Equal(functionSegment.EdmType, entityType);
Assert.Equal(functionSegment.EntitySet, entitySet);
}
}
}

0 comments on commit 3b0b4a2

Please sign in to comment.