Skip to content

Commit 1d16044

Browse files
authored
Ensures only alternate key parameter matching key segment identifier is included in path parameters (#455)
* Ensures only alternate key parameter matching segment identifier is included * Update tests * Updates release notes * Remove unnecessary whitespace
1 parent 9933af1 commit 1d16044

File tree

3 files changed

+43
-17
lines changed

3 files changed

+43
-17
lines changed

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiParameterGenerator.cs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using Microsoft.OData.Edm.Vocabularies;
1313
using Microsoft.OpenApi.OData.Edm;
1414
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
15+
using System.Diagnostics;
16+
using System;
1517

1618
namespace Microsoft.OpenApi.OData.Generator
1719
{
@@ -151,19 +153,20 @@ public static IList<OpenApiParameter> CreateParameters(this ODataContext context
151153
/// <param name="context">The OData context.</param>
152154
/// <param name="keySegment">The key segment.</param>
153155
/// <param name="parameterNameMapping">The parameter name mapping.</param>
154-
/// <returns>The created the list of <see cref="OpenApiParameter"/>.</returns>
156+
/// <returns>The created list of <see cref="OpenApiParameter"/>.</returns>
155157
public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext context, ODataKeySegment keySegment,
156158
IDictionary<string, string> parameterNameMapping = null)
157159
{
158160
Utils.CheckArgumentNull(context, nameof(context));
159161
Utils.CheckArgumentNull(keySegment, nameof(keySegment));
162+
163+
if (keySegment.IsAlternateKey)
164+
return CreateAlternateKeyParameters(context, keySegment);
160165

161166
IEdmEntityType entityType = keySegment.EntityType;
162-
if (keySegment.IsAlternateKey)
163-
return CreateAlternateKeyParameters(context, entityType);
167+
IList<IEdmStructuralProperty> keys = entityType.Key().ToList();
164168

165169
List<OpenApiParameter> parameters = new();
166-
IList<IEdmStructuralProperty> keys = entityType.Key().ToList();
167170
if (keys.Count() == 1)
168171
{
169172
string keyName = keys.First().Name;
@@ -177,7 +180,7 @@ public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext cont
177180

178181
OpenApiParameter parameter = new OpenApiParameter
179182
{
180-
Name = parameterNameMapping == null ? keyName: parameterNameMapping[keyName],
183+
Name = parameterNameMapping == null ? keyName : parameterNameMapping[keyName],
181184
In = ParameterLocation.Path,
182185
Required = true,
183186
Description = $"The unique identifier of {entityType.Name}",
@@ -195,7 +198,7 @@ public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext cont
195198
OpenApiParameter parameter = new OpenApiParameter
196199
{
197200
Name = parameterNameMapping == null ?
198-
keyProperty.Name:
201+
keyProperty.Name :
199202
parameterNameMapping[keyProperty.Name],// By design: not prefix with type name if enable type name prefix
200203
In = ParameterLocation.Path,
201204
Required = true,
@@ -216,15 +219,28 @@ public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext cont
216219
return parameters;
217220
}
218221

219-
private static IList<OpenApiParameter> CreateAlternateKeyParameters(ODataContext context, IEdmEntityType entityType)
222+
223+
/// <summary>
224+
/// Create alternate key parameters for the <see cref="ODataKeySegment"/>.
225+
/// </summary>
226+
/// <param name="context">The OData context.</param>
227+
/// <param name="keySegment">The key segment.</param>
228+
/// <returns>A list of <see cref="OpenApiParameter"/> of alternate key parameters.</returns>
229+
private static IList<OpenApiParameter> CreateAlternateKeyParameters(ODataContext context, ODataSegment keySegment)
220230
{
231+
Debug.Assert(keySegment.Kind == ODataSegmentKind.Key);
232+
221233
IList<OpenApiParameter> parameters = new List<OpenApiParameter>();
222-
IEnumerable<IDictionary<string, IEdmProperty>> alternateKeys = context.Model.GetAlternateKeysAnnotation(entityType);
234+
IEdmEntityType entityType = keySegment.EntityType;
235+
IEnumerable<IDictionary<string, IEdmProperty>> alternateKeys = context.Model.GetAlternateKeysAnnotation(entityType);
236+
223237
foreach (var alternateKey in alternateKeys)
224238
{
225239
if (alternateKey.Count() == 1)
226240
{
227-
parameters.Add(
241+
if (keySegment.Identifier.Equals(alternateKey.First().Key, StringComparison.OrdinalIgnoreCase))
242+
{
243+
parameters.Add(
228244
new OpenApiParameter
229245
{
230246
Name = alternateKey.First().Key,
@@ -234,12 +250,15 @@ private static IList<OpenApiParameter> CreateAlternateKeyParameters(ODataContext
234250
Required = true
235251
}
236252
);
253+
}
237254
}
238255
else
239256
{
240257
foreach (var compositekey in alternateKey)
241258
{
242-
parameters.Add(
259+
if (keySegment.Identifier.Contains(compositekey.Key))
260+
{
261+
parameters.Add(
243262
new OpenApiParameter
244263
{
245264
Name = compositekey.Key,
@@ -248,7 +267,8 @@ private static IList<OpenApiParameter> CreateAlternateKeyParameters(ODataContext
248267
Schema = context.CreateEdmTypeSchema(compositekey.Value.Type),
249268
Required = true
250269
}
251-
);
270+
);
271+
}
252272
}
253273
}
254274
}

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.5.0-preview11</Version>
18+
<Version>1.5.0-preview12</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>
@@ -32,6 +32,7 @@
3232
- Updates operationIds of navigation property paths with OData type cast segments #442
3333
- Generate navigation property paths defined in nested complex properties #446
3434
- Adds support for alternate keys to collection navigation properties #410
35+
- Ensures only alternate key parameter matching key segment identifier is included in path parameters #414
3536
</PackageReleaseNotes>
3637
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
3738
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>

test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiParameterGeneratorTests.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,15 @@ public void CreateKeyParametersForAlternateKeyWithSinglePropertyWorks()
283283
EdmEntityType customer = new("NS", "Customer");
284284
customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String));
285285

286-
IEdmProperty alternateId = customer.AddStructuralProperty("AlternateId", EdmPrimitiveTypeKind.String);
287-
model.AddAlternateKeyAnnotation(customer, new Dictionary<string, IEdmProperty> { { "AltId", alternateId } });
286+
IEdmProperty alternateId1 = customer.AddStructuralProperty("AlternateId1", EdmPrimitiveTypeKind.String);
287+
IEdmProperty alternateId2 = customer.AddStructuralProperty("AlternateId2", EdmPrimitiveTypeKind.String);
288+
model.AddAlternateKeyAnnotation(customer, new Dictionary<string, IEdmProperty> { { "AltId1", alternateId1 } });
289+
model.AddAlternateKeyAnnotation(customer, new Dictionary<string, IEdmProperty> { { "AltId2", alternateId2 } });
290+
IDictionary<string, string> keyMappings = new Dictionary<string, string> { { "AltId1", "AltId1" } };
288291

289292
model.AddElement(customer);
290293
ODataContext context = new(model);
291-
ODataKeySegment keySegment = new(customer)
294+
ODataKeySegment keySegment = new(customer, keyMappings)
292295
{
293296
IsAlternateKey = true
294297
};
@@ -302,7 +305,7 @@ public void CreateKeyParametersForAlternateKeyWithSinglePropertyWorks()
302305
Assert.Single(parameters);
303306
string json = altParameter.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
304307
Assert.Equal(@"{
305-
""name"": ""AltId"",
308+
""name"": ""AltId1"",
306309
""in"": ""path"",
307310
""description"": ""Alternate key of Customer"",
308311
""required"": true,
@@ -330,9 +333,11 @@ public void CreateKeyParametersForAlternateKeyWithMultiplePropertiesWorks()
330333
{ "AltId2", alternateId2 }
331334
});
332335

336+
IDictionary<string, string> keyMappings = new Dictionary<string, string> { { "AltId1", "AltId1" }, { "AltId2", "AltId2" } };
337+
333338
model.AddElement(customer);
334339
ODataContext context = new(model);
335-
ODataKeySegment keySegment = new(customer)
340+
ODataKeySegment keySegment = new(customer, keyMappings)
336341
{
337342
IsAlternateKey = true
338343
};

0 commit comments

Comments
 (0)