Added user start node restrictions to sibling endpoints (#19839)

* Added user start node restrictions to sibling endpoints.

* Further integration tests.

* Tidy up.

* Apply suggestions from code review

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

* Revert previous update.

* Applied previous update correctly.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Andy Butland
2025-08-05 09:53:39 +02:00
committed by GitHub
parent f5ff2bbf59
commit 20254f0bbc
17 changed files with 492 additions and 51 deletions

View File

@@ -146,7 +146,13 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend
}
/// <inheritdoc/>
public IEnumerable<IEntitySlim> GetSiblings(Guid objectType, Guid targetKey, int before, int after, Ordering ordering)
public IEnumerable<IEntitySlim> GetSiblings(
Guid objectType,
Guid targetKey,
int before,
int after,
IQuery<IUmbracoEntity>? filter,
Ordering ordering)
{
// Ideally we don't want to have to do a second query for the parent ID, but the siblings query is already messy enough
// without us also having to do a nested query for the parent ID too.
@@ -167,6 +173,23 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend
.From<NodeDto>()
.Where<NodeDto>(x => x.ParentId == parentId && x.Trashed == false);
// Apply the filter if provided. Note that in doing this, we'll add more parameters to the query, so need to track
// how many so we can offset the parameter indexes for the "before" and "after" values added later.
int beforeAfterParameterIndexOffset = 0;
if (filter != null)
{
foreach (Tuple<string, object[]> filterClause in filter.GetWhereClauses())
{
rowNumberSql.Where(filterClause.Item1, filterClause.Item2);
// We need to offset by one for each non-array parameter in the filter clause.
// If a query is created using Contains or some other set based operation, we'll get both the array and the
// items in the array provided in the where clauses. It's only the latter that count for applying parameters
// to the SQL statement, and hence we should only offset by them.
beforeAfterParameterIndexOffset += filterClause.Item2.Count(x => !x.GetType().IsArray);
}
}
// Find the specific row number of the target node.
// We need this to determine the bounds of the row numbers to select.
Sql<ISqlContext> targetRowSql = Sql()
@@ -180,11 +203,12 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend
IEnumerable<object> afterArguments = targetRowSql.Arguments.Concat([after]);
// Select the UniqueId of nodes which row number is within the specified range of the target node's row number.
const int BeforeAfterParameterIndex = 3;
Sql<ISqlContext>? mainSql = Sql()
.Select("UniqueId")
.From().AppendSubQuery(rowNumberSql, "NumberedNodes")
.Where($"rn >= ({targetRowSql.SQL}) - @3", beforeArguments.ToArray())
.Where($"rn <= ({targetRowSql.SQL}) + @3", afterArguments.ToArray())
.Where($"rn >= ({targetRowSql.SQL}) - @{BeforeAfterParameterIndex + beforeAfterParameterIndexOffset}", beforeArguments.ToArray())
.Where($"rn <= ({targetRowSql.SQL}) + @{BeforeAfterParameterIndex + beforeAfterParameterIndexOffset}", afterArguments.ToArray())
.OrderBy("rn");
List<Guid>? keys = Database.Fetch<Guid>(mainSql);