Skip to content

Commit 1a29d40

Browse files
Andrew Omondibaywetandrueastman
committed
Fixes potential duplicate operation ids of navigation property paths with overloaded composable functions (#597)
* Fixes generation of navigation property op ids with overloaded composable fxns * Adds validation unit test * Updates release note * chore: adds string comparison Co-authored-by: Andrew Omondi <[email protected]> --------- Co-authored-by: Vincent Biret <[email protected]> Co-authored-by: Andrew Omondi <[email protected]>
1 parent 03812a5 commit 1a29d40

File tree

8 files changed

+560
-463
lines changed

8 files changed

+560
-463
lines changed

src/Microsoft.OpenApi.OData.Reader/Common/EdmModelHelper.cs

Lines changed: 488 additions & 456 deletions
Large diffs are not rendered by default.

src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- Updates PUT operation ID prefix from Update to Set #629
2727
- Ensures unique operation IDs for composable functions #630
2828
</PackageReleaseNotes>
29+
- Further fix for generating unique operation ids for navigation property paths with composable overloaded functions #596
2930
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
3031
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
3132
<OutputPath Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">..\..\bin\Debug\</OutputPath>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected override void SetBasicInfo(OpenApiOperation operation)
3939
if (Context.Settings.EnableOperationId)
4040
{
4141
string prefix = ComplexPropertySegment.Property.Type.IsCollection() ? "List" : "Get";
42-
operation.OperationId = EdmModelHelper.GenerateComplexPropertyPathOperationId(Path, prefix);
42+
operation.OperationId = EdmModelHelper.GenerateComplexPropertyPathOperationId(Path, Context, prefix);
4343
}
4444

4545
// Summary and Description

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ protected override void SetBasicInfo(OpenApiOperation operation)
4242
// OperationId
4343
if (Context.Settings.EnableOperationId)
4444
{
45-
operation.OperationId = EdmModelHelper.GenerateComplexPropertyPathOperationId(Path, "Set");
45+
operation.OperationId = EdmModelHelper.GenerateComplexPropertyPathOperationId(Path, Context, "Set");
4646
}
4747

4848
// Summary and Description

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,17 @@ protected override void SetBasicInfo(OpenApiOperation operation)
128128
}
129129
else if (SecondLastSegment is ODataNavigationPropertySegment)
130130
{
131-
var navPropOpId = string.Join(".", EdmModelHelper.RetrieveNavigationPropertyPathsOperationIdSegments(Path));
131+
var navPropOpId = string.Join(".", EdmModelHelper.RetrieveNavigationPropertyPathsOperationIdSegments(Path, Context));
132132
operation.OperationId = $"{navPropOpId}.GetCount-{Path.GetPathHash(Context.Settings)}";
133133
}
134134
else if (SecondLastSegment is ODataTypeCastSegment odataTypeCastSegment)
135135
{
136136
IEdmNamedElement targetStructuredType = odataTypeCastSegment.StructuredType as IEdmNamedElement;
137-
operation.OperationId = $"{EdmModelHelper.GenerateODataTypeCastPathOperationIdPrefix(Path, false)}.GetCount.As{Utils.UpperFirstChar(targetStructuredType.Name)}-{Path.GetPathHash(Context.Settings)}";
137+
operation.OperationId = $"{EdmModelHelper.GenerateODataTypeCastPathOperationIdPrefix(Path, Context, false)}.GetCount.As{Utils.UpperFirstChar(targetStructuredType.Name)}-{Path.GetPathHash(Context.Settings)}";
138138
}
139139
else if (SecondLastSegment is ODataComplexPropertySegment)
140140
{
141-
operation.OperationId = $"{EdmModelHelper.GenerateComplexPropertyPathOperationId(Path)}.GetCount-{Path.GetPathHash(Context.Settings)}";
141+
operation.OperationId = $"{EdmModelHelper.GenerateComplexPropertyPathOperationId(Path, Context)}.GetCount-{Path.GetPathHash(Context.Settings)}";
142142
}
143143
}
144144

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ protected override void SetExtensions(OpenApiOperation operation)
107107

108108
internal string GetOperationId(string prefix = null)
109109
{
110-
return EdmModelHelper.GenerateNavigationPropertyPathOperationId(Path, prefix);
110+
return EdmModelHelper.GenerateNavigationPropertyPathOperationId(Path, Context, prefix);
111111
}
112112

113113
/// <inheritdoc/>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ protected override void SetBasicInfo(OpenApiOperation operation)
188188

