Skip to content

Commit 4136c6a

Browse files
irvinesundaybaywet
authored andcommitted
Creates unique operation ids for paths with composable overloaded functions (#590)
* Update unit function operation test * Account for overloaded functions in any segment * Update release note Signed-off-by: Vincent Biret <[email protected]>
1 parent bbce0a8 commit 4136c6a

File tree

2 files changed

+99
-37
lines changed

2 files changed

+99
-37
lines changed

src/Microsoft.OpenApi.OData.Reader/Operation/EdmOperationOperationHandler.cs

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal abstract class EdmOperationOperationHandler : OperationHandler
4545

4646
/// <inheritdoc/>
4747
protected override void Initialize(ODataContext context, ODataPath path)
48-
{
48+
{
4949
base.Initialize(context, path);
5050

5151
// It's bound operation, the first segment must be the navigaiton source.
@@ -57,9 +57,9 @@ protected override void Initialize(ODataContext context, ODataPath path)
5757

5858
HasTypeCast = path.Segments.Any(s => s is ODataTypeCastSegment);
5959

60-
_operationRestriction = Context.Model.GetRecord<OperationRestrictionsType>(TargetPath, CapabilitiesConstants.OperationRestrictions);
61-
var operationRestrictions = Context.Model.GetRecord<OperationRestrictionsType>(EdmOperation, CapabilitiesConstants.OperationRestrictions);
62-
_operationRestriction?.MergePropertiesIfNull(operationRestrictions);
60+
_operationRestriction = Context.Model.GetRecord<OperationRestrictionsType>(TargetPath, CapabilitiesConstants.OperationRestrictions);
61+
var operationRestrictions = Context.Model.GetRecord<OperationRestrictionsType>(EdmOperation, CapabilitiesConstants.OperationRestrictions);
62+
_operationRestriction?.MergePropertiesIfNull(operationRestrictions);
6363
_operationRestriction ??= operationRestrictions;
6464
}
6565

@@ -81,6 +81,7 @@ protected override void SetBasicInfo(OpenApiOperation operation)
8181
// duplicates in entity vs entityset functions/actions
8282

8383
List<string> identifiers = new();
84+
string pathHash = string.Empty;
8485
foreach (ODataSegment segment in Path.Segments)
8586
{
8687
if (segment is ODataKeySegment keySegment)
@@ -89,41 +90,43 @@ protected override void SetBasicInfo(OpenApiOperation operation)
8990
{
9091
identifiers.Add(segment.EntityType.Name);
9192
continue;
92-
}
93-
94-
// We'll consider alternate keys in the operation id to eliminate potential duplicates with operation id of primary path
95-
if (segment == Path.Segments.Last())
96-
{
97-
identifiers.Add("By" + string.Join("", keySegment.Identifier.Split(',').Select(static x => Utils.UpperFirstChar(x))));
98-
}
99-
else
100-
{
101-
identifiers.Add(keySegment.Identifier);
102-
}
93+
}
94+
95+
// We'll consider alternate keys in the operation id to eliminate potential duplicates with operation id of primary path
96+
if (segment == Path.Segments.Last())
97+
{
98+
identifiers.Add("By" + string.Join("", keySegment.Identifier.Split(',').Select(static x => Utils.UpperFirstChar(x))));
99+
}
100+
else
101+
{
102+
identifiers.Add(keySegment.Identifier);
103+
}
104+
}
105+
else if (segment is ODataOperationSegment opSegment)
106+
{
107+
if (opSegment.Operation is IEdmFunction function && Context.Model.IsOperationOverload(function))
108+
{
109+
// Hash the segment to avoid duplicate operationIds
110+
pathHash = segment.GetPathHash(Context.Settings);
111+
}
112+
113+
identifiers.Add(segment.Identifier);
103114
}
104115
else
105116
{
106-
identifiers.Add(segment.Identifier);
117+
identifiers.Add(segment.Identifier);
107118
}
108119
}
109120

110121
string operationId = string.Join(".", identifiers);
111122

