Skip to content

Commit 1ca0f1b

Browse files
authored
Uses Referenceable annotation to control generation of $ref paths (#152)
* Fetch custom XML element attribute using the DirectValueAnnotationsManager * Make the custom XML attribute name configurable via settings * Update tests to validate setting and reading custom XML attributes * Use Referenceable annotation to generate $ref paths * Update integration test files * Update tests to validate Referenceable annotation * Remove unused param and arg
1 parent 7b0f3cd commit 1ca0f1b

File tree

14 files changed

+72911
-25354
lines changed

14 files changed

+72911
-25354
lines changed

src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,9 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr
366366
// ~/entityset/{key}/single-valued-Nav/subtype
367367
CreateTypeCastPaths(currentPath, convertSettings, navigationProperty.DeclaringType, navigationProperty, targetsMany);
368368

369-
if (!navigationProperty.ContainsTarget)
369+
if (navigation?.Referenceable == true)
370370
{
371-
// Non-Contained
371+
// Referenceable navigation properties
372372
// Single-Valued: ~/entityset/{key}/single-valued-Nav/$ref
373373
// Collection-valued: ~/entityset/{key}/collection-valued-Nav/$ref?$id='{navKey}'
374374
CreateRefPath(currentPath);
@@ -378,7 +378,7 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr
378378
// Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
379379
currentPath.Push(new ODataKeySegment(navEntityType));
380380
CreateRefPath(currentPath);
381-
381+
382382
CreateTypeCastPaths(currentPath, convertSettings, navigationProperty.DeclaringType, navigationProperty, false); // ~/entityset/{key}/collection-valued-Nav/{id}/subtype
383383
}
384384

src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableUnqualifiedCall.get -> bool
134134
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableUnqualifiedCall.set -> void
135135
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableUriEscapeFunctionCall.get -> bool
136136
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableUriEscapeFunctionCall.set -> void
137+
Microsoft.OpenApi.OData.OpenApiConvertSettings.ExpandNavigationPropertyAttributeName.get -> string
138+
Microsoft.OpenApi.OData.OpenApiConvertSettings.ExpandNavigationPropertyAttributeName.set -> void
137139
Microsoft.OpenApi.OData.OpenApiConvertSettings.IEEE754Compatible.get -> bool
138140
Microsoft.OpenApi.OData.OpenApiConvertSettings.IEEE754Compatible.set -> void
139141
Microsoft.OpenApi.OData.OpenApiConvertSettings.OpenApiConvertSettings() -> void

src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/NavigationRestrictionsType.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ internal class NavigationPropertyRestriction : IRecord
121121
/// Restrictions for retrieving entities.
122122
/// </summary>
123123
public ReadRestrictionsType ReadRestrictions { get; set; }
124-
124+
125125
/// <summary>
126126
/// Init the <see cref="NavigationPropertyRestriction"/>.
127127
/// </summary>
@@ -199,6 +199,11 @@ internal class NavigationRestrictionsType : IRecord
199199
/// </summary>
200200
public IList<NavigationPropertyRestriction> RestrictedProperties { get; private set; }
201201

202+
/// <summary>
203+
/// Gets the navigation property referenceable value.
204+
/// </summary>
205+
public bool? Referenceable { get; set; }
206+
202207
/// <summary>
203208
/// Gets a value indicating the target is navigable or not.
204209
/// </summary>
@@ -230,6 +235,9 @@ public void Initialize(IEdmRecordExpression record)
230235

231236
// RestrictedProperties
232237
RestrictedProperties = record.GetCollection<NavigationPropertyRestriction>("RestrictedProperties");
238+
239+
// Referenceable
240+
Referenceable = record.GetBoolean("Referenceable");
233241
}
234242
}
235243
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ public void FindOperationsReturnsCorrectCollectionOrOperations()
3434
var operations = provider.FindOperations(entitySet.EntityType(), false);
3535

3636
// Assert
37-
Assert.Equal(15, operations.Count());
37+
Assert.Equal(29, operations.Count());
3838

3939
// Act
4040
entitySet = model.EntityContainer.FindEntitySet("directoryObjects");
4141

4242
operations = provider.FindOperations(entitySet.EntityType(), false);
4343

4444
// Assert
45-
Assert.Equal(30, operations.Count());
45+
Assert.Equal(57, operations.Count());
4646
}
4747
}
4848
}

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

