Skip to content

Commit 93c5300

Browse files
authored
Use alternate keys in the generation of operation ids of operations and navigation property alternate paths (#489)
* Consider alternate keys in gen. operation ids of operations and nav. props paths * Add tests to validate alternate keys generated in operation ids * Update release note * Update integration test file * Update integration test file number of paths test
1 parent 5513183 commit 93c5300

File tree

7 files changed

+115
-9
lines changed

7 files changed

+115
-9
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,13 @@ internal static IList<string> RetrieveNavigationPropertyPathsOperationIdSegments
163163

164164
// For navigation property paths with odata type cast segments
165165
// the OData type cast segments identifiers will be used in the operation id
166+
// The same applies for navigation property paths with operation segments.
166167
IEnumerable<ODataSegment> segments = path.Segments.Skip(1)
167168
.Where(static s =>
168169
s is ODataNavigationPropertySegment ||
169170
s is ODataTypeCastSegment ||
170-
s is ODataOperationSegment);
171+
s is ODataOperationSegment ||
172+
s is ODataKeySegment);
171173
Utils.CheckArgumentNull(segments, nameof(segments));
172174

173175
string previousTypeCastSegmentId = null;
@@ -190,6 +192,18 @@ s is ODataTypeCastSegment ||
190192
// Navigation property generated via composable function
191193
items.Add(operationSegment.Identifier);
192194
}
195+
else if (segment is ODataKeySegment keySegment && keySegment.IsAlternateKey)
196+
{
197+
// We'll consider alternate keys in the operation id to eliminate potential duplicates with operation id of primary path
198+
if (segment == segments.Last())
199+
{
200+
items.Add("By" + string.Join("", keySegment.Identifier.Split(',').Select(static x => Utils.UpperFirstChar(x))));
201+
}
202+
else
203+
{
204+
items.Add(keySegment.Identifier);
205+
}
206+
}
193207
}
194208

195209
return items;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<TargetFrameworks>netstandard2.0</TargetFrameworks>
1616
<PackageId>Microsoft.OpenApi.OData</PackageId>
1717
<SignAssembly>true</SignAssembly>
18-
<Version>1.6.0-preview.6</Version>
18+
<Version>1.6.0-preview.7</Version>
1919
<Description>This package contains the codes you need to convert OData CSDL to Open API Document of Model.</Description>
2020
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
2121
<PackageTags>Microsoft OpenApi OData EDM</PackageTags>
@@ -28,6 +28,7 @@
2828
- Generates $expand query parameter for operations whose return type is a collection #481
2929
- Adds delete operation for non-contained navigation properties only if explicitly allowed via annotation #483
3030
- Appends parameters and fixes operation ids of navigation property paths generated via composable functions #486
31+
- Use alternate keys in the generation of operation ids of operations and navigation property alternate paths #488
3132
</PackageReleaseNotes>
3233
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
3334
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,30 @@ protected override void SetBasicInfo(OpenApiOperation operation)
7373
// in the operationId to avoid potential
7474
// duplicates in entity vs entityset functions/actions
7575

76-
List<string> identifiers = new();
76+
List<string> identifiers = [];
7777
foreach (ODataSegment segment in Path.Segments)
7878
{
79-
if (segment is not ODataKeySegment)
79+
if (segment is ODataKeySegment keySegment)
8080
{
81-
identifiers.Add(segment.Identifier);
81+
if (!keySegment.IsAlternateKey)
82+
{
83+
identifiers.Add(segment.EntityType.Name);
84+
continue;
85+
}
86+
87+
// We'll consider alternate keys in the operation id to eliminate potential duplicates with operation id of primary path
88+
if (segment == Path.Segments.Last())
89+
{
90+
identifiers.Add("By" + string.Join("", keySegment.Identifier.Split(',').Select(static x => Utils.UpperFirstChar(x))));
91+
}
92+
else
93+
{
94+
identifiers.Add(keySegment.Identifier);
95+
}
8296
}
8397
else
8498
{
85-
identifiers.Add(segment.EntityType.Name);
99+
identifiers.Add(segment.Identifier);
86100
}
87101
}
88102

test/Microsoft.OpenAPI.OData.Reader.Tests/Edm/ODataPathProviderTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void GetPathsForGraphBetaModelReturnsAllPaths()
5252

5353
// Assert
5454
Assert.NotNull(paths);
55-
Assert.Equal(16976, paths.Count());
55+
Assert.Equal(16980, paths.Count());
5656
AssertGraphBetaModelPaths(paths);
5757
}
5858

@@ -117,7 +117,7 @@ public void GetPathsForGraphBetaModelWithDerivedTypesConstraintReturnsAllPaths()
117117

118118
// Assert
119119
Assert.NotNull(paths);
120-
Assert.Equal(17627, paths.Count());
120+
Assert.Equal(17631, paths.Count());
121121
}
122122

123123
[Theory]

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
44
// ------------------------------------------------------------
55

6+
using System.Collections.Generic;
67
using System.Linq;
78
using System.Xml.Linq;
89
using Microsoft.OData.Edm;
@@ -479,5 +480,37 @@ public void CreateOperationForFunctionWithDateTimeParametersReturnsCorrectPathIt
479480
Assert.Equal("Usage: periodEnd={periodEnd}", operation.Parameters.First(x => x.Name == "periodEnd").Description);
480481

