Skip to content

Commit e6a5e52

Browse files
authored
[Fix] Generate paths for stream properties in base types of entities (#110)
* Get all properties declared in type def. including base types * Update/refactor tests to validate stream props. of base types are captured * Refactor Handler class to use encapsulation to define navigation sources
1 parent e8ecb37 commit e6a5e52

File tree

7 files changed

+69
-75
lines changed

7 files changed

+69
-75
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ private void RetrieveMediaEntityStreamPaths(IEdmEntityType entityType, ODataPath
219219
Debug.Assert(currentPath != null);
220220

221221
bool createValuePath = true;
222-
foreach (IEdmStructuralProperty sp in entityType.DeclaredStructuralProperties())
222+
foreach (IEdmStructuralProperty sp in entityType.StructuralProperties())
223223
{
224224
if (sp.Type.AsPrimitive().IsStream())
225225
{
@@ -228,7 +228,7 @@ private void RetrieveMediaEntityStreamPaths(IEdmEntityType entityType, ODataPath
228228
currentPath.Pop();
229229
}
230230

231-
if (sp.Name.Equals("content", System.StringComparison.OrdinalIgnoreCase))
231+
if (sp.Name.Equals("content", StringComparison.OrdinalIgnoreCase))
232232
{
233233
createValuePath = false;
234234
}

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ internal class MediaEntityGetOperationHandler : MediaEntityOperationalHandler
2626
protected override void SetBasicInfo(OpenApiOperation operation)
2727
{
2828
// Summary
29-
if (IsNavigationPropertyPath)
30-
{
31-
operation.Summary = $"Get media content for the navigation property {NavigationProperty.Name} from {NavigationSource.Name}";
32-
}
33-
else
34-
{
35-
IEdmEntityType entityType = EntitySet.EntityType();
36-
operation.Summary = $"Get media content for {entityType.Name} from {EntitySet.Name}";
37-
}
29+
operation.Summary = IsNavigationPropertyPath
30+
? $"Get media content for the navigation property {NavigationProperty.Name} from {NavigationSource.Name}"
31+
: $"Get media content for {NavigationSourceSegment.EntityType.Name} from {NavigationSourceSegment.Identifier}";
3832

3933
// Description
4034
IEdmVocabularyAnnotatable annotatableElement = GetAnnotatableElement();
@@ -73,9 +67,8 @@ protected override void SetResponses(OpenApiOperation operation)
7367
/// <inheritdoc/>
7468
protected override void SetSecurity(OpenApiOperation operation)
7569
{
76-
ReadRestrictionsType read = EntitySet != null
77-
? Context.Model.GetRecord<ReadRestrictionsType>(EntitySet, CapabilitiesConstants.ReadRestrictions)
78-
: Context.Model.GetRecord<ReadRestrictionsType>(Singleton, CapabilitiesConstants.ReadRestrictions);
70+
IEdmVocabularyAnnotatable annotatableNavigationSource = (IEdmVocabularyAnnotatable)NavigationSourceSegment.NavigationSource;
71+
ReadRestrictionsType read = Context.Model.GetRecord<ReadRestrictionsType>(annotatableNavigationSource, CapabilitiesConstants.ReadRestrictions);
7972
if (read == null)
8073
{
8174
return;

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

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,9 @@ namespace Microsoft.OpenApi.OData.Operation
2121
internal abstract class MediaEntityOperationalHandler : NavigationPropertyOperationHandler
2222
{
2323
/// <summary>
24-
/// Gets/sets the <see cref="IEdmEntitySet"/>.
24+
/// Gets/Sets the NavigationSource segment
2525
/// </summary>
26-
protected IEdmEntitySet EntitySet { get; private set; }
27-
28-
/// <summary>
29-
/// Gets the <see cref="IEdmSingleton"/>.
30-
/// </summary>
31-
protected IEdmSingleton Singleton { get; private set; }
26+
protected ODataNavigationSourceSegment NavigationSourceSegment { get; private set; }
3227

3328
/// <summary>
3429
/// Gets/Sets flag indicating whether path is navigation property path
@@ -39,13 +34,7 @@ internal abstract class MediaEntityOperationalHandler : NavigationPropertyOperat
3934
protected override void Initialize(ODataContext context, ODataPath path)
4035
{
4136
// The first segment will either be an EntitySet navigation source or a Singleton navigation source
42-
ODataNavigationSourceSegment navigationSourceSegment = path.FirstSegment as ODataNavigationSourceSegment;
43-
EntitySet = navigationSourceSegment.NavigationSource as IEdmEntitySet;
44-
45-
if (EntitySet == null)
46-
{
47-
Singleton = navigationSourceSegment.NavigationSource as IEdmSingleton;
48-
}
37+
NavigationSourceSegment = path.FirstSegment as ODataNavigationSourceSegment;
4938

5039
// Check whether path is a navigation property path
5140
IsNavigationPropertyPath = Path.Segments.Contains(
@@ -67,9 +56,9 @@ protected override void SetTags(OpenApiOperation operation)
6756
}
6857
else
6958
{
70-
string tagIdentifier = EntitySet.Name + "." + EntitySet.EntityType().Name;
59+
string tagIdentifier = NavigationSourceSegment.Identifier + "." + NavigationSourceSegment.EntityType.Name;
7160

72-
OpenApiTag tag = new OpenApiTag
61+
OpenApiTag tag = new()
7362
{
7463
Name = tagIdentifier
7564
};
@@ -102,7 +91,7 @@ protected string GetOperationId(string prefix, string identifier)
10291

10392
IList<string> items = new List<string>
10493
{
105-
EntitySet?.Name ?? Singleton.Name
94+
NavigationSourceSegment.Identifier
10695
};
10796

10897
ODataSegment lastSegment = Path.Segments.Last(c => c is ODataStreamContentSegment || c is ODataStreamPropertySegment);
@@ -112,7 +101,7 @@ protected string GetOperationId(string prefix, string identifier)
112101
{
113102
if (!IsNavigationPropertyPath)
114103
{
115-
string typeName = EntitySet?.EntityType().Name ?? Singleton.EntityType().Name;
104+
string typeName = NavigationSourceSegment.EntityType.Name;
116105
items.Add(typeName);
117106
items.Add(prefix + Utils.UpperFirstChar(identifier));
118107
}
@@ -185,7 +174,7 @@ protected IDictionary<string, OpenApiMediaType> GetContentDescription()
185174
/// <returns>The annotable element.</returns>
186175
protected IEdmVocabularyAnnotatable GetAnnotatableElement()
187176
{
188-
IEdmEntityType entityType = EntitySet != null ? EntitySet.EntityType() : Singleton.EntityType();
177+
IEdmEntityType entityType = NavigationSourceSegment.EntityType;
189178
ODataSegment lastSegmentProp = Path.Segments.LastOrDefault(c => c is ODataStreamPropertySegment);
190179

191180
if (lastSegmentProp == null)
@@ -214,7 +203,7 @@ protected IEdmVocabularyAnnotatable GetAnnotatableElement()
214203

215204
private IEdmStructuralProperty GetStructuralProperty(IEdmEntityType entityType, string identifier)
216205
{
217-
return entityType.DeclaredStructuralProperties().FirstOrDefault(x => x.Name.Equals(identifier));
206+
return entityType.StructuralProperties().FirstOrDefault(x => x.Name.Equals(identifier));
218207
}
219208

220209
private IEdmNavigationProperty GetNavigationProperty(IEdmEntityType entityType, string identifier)

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ internal class MediaEntityPutOperationHandler : MediaEntityOperationalHandler
2626
protected override void SetBasicInfo(OpenApiOperation operation)
2727
{
2828
// Summary
29-
if (IsNavigationPropertyPath)
30-
{
31-
operation.Summary = $"Update media content for the navigation property {NavigationProperty.Name} in {NavigationSource.Name}";
32-
}
33-
else
34-
{
35-
string typeName = EntitySet.EntityType().Name;
36-
operation.Summary = $"Update media content for {typeName} in {EntitySet.Name}";
37-
}
29+
operation.Summary = IsNavigationPropertyPath
30+
? $"Update media content for the navigation property {NavigationProperty.Name} in {NavigationSource.Name}"
31+
: $"Update media content for {NavigationSourceSegment.EntityType.Name} in {NavigationSourceSegment.Identifier}";
3832

3933
// Description
4034
IEdmVocabularyAnnotatable annotatableElement = GetAnnotatableElement();
@@ -79,9 +73,8 @@ protected override void SetResponses(OpenApiOperation operation)
7973
/// <inheritdoc/>
8074
protected override void SetSecurity(OpenApiOperation operation)
8175
{
82-
UpdateRestrictionsType update = EntitySet != null
83-
? Context.Model.GetRecord<UpdateRestrictionsType>(EntitySet, CapabilitiesConstants.UpdateRestrictions)
84-
: Context.Model.GetRecord<UpdateRestrictionsType>(Singleton, CapabilitiesConstants.UpdateRestrictions);
76+
IEdmVocabularyAnnotatable annotatableNavigationSource = (IEdmVocabularyAnnotatable)NavigationSourceSegment.NavigationSource;
77+
UpdateRestrictionsType update = Context.Model.GetRecord<UpdateRestrictionsType>(annotatableNavigationSource, CapabilitiesConstants.UpdateRestrictions);
8578
if (update == null || update.Permissions == null)
8679
{
8780
return;

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

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -450,41 +450,52 @@ public void GetPathsWithContainedNavigationPropertyWorks()
450450
}
451451

452452
[Theory]
453-
[InlineData(true, "Logo")]
454-
[InlineData(false, "Logo")]
455-
[InlineData(true, "Content")]
456-
[InlineData(false, "Content")]
453+
[InlineData(true, "logo")]
454+
[InlineData(false, "logo")]
455+
[InlineData(true, "content")]
456+
[InlineData(false, "content")]
457457
public void GetPathsWithStreamPropertyAndWithEntityHasStreamWorks(bool hasStream, string streamPropName)
458458
{
459459
// Arrange
460460
IEdmModel model = GetEdmModel(hasStream, streamPropName);
461461
ODataPathProvider provider = new ODataPathProvider();
462462
var settings = new OpenApiConvertSettings();
463+
const string TodosContentPath = "/todos({id})/content";
464+
const string TodosValuePath = "/todos({id})/$value";
465+
const string TodosLogoPath = "/todos({id})/logo";
463466

464467
// Act
465-
var paths = provider.GetPaths(model,settings);
468+
var paths = provider.GetPaths(model, settings);
466469

467470
// Assert
468471
Assert.NotNull(paths);
472+
Assert.Contains("/catalog/content", paths.Select(p => p.GetPathItemName()));
473+
Assert.Contains("/catalog/thumbnailPhoto", paths.Select(p => p.GetPathItemName()));
474+
Assert.Contains("/me/photo/$value", paths.Select(p => p.GetPathItemName()));
469475

470-
if (hasStream && !streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase))
471-
{
472-
Assert.Equal(7, paths.Count());
473-
Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/$value", "/Todos({Id})/Logo" },
474-
paths.Select(p => p.GetPathItemName()));
475-
}
476-
else if ((hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)) ||
477-
(!hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)))
476+
if (streamPropName.Equals("logo"))
478477
{
479-
Assert.Equal(6, paths.Count());
480-
Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Content" },
481-
paths.Select(p => p.GetPathItemName()));
478+
if (hasStream)
479+
{
480+
Assert.Equal(12, paths.Count());
481+
Assert.Contains(TodosValuePath, paths.Select(p => p.GetPathItemName()));
482+
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
483+
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
484+
}
485+
else
486+
{
487+
Assert.Equal(11, paths.Count());
488+
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
489+
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
490+
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));
491+
}
482492
}
483-
else // !hasStream && !streamPropName.Equals("Content")
493+
else if (streamPropName.Equals("content"))
484494
{
485-
Assert.Equal(6, paths.Count());
486-
Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Logo"},
487-
paths.Select(p => p.GetPathItemName()));
495+
Assert.Equal(11, paths.Count());
496+
Assert.Contains(TodosContentPath, paths.Select(p => p.GetPathItemName()));
497+
Assert.DoesNotContain(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
498+
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));
488499
}
489500
}
490501

@@ -576,13 +587,13 @@ private static IEdmModel GetEdmModel(bool hasStream, string streamPropName)
576587
string template = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
577588
<edmx:DataServices>
578589
<Schema Namespace=""microsoft.graph"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
579-
<EntityType Name=""Todo"" HasStream=""{0}"">
590+
<EntityType Name=""todo"" HasStream=""{0}"">
580591
<Key>
581-
<PropertyRef Name=""Id"" />
592+
<PropertyRef Name=""id"" />
582593
</Key>
583-
<Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" />
594+
<Property Name=""id"" Type=""Edm.Int32"" Nullable=""false"" />
584595
<Property Name=""{1}"" Type=""Edm.Stream""/>
585-
<Property Name = ""Description"" Type = ""Edm.String"" />
596+
<Property Name = ""description"" Type = ""Edm.String"" />
586597
</EntityType>
587598
<EntityType Name=""user"" OpenType=""true"">
588599
<NavigationProperty Name = ""photo"" Type = ""microsoft.graph.profilePhoto"" ContainsTarget = ""true"" />
@@ -591,9 +602,17 @@ private static IEdmModel GetEdmModel(bool hasStream, string streamPropName)
591602
<Property Name = ""height"" Type = ""Edm.Int32"" />
592603
<Property Name = ""width"" Type = ""Edm.Int32"" />
593604
</EntityType >
605+
<EntityType Name=""document"">
606+
<Property Name=""content"" Type=""Edm.Stream""/>
607+
<Property Name=""thumbnailPhoto"" Type=""Edm.Stream""/>
608+
</EntityType>
609+
<EntityType Name=""catalog"" BaseType=""microsoft.graph.document"">
610+
<NavigationProperty Name=""reports"" Type = ""Collection(microsoft.graph.report)"" />
611+
</EntityType>
594612
<EntityContainer Name =""GraphService"">
595-
<EntitySet Name=""Todos"" EntityType=""microsoft.graph.Todo"" />
613+
<EntitySet Name=""todos"" EntityType=""microsoft.graph.todo"" />
596614
<Singleton Name=""me"" Type=""microsoft.graph.user"" />
615+
<Singleton Name=""catalog"" Type=""microsoft.graph.catalog"" />
597616
</EntityContainer>
598617
</Schema>
599618
</edmx:DataServices>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ private void VerifyMediaEntityGetOperation(string annotation, bool enableOperati
5555
Assert.NotNull(me);
5656

5757
IEdmEntityType todo = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Todo");
58-
IEdmStructuralProperty sp = todo.DeclaredStructuralProperties().First(c => c.Name == "Logo");
58+
IEdmStructuralProperty sp = todo.StructuralProperties().First(c => c.Name == "Logo");
5959
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(todos),
6060
new ODataKeySegment(todos.EntityType()),
6161
new ODataStreamPropertySegment(sp.Name));
6262

6363
IEdmEntityType user = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "user");
64-
IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
64+
IEdmNavigationProperty navProperty = user.NavigationProperties().First(c => c.Name == "photo");
6565
ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
6666
new ODataNavigationPropertySegment(navProperty),
6767
new ODataStreamContentSegment());

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ private void VerifyMediaEntityPutOperation(string annotation, bool enableOperati
5252
Assert.NotNull(todos);
5353

5454
IEdmEntityType todo = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Todo");
55-
IEdmStructuralProperty sp = todo.DeclaredStructuralProperties().First(c => c.Name == "Logo");
55+
IEdmStructuralProperty sp = todo.StructuralProperties().First(c => c.Name == "Logo");
5656
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(todos),
5757
new ODataKeySegment(todos.EntityType()),
5858
new ODataStreamPropertySegment(sp.Name));
5959

6060
IEdmEntityType user = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "user");
61-
IEdmNavigationProperty navProperty = user.DeclaredNavigationProperties().First(c => c.Name == "photo");
61+
IEdmNavigationProperty navProperty = user.NavigationProperties().First(c => c.Name == "photo");
6262
ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
6363
new ODataNavigationPropertySegment(navProperty),
6464
new ODataStreamContentSegment());

0 commit comments

Comments
 (0)