Lines changed: 61 additions & 13 deletions
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
// --------------------------------------------------------------
@@ -48,7 +48,7 @@ public void GetPathsForGraphBetaModelReturnsAllPaths()
4848

4949
// Assert
5050
Assert.NotNull(paths);
51-
Assert.Equal(50085, paths.Count());
51+
Assert.Equal(28227, paths.Count());
5252
}
5353

5454
[Fact]
@@ -67,7 +67,7 @@ public void GetPathsForGraphBetaModelWithDerivedTypesConstraintReturnsAllPaths()
6767

6868
// Assert
6969
Assert.NotNull(paths);
70-
Assert.Equal(50070, paths.Count());
70+
Assert.Equal(28193, paths.Count());
7171
}
7272

7373
[Fact]
@@ -271,14 +271,15 @@ public void GetPathsWithBoundActionOperationForContainmentNavigationPropertyPath
271271

272272
// Assert
273273
Assert.NotNull(paths);
274-
Assert.Equal(5, paths.Count());
275274

276275
if (containsTarget)
277276
{
277+
Assert.Equal(5, paths.Count());
278278
Assert.Contains("/Customers({ID})/Referral/NS.Ack", paths.Select(p => p.GetPathItemName()));
279279
}
280280
else
281281
{
282+
Assert.Equal(4, paths.Count());
282283
Assert.DoesNotContain("/Customers({ID})/Referral/NS.Ack", paths.Select(p => p.GetPathItemName()));
283284
}
284285
}
@@ -308,14 +309,15 @@ public void GetPathsWithBoundFunctionOperationForContainmentNavigationPropertyPa
308309

309310
// Assert
310311
Assert.NotNull(paths);
311-
Assert.Equal(5, paths.Count());
312312

313313
if (containsTarget)
314314
{
315+
Assert.Equal(5, paths.Count());
315316
Assert.Contains("/Customers({ID})/Referral/NS.Search()", paths.Select(p => p.GetPathItemName()));
316317
}
317318
else
318319
{
320+
Assert.Equal(4, paths.Count());
319321
Assert.DoesNotContain("/Customers({ID})/Referral/NS.Search()", paths.Select(p => p.GetPathItemName()));
320322
}
321323
}
@@ -380,7 +382,7 @@ public void GetPathsWithFalseNavigabilityInNavigationRestrictionsAnnotationWorks
380382

381383
// Assert
382384
Assert.NotNull(paths);
383-
Assert.Equal(10, paths.Count());
385+
Assert.Equal(9, paths.Count());
384386

385387
var pathItems = paths.Select(p => p.GetPathItemName()).ToList();
386388
Assert.DoesNotContain("/Orders({id})/SingleCustomer", pathItems);
@@ -423,23 +425,35 @@ public void GetPathsWithFalseIndexabilityByKeyInNavigationRestrictionsAnnotation
423425

424426
// Assert
425427
Assert.NotNull(paths);
426-
Assert.Equal(9, paths.Count());
428+
Assert.Equal(8, paths.Count());
427429

428430
var pathItems = paths.Select(p => p.GetPathItemName()).ToList();
429431
Assert.DoesNotContain("/Orders({id})/MultipleCustomers({ID})", pathItems);
430432
}
431433

432434
[Fact]
433-
public void GetPathsWithNonContainedNavigationPropertyWorks()
435+
public void GetPathsWithReferenceableNavigationPropertyWorks()
434436
{
435437
// Arrange
436438
string entityType =
437439
@"<EntityType Name=""Order"">
438440
<Key>
439441
<PropertyRef Name=""id"" />
440442
</Key>
441-
<NavigationProperty Name=""MultipleCustomers"" Type=""Collection(NS.Customer)"" />
442-
<NavigationProperty Name=""SingleCustomer"" Type=""NS.Customer"" />
443+
<NavigationProperty Name=""MultipleCustomers"" Type=""Collection(NS.Customer)"">
444+
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
445+
<Record>
446+
<PropertyValue Property=""Referenceable"" Bool=""true"" />
447+
</Record>
448+
</Annotation>
449+
</NavigationProperty>
450+
<NavigationProperty Name=""SingleCustomer"" Type=""NS.Customer"">
451+
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
452+
<Record>
453+
<PropertyValue Property=""Referenceable"" Bool=""true"" />
454+
</Record>
455+
</Annotation>
456+
</NavigationProperty>
443457
</EntityType>";
444458

445459
string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />";
@@ -463,6 +477,40 @@ public void GetPathsWithNonContainedNavigationPropertyWorks()
463477
Assert.Contains("/Orders({id})/MultipleCustomers({ID})/$ref", pathItems);
464478
}
465479

