Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ff209c8
Fix Bulk operations tests
KenitoInc Jul 27, 2022
514a74b
Comment out BulkInsertEF
KenitoInc Jul 27, 2022
be39217
Refactor all tests
KenitoInc Jul 27, 2022
726a175
Update handlers and helpers
KenitoInc Aug 17, 2022
dd89ba1
Cleanup ODataPathHelper
KenitoInc Aug 17, 2022
8e672a7
Cleanup ODataPathExtensions
KenitoInc Aug 17, 2022
c49272d
cleanup BulkInsertController
KenitoInc Aug 17, 2022
8a25402
Activate EF tests
KenitoInc Aug 24, 2022
52802f0
Update namespaces
KenitoInc Aug 24, 2022
7a7b99c
Rename BulkInsert to BulkOperation
KenitoInc Aug 24, 2022
c1142d1
Fix indent
KenitoInc Aug 24, 2022
cf5a3e0
Fix namespaces in tests, contollers and handlers
KenitoInc Aug 24, 2022
9ecd145
cleanup BulkOperationTest
KenitoInc Aug 25, 2022
2e38d32
cleanup BulkOperationTestEF
KenitoInc Aug 25, 2022
092800b
Update comment
KenitoInc Aug 25, 2022
d697dd9
Fix perf of ODataPath helper and extension methods
KenitoInc Aug 30, 2022
5794f56
Remove duplicate links
KenitoInc Aug 30, 2022
eeffcdc
Fix line spacing
KenitoInc Aug 30, 2022
fd0f381
Add ODataPathExtensions tests
KenitoInc Aug 30, 2022
77c1465
Refactor sample model and add ODataPathHelper tests
KenitoInc Aug 30, 2022
f6348b8
Fixes based on review comments
KenitoInc Aug 31, 2022
a57c245
Initialize errorMessage with null in some handlers
KenitoInc Sep 1, 2022
7d3776b
Fix edge cases and add more tests
KenitoInc Sep 1, 2022
a0b01c6
Add more tests
KenitoInc Sep 1, 2022
63dfd6b
Add position validation test
KenitoInc Sep 1, 2022
eb85195
Make ODataPathExtensions and ODataPathHelper internal
KenitoInc Sep 5, 2022
777fbb2
Use ToDictionary
KenitoInc Sep 5, 2022
a513554
Refactor extension methods to return empty collection instead of null
KenitoInc Sep 5, 2022
9e1759c
Remove unnecessary code
KenitoInc Sep 5, 2022
ed795b2
Add tests
KenitoInc Sep 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/Microsoft.AspNet.OData.Shared/Common/ODataPathHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// <copyright file="ODataPathHelper.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNet.OData.Common
{
/// <summary>
/// Helper methods for <see cref="ODataPath"/>.
/// </summary>
internal static class ODataPathHelper
{
/// <summary>
/// Get the keys from a <see cref="KeySegment"/>.
/// </summary>
/// <param name="keySegment">The <see cref="KeySegment"/> to extract the keys.</param>
/// <returns>Dictionary of keys.</returns>
public static Dictionary<string, object> KeySegmentAsDictionary(KeySegment keySegment)
{
if (keySegment == null)
{
throw Error.ArgumentNull(nameof(keySegment));
}

return keySegment.Keys.ToDictionary(d => d.Key, d => d.Value);
}

/// <summary>
/// Get the position of the next <see cref="KeySegment"/> in a list of <see cref="ODataPathSegment"/>.
/// </summary>
/// <param name="pathSegments">List of <see cref="ODataPathSegment"/>.</param>
/// <param name="currentPosition">Current position in the list of <see cref="ODataPathSegment"/>.</param>
/// <returns>Position of the next <see cref="KeySegment"/> if it exists, or -1 otherwise.</returns>
public static int GetNextKeySegmentPosition(IReadOnlyList<ODataPathSegment> pathSegments, int currentPosition)
{
if (pathSegments == null)
{
throw Error.ArgumentNull(nameof(pathSegments));
}

if (currentPosition < 0 || currentPosition >= pathSegments.Count)
{
return -1;
}

if (pathSegments[currentPosition] is KeySegment)
{
currentPosition++;
}

for (int i = currentPosition; i < pathSegments.Count; i++)
{
ODataPathSegment currentSegment = pathSegments[i];

if (currentSegment is KeySegment)
{
return i;
}
}

return -1;
}
}
}
71 changes: 52 additions & 19 deletions src/Microsoft.AspNet.OData.Shared/Extensions/ODataPathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,84 @@
//------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.OData.Common;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNet.OData.Extensions
{
/// <summary>
/// Extensions method for <see cref="ODataPath"/>.
/// </summary>
internal static class ODataPathExtensions
{
public static Dictionary<string, object> GetKeys(this ODataPath path)
/// <summary>
/// Get keys from the last <see cref="KeySegment"/>.
/// </summary>
/// <param name="path"><see cref="ODataPath"/>.</param>
/// <returns>Dictionary of keys.</returns>
internal static Dictionary<string, object> GetKeys(this ODataPath path)
{
Dictionary<string, object> keys = new Dictionary<string, object>();

if (path == null)
{
throw Error.ArgumentNull(nameof(path));
}

Dictionary<string, object> keys = new Dictionary<string, object>();

// Books(1)/Authors(1000)/Namespace.SpecialAuthor
if (path.LastSegment is TypeSegment)
if (path.Count == 0)
{
ODataPath pathWithoutLastSegmentCastType = path.TrimEndingTypeSegment();

if (pathWithoutLastSegmentCastType.LastSegment is KeySegment)
{
keys = GetKeysFromKeySegment(pathWithoutLastSegmentCastType.LastSegment as KeySegment);
}
return keys;
}
// Books(1)/Authors/Namespace.SpecialAuthor/(1000)
else if (path.LastSegment is KeySegment)

List<ODataPathSegment> pathSegments = path.AsList();

KeySegment keySegment = pathSegments.OfType<KeySegment>().LastOrDefault();

if (keySegment == null)
{
keys = GetKeysFromKeySegment(path.LastSegment as KeySegment);
return keys;
}

keys = ODataPathHelper.KeySegmentAsDictionary(keySegment);

return keys;
}

private static Dictionary<string, object> GetKeysFromKeySegment(KeySegment keySegment)
/// <summary>
/// Return the last segment in the path, which is not a <see cref="TypeSegment"/> or <see cref="KeySegment"/>.
/// </summary>
/// <param name="path">The <see cref="ODataPath"/>.</param>
/// <returns>An <see cref="ODataPathSegment"/>.</returns>
public static ODataPathSegment GetLastNonTypeNonKeySegment(this ODataPath path)
{
Dictionary<string, object> keys = new Dictionary<string, object>();
if (path == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check seems unnecessary in an extension method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
throw Error.ArgumentNull(nameof(path));
}

// If the path is Employees(2)/NewFriends(2)/Namespace.MyNewFriend where Namespace.MyNewFriend is a type segment,
// This method will return NewFriends NavigationPropertySegment.

foreach (KeyValuePair<string, object> kvp in keySegment.Keys)
List<ODataPathSegment> pathSegments = path.AsList();
int position = path.Count - 1;

while (position >= 0 && (pathSegments[position] is TypeSegment || pathSegments[position] is KeySegment))
{
keys.Add(kvp.Key, kvp.Value);
--position;
}

return keys;
return position < 0 ? null : pathSegments[position];
}

/// <summary>
/// Returns a list of <see cref="ODataPathSegment"/> in an <see cref="ODataPath"/>.
/// </summary>
/// <param name="path">The <see cref="ODataPath"/>.</param>
/// <returns>List of <see cref="ODataPathSegment"/>.</returns>
public static List<ODataPathSegment> GetSegments(this ODataPath path)
{
return path.AsList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ClrEnumMemberAnnotation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\CollectionExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\ListWrapperCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\ODataPathHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\PropertyHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\TaskHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\Error.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1402,30 +1402,21 @@
<Compile Include="..\OpenType\TypedTest.cs">
<Link>OpenType\TypedTest.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertDataModel.cs">
<Link>BulkOperation\BulkInsertDataModel.cs</Link>
<Compile Include="..\BulkOperation\BulkOperationTest.cs">
<Link>BulkOperation\BulkOperationTest.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertEdmModel.cs">
<Link>BulkOperation\BulkInsertEdmModel.cs</Link>
<Compile Include="..\BulkOperation\BulkOperationDataModel.cs">
<Link>BulkOperation\BulkOperationDataModel.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertController.cs">
<Link>BulkOperation\BulkInsertController.cs</Link>
<Compile Include="..\BulkOperation\BulkOperationEdmModel.cs">
<Link>BulkOperation\BulkOperationEdmModel.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkOperationController.cs">
<Link>BulkOperation\BulkOperationController.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkOperationPatchHandlers.cs">
<Link>BulkOperation\BulkOperationPatchHandlers.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertDataModel.cs">
<Link>BulkOperation\BulkInsertDataModel.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertEdmModel.cs">
<Link>BulkOperation\BulkInsertEdmModel.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkInsertController.cs">
<Link>BulkOperation\BulkInsertController.cs</Link>
</Compile>
<Compile Include="..\BulkOperation\BulkOperationPatchHandlers.cs">
<Link>BulkOperation\BulkOperationPatchHandlers.cs</Link>
</Compile>
<Compile Include="..\ParameterAlias\ParameterAliasDataSource.cs">
<Link>ParameterAlias\ParameterAliasDataSource.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Test.E2E.AspNet.OData.BulkOperation;
using Microsoft.Test.E2E.AspNet.OData.Common.Controllers;
using Xunit;
using static Microsoft.Test.E2E.AspNet.OData.BulkOperation.APIHandlerFactoryEF;

namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
public class EmployeesControllerEF : TestODataController
{
public EmployeesControllerEF()
{

}

public static List<Employee> employees;
public static List<Friend> friends;

internal DbSet<Employee> GenerateData(EmployeeDBContext context)
{
{
if (context.Employees.Any())
{
return context.Employees;
Expand Down Expand Up @@ -59,7 +58,7 @@ internal DbSet<Friend> GenerateDataOrders(EmployeeDBContext context)
}

friends = new List<Friend>();
friends.Add(new Friend { Id = 1, Age = 10 , Orders = new List<Order>() { new Order { Id = 1, Price = 5 }, new Order { Id = 2, Price = 5 } } });
friends.Add(new Friend { Id = 1, Age = 10, Orders = new List<Order>() { new Order { Id = 1, Price = 5 }, new Order { Id = 2, Price = 5 } } });
friends.Add(new Friend { Id = 2, Age = 20, Orders = new List<Order>() { new Order { Id = 10, Price = 5 }, new Order { Id = 20, Price = 5 } } });
friends.Add(new Friend { Id = 3, Age = 30, Orders = new List<Order>() { new Order { Id = 3, Price = 5 }, new Order { Id = 4, Price = 5 } } });
friends.Add(new Friend { Id = 4, Age = 40, Orders = new List<Order>() { new Order { Id = 30, Price = 5 }, new Order { Id = 40, Price = 5 } } });
Expand Down
Loading