Skip to content
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
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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think I understand why you did away with the GetTargetNavigationSource method. and called the path.GetNavigationSource directly. Does it mean the path.LastSegment is always null at this point?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The GetTargetNavigationSource method defined in this class was a workaround to the problem that we're fixing from the TryTranslate methods of ActionSegmentTemplate and FunctionSegmentTemplate. It's not necessary to return it after the fix that we applied

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

aah I see

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;
Comment thread
gathogojr marked this conversation as resolved.
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);
}
}
}