480+
[Fact]
481+
public void GetPathsWithNonReferenceableNavigationPropertyWorks()
482+
{
483+
// Arrange
484+
string entityType =
485+
@"<EntityType Name=""Order"">
486+
<Key>
487+
<PropertyRef Name=""id"" />
488+
</Key>
489+
<NavigationProperty Name=""MultipleCustomers"" Type=""Collection(NS.Customer)"" />
490+
<NavigationProperty Name=""SingleCustomer"" Type=""NS.Customer"" />
491+
</EntityType>";
492+
493+
string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />";
494+
IEdmModel model = GetEdmModel(entityType, entitySet);
495+
496+
ODataPathProvider provider = new ODataPathProvider();
497+
var settings = new OpenApiConvertSettings();
498+
499+
// Act
500+
var paths = provider.GetPaths(model, settings);
501+
502+
// Assert
503+
Assert.NotNull(paths);
504+
Assert.Equal(10, paths.Count());
505+
506+
var pathItems = paths.Select(p => p.GetPathItemName()).ToList();
507+
Assert.Contains("/Orders({id})/MultipleCustomers", pathItems);
508+
Assert.Contains("/Orders({id})/SingleCustomer", pathItems);
509+
Assert.Contains("/Orders({id})/SingleCustomer", pathItems);
510+
Assert.Contains("/Orders({id})/MultipleCustomers", pathItems);
511+
Assert.Contains("/Orders({id})/MultipleCustomers({ID})", pathItems);
512+
}
513+
466514
[Fact]
467515
public void GetPathsWithContainedNavigationPropertyWorks()
468516
{
@@ -522,22 +570,22 @@ public void GetPathsWithStreamPropertyAndWithEntityHasStreamWorks(bool hasStream
522570
{
523571
if (hasStream)
524572
{
525-
Assert.Equal(15, paths.Count());
573+
Assert.Equal(14, paths.Count());
526574
Assert.Contains(TodosValuePath, paths.Select(p => p.GetPathItemName()));
527575
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
528576
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
529577
}
530578
else
531579
{
532-
Assert.Equal(14, paths.Count());
580+
Assert.Equal(13, paths.Count());
533581
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
534582
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
535583
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));
536584
}
537585
}
538586
else if (streamPropName.Equals("content"))
539587
{
540-
Assert.Equal(14, paths.Count());
588+
Assert.Equal(13, paths.Count());
541589
Assert.Contains(TodosContentPath, paths.Select(p => p.GetPathItemName()));
542590
Assert.DoesNotContain(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
543591
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void CreatePathItemsReturnsForBasicModel()
5757

5858
// Assert
5959
Assert.NotNull(pathItems);
60-
Assert.Equal(26, pathItems.Count);
60+
Assert.Equal(22, pathItems.Count);
6161

6262
Assert.Contains("/People", pathItems.Keys);
6363
Assert.Contains("/People/$count", pathItems.Keys);
@@ -66,7 +66,6 @@ public void CreatePathItemsReturnsForBasicModel()
6666
Assert.Contains("/People/{UserName}/Addresses/$count", pathItems.Keys);
6767
Assert.Contains("/People/{UserName}/HomeAddress", pathItems.Keys);
6868
Assert.Contains("/People/{UserName}/HomeAddress/City", pathItems.Keys);
69-
Assert.Contains("/People/{UserName}/HomeAddress/City/$ref", pathItems.Keys);
7069
Assert.Contains("/City", pathItems.Keys);
7170
Assert.Contains("/City/$count", pathItems.Keys);
7271
Assert.Contains("/City/{Name}", pathItems.Keys);
@@ -78,10 +77,8 @@ public void CreatePathItemsReturnsForBasicModel()
7877
Assert.Contains("/Me/Addresses/$count", pathItems.Keys);
7978
Assert.Contains("/Me/HomeAddress", pathItems.Keys);
8079
Assert.Contains("/Me/HomeAddress/City", pathItems.Keys);
81-
Assert.Contains("/Me/HomeAddress/City/$ref", pathItems.Keys);
8280
Assert.Contains("/Me/WorkAddress", pathItems.Keys);
8381
Assert.Contains("/Me/WorkAddress/City", pathItems.Keys);
84-
Assert.Contains("/Me/WorkAddress/City/$ref", pathItems.Keys);
8582
}
8683

8784
[Theory]

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void CreatePathsReturnsForBasicModel()
5656

5757
// Assert
5858
Assert.NotNull(paths);
59-
Assert.Equal(26, paths.Count);
59+
Assert.Equal(22, paths.Count);
6060

6161
Assert.Contains("/People", paths.Keys);
6262
Assert.Contains("/People/$count", paths.Keys);
@@ -65,7 +65,6 @@ public void CreatePathsReturnsForBasicModel()
6565
Assert.Contains("/People/{UserName}/Addresses/$count", paths.Keys);
6666
Assert.Contains("/People/{UserName}/HomeAddress", paths.Keys);
6767
Assert.Contains("/People/{UserName}/HomeAddress/City", paths.Keys);
68-
Assert.Contains("/People/{UserName}/HomeAddress/City/$ref", paths.Keys);
6968
Assert.Contains("/City", paths.Keys);
7069
Assert.Contains("/City/$count", paths.Keys);
7170
Assert.Contains("/City/{Name}", paths.Keys);
@@ -77,10 +76,8 @@ public void CreatePathsReturnsForBasicModel()
7776
Assert.Contains("/Me/Addresses/$count", paths.Keys);
7877
Assert.Contains("/Me/HomeAddress", paths.Keys);
7978
Assert.Contains("/Me/HomeAddress/City", paths.Keys);
80-
Assert.Contains("/Me/HomeAddress/City/$ref", paths.Keys);
8179
Assert.Contains("/Me/WorkAddress", paths.Keys);
8280
Assert.Contains("/Me/WorkAddress/City", paths.Keys);
83-
Assert.Contains("/Me/WorkAddress/City/$ref", paths.Keys);
8481
}
8582