481482
}
483+
484+
[Fact]
485+
public void CreateFunctionOperationWithAlternateKeyReturnsCorrectOperationId()
486+
{
487+
// Arrange
488+
IEdmModel model = EdmModelHelper.GraphBetaModel;
489+
ODataContext context = new(model, new OpenApiConvertSettings()
490+
{
491+
EnableOperationId = true
492+
});
493+
494+
IEdmSingleton singleton = model.EntityContainer.FindSingleton("communications");
495+
IEdmEntityType entityType = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "cloudCommunications");
496+
IEdmNavigationProperty navProp = entityType.DeclaredNavigationProperties().First(c => c.Name == "onlineMeetings");
497+
IEdmOperation action = model.SchemaElements.OfType<IEdmOperation>().First(f => f.Name == "sendVirtualAppointmentReminderSms");
498+
IDictionary<string, string> keyMappings = new Dictionary<string, string> { { "joinWebUrl", "joinWebUrl" } };
499+
500+
ODataPath path = new(new ODataNavigationSourceSegment(singleton),
501+
new ODataNavigationPropertySegment(navProp),
502+
new ODataKeySegment(entityType, keyMappings)
503+
{
504+
IsAlternateKey = true
505+
},
506+
new ODataOperationSegment(action));
507+
508+
// Act
509+
var operation = _operationHandler.CreateOperation(context, path);
510+
511+
// Assert
512+
Assert.NotNull(operation);
513+
Assert.Equal("communications.onlineMeetings.joinWebUrl.sendVirtualAppointmentReminderSms", operation.OperationId);
514+
}
482515
}
483516
}

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
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
// ------------------------------------------------------------
55

6+
using System.Collections.Generic;
67
using System.Linq;
78
using Microsoft.OData.Edm;
89
using Microsoft.OpenApi.Extensions;
@@ -242,5 +243,35 @@ public void CreateNavigationGetOperationReturnsSecurityForReadRestrictions(bool
242243
Assert.Empty(operation.Security);
243244
}
244245
}
246+
247+
[Fact]
248+
public void CreateNavigationGetOperationWithAlternateKeyReturnsCorrectOperationId()
249+
{
250+
// Arrange
251+
IEdmModel model = EdmModelHelper.GraphBetaModel;
252+
ODataContext context = new(model, new OpenApiConvertSettings()
253+
{
254+
EnableOperationId = true
255+
});
256+
257+
IEdmSingleton singleton = model.EntityContainer.FindSingleton("communications");
258+
IEdmEntityType entityType = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "cloudCommunications");
259+
IEdmNavigationProperty navProp = entityType.DeclaredNavigationProperties().First(c => c.Name == "onlineMeetings");
260+
IDictionary<string, string> keyMappings = new Dictionary<string, string> { { "joinWebUrl", "joinWebUrl" } };
261+
262+
ODataPath path = new(new ODataNavigationSourceSegment(singleton),
263+
new ODataNavigationPropertySegment(navProp),
264+
new ODataKeySegment(entityType, keyMappings)
265+
{
266+
IsAlternateKey = true
267+
});
268+
269+
// Act
270+
var operation = _operationHandler.CreateOperation(context, path);
271+
272+
// Assert
273+
Assert.NotNull(operation);
274+
Assert.Equal("communications.onlineMeetings.GetByJoinWebUrl", operation.OperationId);
275+
}
245276
}
246277
}

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Graph.Beta.OData.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102459,6 +102459,11 @@
102459102459
<Parameter Name="bindingParameter" Type="Collection(graph.deletedTeam)" />
102460102460
<ReturnType Type="Collection(graph.chatMessage)" />
102461102461
</Function>
102462+
<Action Name="sendVirtualAppointmentReminderSms" IsBound="true">
102463+
<Parameter Name="bindingParameter" Type="graph.onlineMeeting" />
102464+
<Parameter Name="remindBeforeTimeInMinutesType" Type="graph.remindBeforeTimeInMinutesType" />
102465+
<Parameter Name="attendees" Type="Collection(graph.attendeeNotificationInfo)" />
102466+
</Action>
102462102467
<Action Name="complete" IsBound="true">
102463102468
<Parameter Name="bindingParameter" Type="graph.impactedResource" Nullable="false" />
102464102469
<ReturnType Type="graph.impactedResource" />
@@ -132565,6 +132570,10 @@
132565132570
</Annotations>
132566132571
</Schema>
132567132572
<Schema Namespace="microsoft.graph.ediscovery" xmlns="http://docs.oasis-open.org/odata/ns/edm">
132573+
<EnumType Name="remindBeforeTimeInMinutesType">
132574+
<Member Name="mins15" Value="0" />
132575+
<Member Name="unknownFutureValue" Value="100" />
132576+
</EnumType>
132568132577
<EnumType Name="additionalDataOptions" IsFlags="true">
132569132578
<Member Name="allVersions" Value="1" />
132570132579
<Member Name="linkedFiles" Value="2" />
@@ -133020,6 +133029,10 @@
133020133029
</Property>
133021133030
</EntityType>
133022133031
<EntityType Name="tagOperation" BaseType="microsoft.graph.ediscovery.caseOperation" />
133032+
<ComplexType Name="attendeeNotificationInfo">
133033+
<Property Name="phoneNumber" Type="Edm.String" />
133034+
<Property Name="timeZone" Type="Edm.String" />
133035+
</ComplexType>
133023133036
<ComplexType Name="ocrSettings">
133024133037
<Property Name="isEnabled" Type="Edm.Boolean">
133025133038
<Annotation Term="Org.OData.Core.V1.Description" String="Indicates whether or not OCR is enabled for the case." />

0 commit comments

Comments
 (0)