Skip to content
Merged
52 changes: 32 additions & 20 deletions src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,37 +271,38 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr

// Check whether a collection-valued navigation property should be indexed by key value(s).
NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault();

if (restriction == null || restriction.IndexableByKey == true)
{
IEdmEntityType navEntityType = navigationProperty.ToEntityType();

if (!navigationProperty.ContainsTarget)
{
// Non-Contained
// Single-Valued: DELETE ~/entityset/{key}/single-valued-Nav/$ref
// collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/$ref?$id ={ navKey}
ODataPath newPath = currentPath.Clone();
newPath.Push(ODataRefSegment.Instance); // $ref
AppendPath(newPath);
// Single-Valued: ~/entityset/{key}/single-valued-Nav/$ref
// Collection-valued: ~/entityset/{key}/collection-valued-Nav/$ref?$id ={navKey}
CreateRefPath(currentPath);

if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
// Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
currentPath.Push(new ODataKeySegment(navEntityType));
CreateRefPath(currentPath);
}

// Get possible stream paths for the navigation entity type
RetrieveMediaEntityStreamPaths(navEntityType, currentPath);
}
else
{
IEdmEntityType navEntityType = navigationProperty.ToEntityType();

// append a navigation property key.
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Push(new ODataKeySegment(navEntityType));
AppendPath(currentPath.Clone());

if (!navigationProperty.ContainsTarget)
{
// TODO: Shall we add "$ref" after {key}, and only support delete?
// ODataPath newPath = currentPath.Clone();
// newPath.Push(ODataRefSegment.Instance); // $ref
// AppendPath(newPath);
}
}

// Get possible navigation property stream paths
// Get possible stream paths for the navigation entity type
RetrieveMediaEntityStreamPaths(navEntityType, currentPath);

if (shouldExpand)
Expand All @@ -315,11 +316,11 @@ private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationPr
}
}
}
}

if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Pop();
}
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Pop();
}
}
currentPath.Pop();
Expand Down Expand Up @@ -349,6 +350,17 @@ private bool ShouldExpandNavigationProperty(IEdmNavigationProperty navigationPro
return true;
}

/// <summary>
/// Create $ref paths.
/// </summary>
/// <param name="currentPath">The current OData path.</param>
private void CreateRefPath(ODataPath currentPath)
{
ODataPath newPath = currentPath.Clone();
newPath.Push(ODataRefSegment.Instance); // $ref
AppendPath(newPath);
}

/// <summary>
/// Retrieve all bounding <see cref="IEdmOperation"/>.
/// </summary>
Expand Down
69 changes: 47 additions & 22 deletions src/Microsoft.OpenApi.OData.Reader/PathItem/RefPathItemHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,37 +68,62 @@ protected override void SetOperations(OpenApiPathItem item)
// So far, we only consider the non-containment
Debug.Assert(!NavigationProperty.ContainsTarget);

// It seems OData supports to "GetRef, DeleteRef",
// Here at this time,let's only consider the "delete"
ReadRestrictionsType read = restriction?.ReadRestrictions;
if (read == null || read.IsReadable)
{
AddOperation(item, OperationType.Get);
}

// Create the ref
if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
InsertRestrictionsType insert = restriction?.InsertRestrictions;
if (insert == null || insert.IsInsertable)
ODataSegment penultimateSegment = Path.Segments.Reverse().Skip(1).First();
if (penultimateSegment is ODataKeySegment)
{
// Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
AddDeleteOperation(item, restriction);
}
else
{
AddOperation(item, OperationType.Post);
AddReadOperation(item, restriction);
AddInsertOperation(item, restriction);
}
}
else
{
UpdateRestrictionsType update = restriction?.UpdateRestrictions;
if (update == null || update.IsUpdatable)
{
AddOperation(item, OperationType.Put);
}
AddReadOperation(item, restriction);
AddUpdateOperation(item, restriction);
AddDeleteOperation(item, restriction);
}
}

// delete the link
DeleteRestrictionsType delete = restriction?.DeleteRestrictions;
if (delete == null || delete.IsDeletable)
{
AddOperation(item, OperationType.Delete);
}
private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
{
DeleteRestrictionsType delete = restriction?.DeleteRestrictions;
if (delete == null || delete.IsDeletable)
{
AddOperation(item, OperationType.Delete);
}
}

private void AddReadOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
{
ReadRestrictionsType read = restriction?.ReadRestrictions;
if (read == null || read.IsReadable)
{
AddOperation(item, OperationType.Get);
}
}

private void AddInsertOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
{
InsertRestrictionsType insert = restriction?.InsertRestrictions;
if (insert == null || insert.IsInsertable)
{
AddOperation(item, OperationType.Post);
}
}