112-
if (EdmOperation.IsAction())
123+
if (!string.IsNullOrEmpty(pathHash))
113124
{
114-
operation.OperationId = operationId;
125+
operation.OperationId = operationId + "-" + pathHash;
115126
}
116127
else
117128
{
118-
if (Path.LastSegment is ODataOperationSegment operationSegment &&
119-
Context.Model.IsOperationOverload(operationSegment.Operation))
120-
{
121-
operation.OperationId = operationId + "-" + Path.LastSegment.GetPathHash(Context.Settings);
122-
}
123-
else
124-
{
125-
operation.OperationId = operationId;
126-
}
129+
operation.OperationId = operationId;
127130
}
128131
}
129132

@@ -269,15 +272,15 @@ protected override void SetExternalDocs(OpenApiOperation operation)
269272
if (Context.Settings.ShowExternalDocs)
270273
{
271274
var externalDocs = Context.Model.GetLinkRecord(TargetPath, CustomLinkRel) ??
272-
Context.Model.GetLinkRecord(EdmOperation, CustomLinkRel);
273-
274-
if (externalDocs != null)
275-
{
276-
operation.ExternalDocs = new OpenApiExternalDocs()
277-
{
278-
Description = CoreConstants.ExternalDocsDescription,
279-
Url = externalDocs.Href
280-
};
275+
Context.Model.GetLinkRecord(EdmOperation, CustomLinkRel);
276+
277+
if (externalDocs != null)
278+
{
279+
operation.ExternalDocs = new OpenApiExternalDocs()
280+
{
281+
Description = CoreConstants.ExternalDocsDescription,
282+
Url = externalDocs.Href
283+
};
281284
}
282285
}
283286
}

test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ------------------------------------------------------------
1+
// ------------------------------------------------------------
22
// Copyright (c) Microsoft Corporation. All rights reserved.
33
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
44
// ------------------------------------------------------------
@@ -299,6 +299,65 @@ public void CreateOperationForOverloadEdmFunctionReturnsCorrectOperationId(bool
299299
}
300300
}
301301

302+
[Theory]
303+
[InlineData(true)]
304+
[InlineData(false)]
305+
public void CreateOperationForComposableOverloadEdmFunctionReturnsCorrectOperationId(bool enableOperationId)
306+
{
307+
// Arrange
308+
EdmModel model = new();
309+
EdmEntityType customer = new("NS", "Customer");
310+
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
311+
model.AddElement(customer);
312+
313+
EdmFunction function = new("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false);
314+
function.AddParameter("entity", new EdmEntityTypeReference(customer, false));
315+
function.AddParameter("param", EdmCoreModel.Instance.GetString(false));
316+
model.AddElement(function);
317+
318+
function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false);
319+
function.AddParameter("entity", new EdmEntityTypeReference(customer, false));
320+
function.AddParameter("param", EdmCoreModel.Instance.GetString(false));
321+
function.AddParameter("param2", EdmCoreModel.Instance.GetString(false));
322+
model.AddElement(function);
323+
324+
EdmFunction function2 = new("NS", "MyFunction2", EdmCoreModel.Instance.GetString(false), true, null, false);
325+
function2.AddParameter("entity2", new EdmEntityTypeReference(customer, false));
326+
function2.AddParameter("param3", EdmCoreModel.Instance.GetString(false));
327+
model.AddElement(function2);
328+
329+
EdmEntityContainer container = new("NS", "Default");
330+
EdmEntitySet customers = new(container, "Customers", customer);
331+
model.AddElement(container);
332+
333+
OpenApiConvertSettings settings = new OpenApiConvertSettings
334+
{
335+
EnableOperationId = enableOperationId,
336+
AddSingleQuotesForStringParameters = true,
337+
};
338+
ODataContext context = new(model, settings);
339+
340+
ODataPath path = new(new ODataNavigationSourceSegment(customers),
341+
new ODataKeySegment(customer),
342+
new ODataOperationSegment(function),
343+
new ODataOperationSegment(function2));
344+
345+
// Act
346+
var operation = _operationHandler.CreateOperation(context, path);
347+
348+
// Assert
349+
Assert.NotNull(operation);
350+
351+
if (enableOperationId)
352+
{
353+
Assert.Equal("Customers.Customer.MyFunction.MyFunction2-df74", operation.OperationId);
354+
}
355+
else
356+
{
357+
Assert.Null(operation.OperationId);
358+
}
359+
}
360+
302361
[Theory]
303362
[InlineData(true)]
304363
[InlineData(false)]

0 commit comments

Comments
 (0)