Adds ancestor ID details on document tree and collection responses (#18909)
* Populate ancestor keys on document tree response items. * Populate ancestor keys on document collection response items. * Update OpenApi.json * Use array of objects rather than Ids for the ancestor collection. * Update OpenApi.json.
This commit is contained in:
@@ -4,6 +4,7 @@ using Umbraco.Cms.Api.Management.Controllers.Tree;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.Services.Entities;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Tree;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
@@ -33,7 +34,7 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IDocumentPresentationFactory documentPresentationFactory)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_appCaches = appCaches;
|
||||
@@ -52,6 +53,8 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa
|
||||
if (entity is IDocumentEntitySlim documentEntitySlim)
|
||||
{
|
||||
responseModel.IsProtected = _publicAccessService.IsProtected(entity.Path);
|
||||
responseModel.Ancestors = EntityService.GetPathKeys(entity, omitSelf: true)
|
||||
.Select(x => new ReferenceByIdModel(x));
|
||||
responseModel.IsTrashed = entity.Trashed;
|
||||
responseModel.Id = entity.Key;
|
||||
responseModel.CreateDate = entity.CreateDate;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -9,11 +12,23 @@ namespace Umbraco.Cms.Api.Management.Factories;
|
||||
public class DocumentCollectionPresentationFactory : ContentCollectionPresentationFactory<IContent, DocumentCollectionResponseModel, DocumentValueResponseModel, DocumentVariantResponseModel>, IDocumentCollectionPresentationFactory
|
||||
{
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
[Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService)
|
||||
: base(mapper)
|
||||
: this(
|
||||
mapper,
|
||||
publicAccessService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEntityService>())
|
||||
{
|
||||
}
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService, IEntityService entityService)
|
||||
: base(mapper)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_entityService = entityService;
|
||||
}
|
||||
|
||||
protected override Task SetUnmappedProperties(ListViewPagedModel<IContent> contentCollection, List<DocumentCollectionResponseModel> collectionResponseModels)
|
||||
@@ -27,6 +42,8 @@ public class DocumentCollectionPresentationFactory : ContentCollectionPresentati
|
||||
}
|
||||
|
||||
item.IsProtected = _publicAccessService.IsProtected(matchingContentItem).Success;
|
||||
item.Ancestors = _entityService.GetPathKeys(matchingContentItem, omitSelf: true)
|
||||
.Select(x => new ReferenceByIdModel(x));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -67,7 +67,7 @@ public class DocumentMapDefinition : ContentMapDefinition<IContent, DocumentValu
|
||||
target.IsTrashed = source.Trashed;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -IsProtected
|
||||
// Umbraco.Code.MapAll -IsProtected -Ancestors
|
||||
private void Map(IContent source, DocumentCollectionResponseModel target, MapperContext context)
|
||||
{
|
||||
target.Id = source.Key;
|
||||
|
||||
@@ -37088,6 +37088,7 @@
|
||||
},
|
||||
"DocumentCollectionResponseModel": {
|
||||
"required": [
|
||||
"ancestors",
|
||||
"documentType",
|
||||
"id",
|
||||
"isProtected",
|
||||
@@ -37143,6 +37144,16 @@
|
||||
"isProtected": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ancestors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"updater": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
@@ -37456,6 +37467,7 @@
|
||||
},
|
||||
"DocumentTreeItemResponseModel": {
|
||||
"required": [
|
||||
"ancestors",
|
||||
"createDate",
|
||||
"documentType",
|
||||
"hasChildren",
|
||||
@@ -37495,6 +37507,16 @@
|
||||
"isProtected": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ancestors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ReferenceByIdModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"documentType": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -11,5 +11,7 @@ public class DocumentCollectionResponseModel : ContentCollectionResponseModelBas
|
||||
|
||||
public bool IsProtected { get; set; }
|
||||
|
||||
public IEnumerable<ReferenceByIdModel> Ancestors { get; set; } = [];
|
||||
|
||||
public string? Updater { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
@@ -8,6 +7,8 @@ public class DocumentTreeItemResponseModel : ContentTreeItemResponseModel
|
||||
{
|
||||
public bool IsProtected { get; set; }
|
||||
|
||||
public IEnumerable<ReferenceByIdModel> Ancestors { get; set; } = [];
|
||||
|
||||
public DocumentTypeReferenceResponseModel DocumentType { get; set; } = new();
|
||||
|
||||
public IEnumerable<DocumentVariantItemResponseModel> Variants { get; set; } = Enumerable.Empty<DocumentVariantItemResponseModel>();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
@@ -758,5 +759,26 @@ public class EntityService : RepositoryService, IEntityService
|
||||
return _entityRepository.GetPagedResultsByQuery(query, objectTypeGuids, pageNumber, pageSize, out totalRecords, filter, ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>>
|
||||
public Guid[] GetPathKeys(ITreeEntity entity, bool omitSelf = false)
|
||||
{
|
||||
IEnumerable<int> ids = entity.Path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => int.TryParse(x, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val) ? val : -1)
|
||||
.Where(x => x != -1);
|
||||
|
||||
Guid[] keys = ids
|
||||
.Select(x => _idKeyMap.GetKeyForId(x, UmbracoObjectTypes.Document))
|
||||
.Where(x => x.Success)
|
||||
.Select(x => x.Result)
|
||||
.ToArray();
|
||||
|
||||
if (omitSelf)
|
||||
{
|
||||
// Omit the last path key as that will be for the item itself.
|
||||
return keys.Take(keys.Length - 1).ToArray();
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,4 +382,12 @@ public interface IEntityService
|
||||
/// <returns>The identifier.</returns>
|
||||
/// <remarks>When a new content or a media is saved with the key, it will have the reserved identifier.</remarks>
|
||||
int ReserveId(Guid key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID keys for an entity's path (provided as a comma separated list of integer Ids).
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="omitSelf">A value indicating whether to omit the entity's own key from the result.</param>
|
||||
/// <returns>The path with each ID converted to a GUID.</returns>
|
||||
Guid[] GetPathKeys(ITreeEntity entity, bool omitSelf = false) => [];
|
||||
}
|
||||
|
||||
@@ -910,6 +910,27 @@ public class EntityServiceTests : UmbracoIntegrationTest
|
||||
Assert.IsFalse(EntityService.GetId(Guid.NewGuid(), UmbracoObjectTypes.DocumentType).Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EntityService_GetPathKeys_ReturnsExpectedKeys()
|
||||
{
|
||||
var contentType = ContentTypeService.Get("umbTextpage");
|
||||
|
||||
var root = ContentBuilder.CreateSimpleContent(contentType);
|
||||
ContentService.Save(root);
|
||||
|
||||
var child = ContentBuilder.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
|
||||
ContentService.Save(child);
|
||||
var grandChild = ContentBuilder.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), child);
|
||||
ContentService.Save(grandChild);
|
||||
|
||||
var result = EntityService.GetPathKeys(grandChild);
|
||||
Assert.AreEqual($"{root.Key},{child.Key},{grandChild.Key}", string.Join(",", result));
|
||||
|
||||
var result2 = EntityService.GetPathKeys(grandChild, omitSelf: true);
|
||||
Assert.AreEqual($"{root.Key},{child.Key}", string.Join(",", result2));
|
||||
|
||||
}
|
||||
|
||||
private static bool _isSetup;
|
||||
|
||||
private int _folderId;
|
||||
|
||||
Reference in New Issue
Block a user