8683
[Fact]
@@ -100,7 +97,7 @@ public void CreatePathsReturnsForBasicModelWithPrefix()
10097

10198
// Assert
10299
Assert.NotNull(paths);
103-
Assert.Equal(26, paths.Count);
100+
Assert.Equal(22, paths.Count);
104101

105102
Assert.Contains("/some/prefix/People", paths.Keys);
106103
Assert.Contains("/some/prefix/People/$count", paths.Keys);
@@ -109,7 +106,6 @@ public void CreatePathsReturnsForBasicModelWithPrefix()
109106
Assert.Contains("/some/prefix/People/{UserName}/Addresses/$count", paths.Keys);
110107
Assert.Contains("/some/prefix/People/{UserName}/HomeAddress", paths.Keys);
111108
Assert.Contains("/some/prefix/People/{UserName}/HomeAddress/City", paths.Keys);
112-
Assert.Contains("/some/prefix/People/{UserName}/HomeAddress/City/$ref", paths.Keys);
113109
Assert.Contains("/some/prefix/City", paths.Keys);
114110
Assert.Contains("/some/prefix/City/$count", paths.Keys);
115111
Assert.Contains("/some/prefix/City/{Name}", paths.Keys);
@@ -121,10 +117,8 @@ public void CreatePathsReturnsForBasicModelWithPrefix()
121117
Assert.Contains("/some/prefix/Me/Addresses/$count", paths.Keys);
122118
Assert.Contains("/some/prefix/Me/HomeAddress", paths.Keys);
123119
Assert.Contains("/some/prefix/Me/HomeAddress/City", paths.Keys);
124-
Assert.Contains("/some/prefix/Me/HomeAddress/City/$ref", paths.Keys);
125120
Assert.Contains("/some/prefix/Me/WorkAddress", paths.Keys);
126121
Assert.Contains("/some/prefix/Me/WorkAddress/City", paths.Keys);
127-
Assert.Contains("/some/prefix/Me/WorkAddress/City/$ref", paths.Keys);
128122
}
129123

130124
[Fact]

0 commit comments

Comments
 (0)