Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.DataType.Tree;

public class SiblingsDataTypeTreeController : DataTypeTreeControllerBase
{
public SiblingsDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
: base(entityService, dataTypeService)
{
}

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<DataTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<DataTypeTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after)
=> GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.Document.Tree;

[ApiVersion("1.0")]
public class SiblingsDocumentTreeController : DocumentTreeControllerBase
{
public SiblingsDocumentTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
IPublicAccessService publicAccessService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IDocumentPresentationFactory documentPresentationFactory)
: base(
entityService,
userStartNodeEntitiesService,
dataTypeService,
publicAccessService,
appCaches,
backofficeSecurityAccessor,
documentPresentationFactory)
{
}

Check warning on line 33 in src/Umbraco.Cms.Api.Management/Controllers/Document/Tree/SiblingsDocumentTreeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Constructor Over-Injection

SiblingsDocumentTreeController has 7 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.

[HttpGet("siblings")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<DocumentTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after)
=> GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentBlueprint.Tree;

public class SiblingsDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
public SiblingsDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<DocumentBlueprintTreeItemResponseModel>>> Siblings(
CancellationToken cancellationToken,
Guid target,
int before,
int after) =>
GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentType.Tree;

public class SiblingsDocumentTypeTreeController : DocumentTypeTreeControllerBase
{
public SiblingsDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
: base(entityService, contentTypeService)
{
}

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<DocumentTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<DocumentTypeTreeItemResponseModel>>> Siblings(
CancellationToken cancellationToken,
Guid target,
int before,
int after) =>
GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Services.Entities;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.Media.Tree;

public class SiblingsMediaTreeController : MediaTreeControllerBase
{
public SiblingsMediaTreeController(
IEntityService entityService,
IUserStartNodeEntitiesService userStartNodeEntitiesService,
IDataTypeService dataTypeService,
AppCaches appCaches,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor, mediaPresentationFactory)
{
}

Check warning on line 23 in src/Umbraco.Cms.Api.Management/Controllers/Media/Tree/SiblingsMediaTreeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Constructor Over-Injection

SiblingsMediaTreeController has 6 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<MediaTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<MediaTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after)
=> GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;

public class SiblingsMediaTypeTreeController : MediaTypeTreeControllerBase
{
public SiblingsMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
: base(entityService, mediaTypeService)
{
}

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<MediaTypeTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<MediaTypeTreeItemResponseModel>>> Siblings(CancellationToken cancellationToken, Guid target, int before, int after)
=> GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Controllers.Template.Tree;

public class SiblingsTemplateTreeController : TemplateTreeControllerBase
{
public SiblingsTemplateTreeController(IEntityService entityService)
: base(entityService)
{
}

[HttpGet("siblings")]
[ProducesResponseType(typeof(IEnumerable<NamedEntityTreeItemResponseModel>), StatusCodes.Status200OK)]
public Task<ActionResult<IEnumerable<NamedEntityTreeItemResponseModel>>> Siblings(
CancellationToken cancellationToken,
Guid target,
int before,
int after) =>
GetSiblings(target, before, after);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Tree;
Expand Down Expand Up @@ -44,6 +44,23 @@ protected Task<ActionResult<PagedViewModel<TItem>>> GetChildren(Guid parentId, i
return Task.FromResult<ActionResult<PagedViewModel<TItem>>>(Ok(result));
}

protected Task<ActionResult<IEnumerable<TItem>>> GetSiblings(Guid target, int before, int after)
{
IEntitySlim[] siblings = EntityService.GetSiblings(target, ItemObjectType, before, after, ItemOrdering).ToArray();
if (siblings.Length == 0)
{
return Task.FromResult<ActionResult<IEnumerable<TItem>>>(NotFound());
}

IEntitySlim? entity = siblings.FirstOrDefault();
Guid? parentKey = entity?.ParentId > 0
? EntityService.GetKey(entity.ParentId, ItemObjectType).Result
: Constants.System.RootKey;

TItem[] treeItemsViewModels = MapTreeItemViewModels(parentKey, siblings);
return Task.FromResult<ActionResult<IEnumerable<TItem>>>(Ok(treeItemsViewModels));
}

protected virtual async Task<ActionResult<IEnumerable<TItem>>> GetAncestors(Guid descendantKey, bool includeSelf = true)
{
IEntitySlim[] ancestorEntities = await GetAncestorEntitiesAsync(descendantKey, includeSelf);
Expand Down
11 changes: 11 additions & 0 deletions src/Umbraco.Core/Persistence/Repositories/IEntityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ public interface IEntityRepository : IRepository

IEnumerable<IEntitySlim> GetAll(Guid objectType, params Guid[] keys);

/// <summary>
/// Gets sibling entities of a specified target entity, within a given range before and after the target, ordered as specified.
/// </summary>
/// <param name="objectType">The object type key of the entities.</param>
/// <param name="targetKey">The key of the target entity whose siblings are to be retrieved.</param>
/// <param name="before">The number of siblings to retrieve before the target entity.</param>
/// <param name="after">The number of siblings to retrieve after the target entity.</param>
/// <param name="ordering">The ordering to apply to the siblings.</param>
/// <returns>Enumerable of sibling entities.</returns>
IEnumerable<IEntitySlim> GetSiblings(Guid objectType, Guid targetKey, int before, int after, Ordering ordering) => [];

/// <summary>
/// Gets entities for a query
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions src/Umbraco.Core/Services/EntityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,39 @@ public IEnumerable<IEntitySlim> GetChildren(Guid? key, UmbracoObjectTypes object
return children;
}

/// <inheritdoc />
public IEnumerable<IEntitySlim> GetSiblings(
Guid key,
UmbracoObjectTypes objectType,
int before,
int after,
Ordering? ordering = null)
{
if (before < 0)
{
throw new ArgumentOutOfRangeException(nameof(before), "The 'before' parameter must be greater than or equal to 0.");
}

if (after < 0)
{
throw new ArgumentOutOfRangeException(nameof(after), "The 'after' parameter must be greater than or equal to 0.");
}

ordering ??= new Ordering("sortOrder");

using ICoreScope scope = ScopeProvider.CreateCoreScope();

IEnumerable<IEntitySlim> siblings = _entityRepository.GetSiblings(
objectType.GetGuid(),
key,
before,
after,
ordering);

scope.Complete();
return siblings;
}

/// <inheritdoc />
public virtual IEnumerable<IEntitySlim> GetDescendants(int id)
{
Expand Down
16 changes: 16 additions & 0 deletions src/Umbraco.Core/Services/IEntityService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Models;

Check notice on line 1 in src/Umbraco.Core/Services/IEntityService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

✅ Getting better: Missing Arguments Abstractions

The average number of function arguments decreases from 5.11 to 5.10, threshold = 4.00. The functions in this file have too many arguments, indicating a lack of encapsulation or too many responsibilities in the same functions. Avoid adding more.
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence.Querying;

Expand Down Expand Up @@ -170,6 +170,22 @@
/// <param name="objectType">The object type of the parent.</param>
IEntitySlim? GetParent(int id, UmbracoObjectTypes objectType);

/// <summary>
/// Gets sibling entities of a specified target entity, within a given range before and after the target, ordered as specified.
/// </summary>
/// <param name="key">The key of the target entity whose siblings are to be retrieved.</param>
/// <param name="objectType">The object type key of the entities.</param>
/// <param name="before">The number of siblings to retrieve before the target entity. Needs to be greater or equal to 0.</param>
/// <param name="after">The number of siblings to retrieve after the target entity. Needs to be greater or equal to 0.</param>
/// <param name="ordering">The ordering to apply to the siblings.</param>
/// <returns>Enumerable of sibling entities.</returns>
IEnumerable<IEntitySlim> GetSiblings(
Guid key,
UmbracoObjectTypes objectType,
int before,
int after,
Ordering? ordering = null) => [];

/// <summary>
/// Gets the children of an entity.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,14 @@ internal static string GetAliasedField(this Sql<ISqlContext> sql, string field)

#region Utilities

public static Sql<ISqlContext> AppendSubQuery(this Sql<ISqlContext> sql, Sql<ISqlContext> subQuery, string alias)
{
// Append the subquery as a derived table with an alias
sql.Append("(").Append(subQuery.SQL, subQuery.Arguments).Append($") AS {alias}");

return sql;
}

private static string[] GetColumns<TDto>(this Sql<ISqlContext> sql, string? tableAlias = null, string? referenceName = null, Expression<Func<TDto, object?>>[]? columnExpressions = null, bool withAlias = true, bool forInsert = false)
{
PocoData? pd = sql.SqlContext.PocoDataFactory.ForType(typeof (TDto));
Expand Down
Loading