189189
// OperationId
190190
if (Context.Settings.EnableOperationId)
191-
operation.OperationId = EdmModelHelper.GenerateODataTypeCastPathOperationIdPrefix(Path) + $".As{Utils.UpperFirstChar(TargetSchemaElement.Name)}";
191+
operation.OperationId = EdmModelHelper.GenerateODataTypeCastPathOperationIdPrefix(Path, Context) + $".As{Utils.UpperFirstChar(TargetSchemaElement.Name)}";
192192

193193
base.SetBasicInfo(operation);
194194
}

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,70 @@ public void CreateNavigationGetOperationViaComposableFunctionReturnsCorrectOpera
131131
Assert.Contains(operation.Parameters, x => x.Name == "path");
132132
}
133133

134+
[Fact]
135+
public void CreateNavigationGetOperationViaOverloadedComposableFunctionReturnsCorrectOperation()
136+
{
137+
// Arrange
138+
IEdmModel model = EdmModelHelper.GraphBetaModel;
139+
ODataContext context = new(model, new OpenApiConvertSettings()
140+
{
141+
EnableOperationId = true
142+
});
143+
144+
IEdmEntitySet drives = model.EntityContainer.FindEntitySet("drives");
145+
IEdmEntityType drive = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "drive");
146+
IEdmNavigationProperty items = drive.DeclaredNavigationProperties().First(c => c.Name == "items");
147+
IEdmEntityType driveItem = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "driveItem");
148+
IEdmNavigationProperty workbook = driveItem.DeclaredNavigationProperties().First(c => c.Name == "workbook");
149+
IEdmEntityType workbookEntity = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "workbook");
150+
IEdmNavigationProperty worksheets = workbookEntity.DeclaredNavigationProperties().First(c => c.Name == "worksheets");
151+
IEdmEntityType workbookWorksheet = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "workbookWorksheet");
152+
IEdmOperation usedRangeWithParams = model.SchemaElements.OfType<IEdmOperation>().First(f => f.Name == "usedRange" && f.Parameters.Any(x => x.Name.Equals("valuesOnly")));
153+
IEdmOperation usedRange = model.SchemaElements.OfType<IEdmOperation>().First(f => f.Name == "usedRange" && f.Parameters.Count() == 1);
154+
IEdmEntityType workbookRange = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "workbookRange");
155+
IEdmNavigationProperty format = workbookRange.DeclaredNavigationProperties().First(c => c.Name == "format");
156+
157+
158+
ODataPath path1 = new(new ODataNavigationSourceSegment(drives),
159+
new ODataKeySegment(drive),
160+
new ODataNavigationPropertySegment(items),
161+
new ODataKeySegment(driveItem),
162+
new ODataNavigationPropertySegment(workbook),
163+
new ODataNavigationPropertySegment(worksheets),
164+
new ODataKeySegment(workbookWorksheet),
165+
new ODataOperationSegment(usedRangeWithParams),
166+
new ODataNavigationPropertySegment(format));
167+
168+
ODataPath path2 = new(new ODataNavigationSourceSegment(drives),
169+
new ODataKeySegment(drive),
170+
new ODataNavigationPropertySegment(items),
171+
new ODataKeySegment(driveItem),
172+
new ODataNavigationPropertySegment(workbook),
173+
new ODataNavigationPropertySegment(worksheets),
174+
new ODataKeySegment(workbookWorksheet),
175+
new ODataOperationSegment(usedRange),
176+
new ODataNavigationPropertySegment(format));
177+
178+
// Act
179+
var operation1 = _operationHandler.CreateOperation(context, path1);
180+
var operation2 = _operationHandler.CreateOperation(context, path2);
181+
182+
// Assert
183+
Assert.NotNull(operation1);
184+
Assert.NotNull(operation2);
185+
186+
Assert.Equal("drives.items.workbook.worksheets.usedRange.GetFormat-206d", operation1.OperationId);
187+
Assert.Equal("drives.items.workbook.worksheets.usedRange.GetFormat-ec2c", operation2.OperationId);
188+
189+
Assert.NotNull(operation1.Parameters);
190+
Assert.Equal(6, operation1.Parameters.Count);
191+
Assert.Contains(operation1.Parameters, x => x.Name == "valuesOnly");
192+
193+
Assert.NotNull(operation2.Parameters);
194+
Assert.Equal(5, operation2.Parameters.Count);
195+
Assert.DoesNotContain(operation2.Parameters, x => x.Name == "valuesOnly");
196+
}
197+
134198
[Theory]
135199
[InlineData(true)]
136200
[InlineData(false)]

0 commit comments

Comments
 (0)