Adds support for the "folders only" flag on retrieving siblings of a node. (#19861)

* Adds support for the "folders only" flag on retrieving siblings of a node.

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Updated test code.

* Removed double secondary ordering by node Id and ensured we include this clause for all sort orders.

* Ensure that ordering by node Id is always added only once and last, and only if it's not already been included in the order by clause.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Andy Butland
2025-08-07 12:35:04 +02:00
committed by GitHub
parent eb986d9de9
commit 63ed1eec41
17 changed files with 202 additions and 85 deletions

View File

@@ -385,9 +385,14 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
Assert.That(total, Is.EqualTo(10));
}
[Test]
public async Task EntityService_Can_Get_Paged_Document_Type_Children()
[TestCase("")]
[TestCase("sortOrder")]
[TestCase("nodeId")]
public async Task EntityService_Can_Get_Paged_Document_Type_Children(string orderBy)
{
Ordering? ordering = string.IsNullOrEmpty(orderBy)
? null
: Ordering.By(orderBy, Direction.Ascending);
IEnumerable<IEntitySlim> children = EntityService.GetPagedChildren(
_documentTypeRootContainerKey,
[UmbracoObjectTypes.DocumentTypeContainer],
@@ -395,13 +400,14 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
0,
10,
false,
out long totalRecords);
out long totalRecords,
ordering: ordering);
Assert.AreEqual(3, totalRecords);
Assert.AreEqual(3, children.Count());
Assert.IsTrue(children.Single(x => x.Key == _documentTypeSubContainer1Key).HasChildren); // Has a single folder as a child.
Assert.IsTrue(children.Single(x => x.Key == _documentTypeSubContainer2Key).HasChildren); // Has a single document type as a child.
Assert.IsFalse(children.Single(x => x.Key == _documentType1Key).HasChildren); // Is a document type (has no children).
Assert.IsFalse(children.Single(x => x.Key == _documentType1Key).HasChildren); // Is a document type (has no children).
}
[Test]
@@ -934,11 +940,11 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_ReturnsExpectedSiblings()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
var target = children[1];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 1, out long totalBefore, out long totalAfter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 1, out long totalBefore, out long totalAfter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(7, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -950,13 +956,13 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_SkipsTrashedEntities()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
var trash = children[1];
ContentService.MoveToRecycleBin(trash);
var target = children[2];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 1, out long totalBefore, out long totalAfter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 1, out long totalBefore, out long totalAfter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(6, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -969,7 +975,7 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_SkipsFilteredEntities_UsingFilterWithSet()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
// Apply a filter that excludes the child at index 1. We'd expect to not get this, but
// get still get one previous sibling, i.e. the entity at index 0.
@@ -977,7 +983,7 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
IQuery<IUmbracoEntity> filter = ScopeProvider.CreateQuery<IUmbracoEntity>().Where(x => !keysToExclude.Contains(x.Key));
var target = children[2];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 1, out long totalBefore, out long totalAfter, filter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 1, out long totalBefore, out long totalAfter, filter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(6, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -990,7 +996,7 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_SkipsFilteredEntities_UsingFilterWithoutSet()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
// Apply a filter that excludes the child at index 1. We'd expect to not get this, but
// get still get one previous sibling, i.e. the entity at index 0.
@@ -998,7 +1004,7 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
IQuery<IUmbracoEntity> filter = ScopeProvider.CreateQuery<IUmbracoEntity>().Where(x => x.Key != keyToExclude);
var target = children[2];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 1, out long totalBefore, out long totalAfter, filter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 1, out long totalBefore, out long totalAfter, filter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(6, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -1011,13 +1017,13 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_RespectsOrdering()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
// Order the children by name to ensure the ordering works when differing from the default sort order, the name is a GUID.
children = children.OrderBy(x => x.Name).ToList();
var target = children[1];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 1, out long totalBefore, out long totalAfter, ordering: Ordering.By(nameof(NodeDto.Text))).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 1, out long totalBefore, out long totalAfter, ordering: Ordering.By(nameof(NodeDto.Text))).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(7, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -1029,10 +1035,10 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_IgnoresOutOfBoundsLower()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
var target = children[1];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 100, 1, out long totalBefore, out long totalAfter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 100, 1, out long totalBefore, out long totalAfter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(7, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -1044,10 +1050,10 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
[Test]
public void EntityService_Siblings_IgnoresOutOfBoundsUpper()
{
var children = CreateSiblingsTestData();
var children = CreateDocumentSiblingsTestData();
var target = children[^2];
var result = EntityService.GetSiblings(target.Key, UmbracoObjectTypes.Document, 1, 100, out long totalBefore, out long totalAfter).ToArray();
var result = EntityService.GetSiblings(target.Key, [UmbracoObjectTypes.Document], 1, 100, out long totalBefore, out long totalAfter).ToArray();
Assert.AreEqual(7, totalBefore);
Assert.AreEqual(0, totalAfter);
Assert.AreEqual(3, result.Length);
@@ -1056,7 +1062,33 @@ internal sealed class EntityServiceTests : UmbracoIntegrationTest
Assert.IsTrue(result[^3].Key == children[^3].Key);
}
private List<Content> CreateSiblingsTestData()
[TestCase(true)]
[TestCase(false)]
public async Task EntityService_Siblings_FiltersByObjectTypes(bool foldersOnly)
{
// For testing these scenarios we can use the data setup in CreateStructureForPagedDocumentTypeChildrenTest.
var objectTypes = new List<UmbracoObjectTypes> { UmbracoObjectTypes.DocumentTypeContainer };
if (foldersOnly is false)
{
objectTypes.Add(UmbracoObjectTypes.DocumentType);
}
var result = EntityService.GetSiblings(_documentTypeSubContainer2Key, objectTypes, 1, 1, out long totalBefore, out long totalAfter).ToArray();
Assert.AreEqual(0, totalBefore);
Assert.AreEqual(0, totalAfter);
var expectedCount = foldersOnly ? 2 : 3;
Assert.AreEqual(expectedCount, result.Length);
Assert.IsTrue(result[0].Key == _documentTypeSubContainer1Key);
Assert.IsTrue(result[1].Key == _documentTypeSubContainer2Key);
if (foldersOnly is false)
{
Assert.IsTrue(result[2].Key == _documentType1Key);
}
}
private List<Content> CreateDocumentSiblingsTestData()
{
var contentType = ContentTypeService.Get("umbTextpage");

View File

@@ -11,7 +11,7 @@
},
"Tests": {
"Database": {
"DatabaseType": "SQLite",
"DatabaseType": "SQLite", // "SQLite", "LocalDb"
"PrepareThreadCount": 4,
"SchemaDatabaseCount": 4,
"EmptyDatabasesCount": 2,

View File

@@ -23,7 +23,7 @@ public class EntityServiceTests
if (shouldThrow)
{
Assert.Throws<ArgumentOutOfRangeException>(() => sut.GetSiblings(Guid.NewGuid(), UmbracoObjectTypes.Document, before, after, out _, out _));
Assert.Throws<ArgumentOutOfRangeException>(() => sut.GetSiblings(Guid.NewGuid(), [UmbracoObjectTypes.Document], before, after, out _, out _));
}
}