Skip to content

Commit

Permalink
Fixes #2810, make action/function link works using lower camel case
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhg committed Oct 26, 2023
1 parent 72db0c7 commit 1c61a5f
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
24 changes: 20 additions & 4 deletions src/Microsoft.AspNet.OData.Shared/Builder/LinkGenerationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ internal static Uri GenerateActionLink(this ResourceSetContext feedContext, stri
isNested: false);
Contract.Assert(elementType != null);

IEdmTypeReference typeReference = model.FindDeclaredType(elementType).ToEdmTypeReference(true);
IEdmTypeReference typeReference = model.FindBindingType(elementType).ToEdmTypeReference(true);
IEdmTypeReference collection = new EdmCollectionTypeReference(new EdmCollectionType(typeReference));

IEdmOperation operation = model.FindDeclaredOperations(actionName).First();
Expand Down Expand Up @@ -278,7 +278,7 @@ internal static Uri GenerateFunctionLink(this ResourceSetContext feedContext, st
isNested: false);
Contract.Assert(elementType != null);

IEdmTypeReference typeReference = model.FindDeclaredType(elementType).ToEdmTypeReference(true);
IEdmTypeReference typeReference = model.FindBindingType(elementType).ToEdmTypeReference(true);
IEdmTypeReference collection = new EdmCollectionTypeReference(new EdmCollectionType(typeReference));
IEdmOperation operation = model.FindDeclaredOperations(functionName).First();
return feedContext.GenerateFunctionLink(collection, operation, parameterNames);
Expand Down Expand Up @@ -351,7 +351,7 @@ internal static Uri GenerateActionLink(this ResourceContext resourceContext, str
}

IEdmModel model = resourceContext.EdmModel;
IEdmTypeReference typeReference = model.FindDeclaredType(bindingParameterType).ToEdmTypeReference(true);
IEdmTypeReference typeReference = model.FindBindingType(bindingParameterType).ToEdmTypeReference(true);
IEdmOperation operation = model.FindDeclaredOperations(actionName).First();
return resourceContext.GenerateActionLink(typeReference, operation);
}
Expand Down Expand Up @@ -422,7 +422,7 @@ internal static Uri GenerateFunctionLink(this ResourceContext resourceContext, s
}

IEdmModel model = resourceContext.EdmModel;
IEdmTypeReference typeReference = model.FindDeclaredType(bindingParameterType).ToEdmTypeReference(true);
IEdmTypeReference typeReference = model.FindBindingType(bindingParameterType).ToEdmTypeReference(true);
IEdmOperation operation = model.FindDeclaredOperations(functionName).First();
return resourceContext.GenerateFunctionLink(typeReference, operation, parameterNames);
}
Expand Down Expand Up @@ -661,5 +661,21 @@ private static IList<ODataPathSegment> GenerateContainmentODataPathSegments(this

return pathSegments;
}

private static IEdmSchemaType FindBindingType(this IEdmModel model, string bindingParameterType)
{
IEdmSchemaType type = model.FindDeclaredType(bindingParameterType);
if (type != null)
{
return type;
}

// When the customer uses 'OnModelCreating' to change the type name case,
// the bindingParameterType could mis-match the type defined in the model.
// So, let's loose the logic to use 'ignore case' lookup.
return model.SchemaElements.OfType<IEdmSchemaType>()
.Where(e => string.Equals(bindingParameterType, e.FullName(), StringComparison.OrdinalIgnoreCase))
.SingleOrDefault();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,57 @@ public void Convention_GeneratesUri_ForActionBoundToEntity()
Assert.Equal("http://localhost:123/odata/Customers(109)/Default.MyAction", link.AbsoluteUri);
}

[Fact]
public void Convention_GeneratesUri_ForActionBoundToEntity_UsingCamelCase()
{
// Arrange
ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create();
builder.OnModelCreating += ApplyLowerCamelCase;
builder.EntitySet<Customer>("Customers");
var action = builder.EntityType<Customer>().Action("simpleAction");
action.Parameter<string>("param");
IEdmModel model = builder.GetEdmModel();

// Act
var configuration = RoutingConfigurationFactory.Create();
configuration.MapODataServiceRoute("odata", "odata", model);

var request = RequestFactory.Create(HttpMethod.Get, "http://localhost:123", configuration, "odata");

IEdmEntitySet customers = model.EntityContainer.FindEntitySet("Customers");
var edmType = model.SchemaElements.OfType<IEdmEntityType>().FirstOrDefault(e => e.Name == "Customer");
Assert.Null(edmType); // guard, since it's lower camel case, it should be null.
edmType = model.SchemaElements.OfType<IEdmEntityType>().FirstOrDefault(e => e.Name == "customer");
Assert.NotNull(edmType);

var serializerContext = ODataSerializerContextFactory.Create(model, customers, request);
var resourceContext = new ResourceContext(serializerContext, edmType.AsReference(), new Customer { Id = 109 });

// Assert
var edmAction = model.SchemaElements.OfType<IEdmAction>().First(f => f.Name == "simpleAction");
Assert.NotNull(edmAction);

OperationLinkBuilder actionLinkBuilder = model.GetOperationLinkBuilder(edmAction);
Uri link = actionLinkBuilder.BuildLink(resourceContext);

Assert.Equal("http://localhost:123/odata/Customers(109)/Default.simpleAction", link.AbsoluteUri);
}

internal static void ApplyLowerCamelCase(ODataConventionModelBuilder builder)
{
LowerCamelCaser lowerCamelCaser = new LowerCamelCaser();

// handle structural types & their properties
foreach (StructuralTypeConfiguration type in builder.StructuralTypes)
{
type.Name = lowerCamelCaser.ToLowerCamelCase(type.Name);
foreach (PropertyConfiguration property in type.Properties)
{
property.Name = lowerCamelCaser.ToLowerCamelCase(property.Name);
}
}
}

[Fact]
public void Apply_WorksFor_ActionBoundToCollectionOfEntity()
{
Expand Down

0 comments on commit 1c61a5f

Please sign in to comment.