diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs index 55ad88b2..ad6217e9 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs @@ -81,6 +81,7 @@ protected override void SetBasicInfo(OpenApiOperation operation) // duplicates in entity vs entityset functions/actions List identifiers = new(); + string pathHash = string.Empty; foreach (ODataSegment segment in Path.Segments) { if (segment is ODataKeySegment keySegment) @@ -101,6 +102,16 @@ protected override void SetBasicInfo(OpenApiOperation operation) identifiers.Add(keySegment.Identifier); } } + else if (segment is ODataOperationSegment opSegment) + { + if (opSegment.Operation is IEdmFunction function && Context.Model.IsOperationOverload(function)) + { + // Hash the segment to avoid duplicate operationIds + pathHash = segment.GetPathHash(Context.Settings); + } + + identifiers.Add(segment.Identifier); + } else { identifiers.Add(segment.Identifier); @@ -109,21 +120,13 @@ protected override void SetBasicInfo(OpenApiOperation operation) string operationId = string.Join(".", identifiers); - if (EdmOperation.IsAction()) + if (!string.IsNullOrEmpty(pathHash)) { - operation.OperationId = operationId; + operation.OperationId = operationId + "-" + pathHash; } else { - if (Path.LastSegment is ODataOperationSegment operationSegment && - Context.Model.IsOperationOverload(operationSegment.Operation)) - { - operation.OperationId = operationId + "-" + Path.LastSegment.GetPathHash(Context.Settings); - } - else - { - operation.OperationId = operationId; - } + operation.OperationId = operationId; } } diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs index 49322066..1a9c9a2c 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs @@ -299,6 +299,65 @@ public void CreateOperationForOverloadEdmFunctionReturnsCorrectOperationId(bool } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CreateOperationForComposableOverloadEdmFunctionReturnsCorrectOperationId(bool enableOperationId) + { + // Arrange + EdmModel model = new(); + EdmEntityType customer = new("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32)); + model.AddElement(customer); + + EdmFunction function = new("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false); + function.AddParameter("entity", new EdmEntityTypeReference(customer, false)); + function.AddParameter("param", EdmCoreModel.Instance.GetString(false)); + model.AddElement(function); + + function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false); + function.AddParameter("entity", new EdmEntityTypeReference(customer, false)); + function.AddParameter("param", EdmCoreModel.Instance.GetString(false)); + function.AddParameter("param2", EdmCoreModel.Instance.GetString(false)); + model.AddElement(function); + + EdmFunction function2 = new("NS", "MyFunction2", EdmCoreModel.Instance.GetString(false), true, null, false); + function2.AddParameter("entity2", new EdmEntityTypeReference(customer, false)); + function2.AddParameter("param3", EdmCoreModel.Instance.GetString(false)); + model.AddElement(function2); + + EdmEntityContainer container = new("NS", "Default"); + EdmEntitySet customers = new(container, "Customers", customer); + model.AddElement(container); + + OpenApiConvertSettings settings = new OpenApiConvertSettings + { + EnableOperationId = enableOperationId, + AddSingleQuotesForStringParameters = true, + }; + ODataContext context = new(model, settings); + + ODataPath path = new(new ODataNavigationSourceSegment(customers), + new ODataKeySegment(customer), + new ODataOperationSegment(function), + new ODataOperationSegment(function2)); + + // Act + var operation = _operationHandler.CreateOperation(context, path); + + // Assert + Assert.NotNull(operation); + + if (enableOperationId) + { + Assert.Equal("Customers.Customer.MyFunction.MyFunction2-df74", operation.OperationId); + } + else + { + Assert.Null(operation.OperationId); + } + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -512,5 +571,7 @@ public void CreateFunctionOperationWithAlternateKeyReturnsCorrectOperationId() Assert.NotNull(operation); Assert.Equal("communications.onlineMeetings.joinWebUrl.sendVirtualAppointmentReminderSms", operation.OperationId); } + + } }