private void AddUpdateOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
{
UpdateRestrictionsType update = restriction?.UpdateRestrictions;
if (update == null || update.IsUpdatable)
{
AddOperation(item, OperationType.Put);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ------------------------------------------------------------
// --------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
// --------------------------------------------------------------

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -48,7 +48,7 @@ public void GetPathsForGraphBetaModelReturnsAllPaths()

// Assert
Assert.NotNull(paths);
Assert.Equal(17313, paths.Count());
Assert.Equal(17401, paths.Count());
}

[Fact]
Expand All @@ -67,7 +67,7 @@ public void GetPathsForGraphBetaModelWithDerivedTypesConstraintReturnsAllPaths()

// Assert
Assert.NotNull(paths);
Assert.Equal(13642, paths.Count());
Assert.Equal(13730, paths.Count());
}

[Fact]
Expand Down Expand Up @@ -336,7 +336,7 @@ public void GetPathsWithFalseNavigabilityInNavigationRestrictionsAnnotationWorks

// Assert
Assert.NotNull(paths);
Assert.Equal(6, paths.Count());
Assert.Equal(7, paths.Count());

var pathItems = paths.Select(p => p.GetPathItemName()).ToList();
Assert.DoesNotContain("/Orders({id})/SingleCustomer", pathItems);
Expand Down Expand Up @@ -409,13 +409,14 @@ public void GetPathsWithNonContainedNavigationPropertyWorks()

// Assert
Assert.NotNull(paths);
Assert.Equal(8, paths.Count());
Assert.Equal(9, paths.Count());

var pathItems = paths.Select(p => p.GetPathItemName()).ToList();
Assert.Contains("/Orders({id})/MultipleCustomers", pathItems);
Assert.Contains("/Orders({id})/SingleCustomer", pathItems);
Assert.Contains("/Orders({id})/SingleCustomer/$ref", pathItems);
Assert.Contains("/Orders({id})/MultipleCustomers/$ref", pathItems);
Assert.Contains("/Orders({id})/MultipleCustomers({ID})/$ref", pathItems);
}

[Fact]
Expand Down Expand Up @@ -477,22 +478,22 @@ public void GetPathsWithStreamPropertyAndWithEntityHasStreamWorks(bool hasStream
{
if (hasStream)
{
Assert.Equal(12, paths.Count());
Assert.Equal(13, paths.Count());
Assert.Contains(TodosValuePath, paths.Select(p => p.GetPathItemName()));
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
}
else
{
Assert.Equal(11, paths.Count());
Assert.Equal(12, paths.Count());
Assert.Contains(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
Assert.DoesNotContain(TodosContentPath, paths.Select(p => p.GetPathItemName()));
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));
}
}
else if (streamPropName.Equals("content"))
{
Assert.Equal(11, paths.Count());
Assert.Equal(12, paths.Count());
Assert.Contains(TodosContentPath, paths.Select(p => p.GetPathItemName()));
Assert.DoesNotContain(TodosLogoPath, paths.Select(p => p.GetPathItemName()));
Assert.DoesNotContain(TodosValuePath, paths.Select(p => p.GetPathItemName()));
Expand Down Expand Up @@ -609,6 +610,12 @@ private static IEdmModel GetEdmModel(bool hasStream, string streamPropName)
<EntityType Name=""catalog"" BaseType=""microsoft.graph.document"">
<NavigationProperty Name=""reports"" Type = ""Collection(microsoft.graph.report)"" />
</EntityType>
<EntityType Name=""report"">
<Key>
<PropertyRef Name=""id"" />
</Key>
<Property Name=""id"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityContainer Name =""GraphService"">
<EntitySet Name=""todos"" EntityType=""microsoft.graph.todo"" />
<Singleton Name=""me"" Type=""microsoft.graph.user"" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public void CreatePathItemThrowsForNonNavigationPropertyPath()
}

[Theory]
[InlineData(true, new OperationType[] { OperationType.Delete})]
[InlineData(true, new OperationType[] { OperationType.Get, OperationType.Post})]
[InlineData(false, new OperationType[] { OperationType.Get, OperationType.Put, OperationType.Delete })]
public void CreateNavigationPropertyRefPathItemReturnsCorrectPathItem(bool collectionNav, OperationType[] expected)
Expand All @@ -73,10 +74,23 @@ public void CreateNavigationPropertyRefPathItemReturnsCorrectPathItem(bool colle
(collectionNav ? c.TargetMultiplicity() == EdmMultiplicity.Many : c.TargetMultiplicity() != EdmMultiplicity.Many));
Assert.NotNull(property);

ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
ODataPath path;
if (collectionNav && expected.Contains(OperationType.Delete))
{
// DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
new ODataNavigationPropertySegment(property),
new ODataKeySegment(property.ToEntityType()),
ODataRefSegment.Instance);
}
else
{
path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
new ODataKeySegment(entityType),
new ODataNavigationPropertySegment(property),
ODataRefSegment.Instance);
}

// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);
Expand Down
Loading