V14: allowed children endpoints (#14434)

* Implement allowed children of root

* Allow children by key controller

* Update to be document controlller instead

* Fix AllowedChildrenOfRootDocumentController

* Create GetAllAsync method in ContentTypeServiceBaseOfTRepositoryTItemTService

* Revert "Create GetAllAsync method in ContentTypeServiceBaseOfTRepositoryTItemTService"

This reverts commit b01b5b924099e58bb53246e4b4ba5fa15358d0cd.

* Implement attemp pattern in IContentTypeService

* Create IContentCreatingService

* Use new contentCreatingService in controller

* Revert if statement

* Use total from attempt

* Throw exceptions instead of returning attempt

* Wrap in scope

* Rename to GetAllowedChildrenContentTypesAsync

* Fix summary

* Removed unneccessary await Task.FromResult

---------

Co-authored-by: Zeegaan <nge@umbraco.dk>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Nikolaj Geisle
2023-06-29 09:38:05 +02:00
committed by GitHub
parent 85ad419cb7
commit d5a0dda89a
11 changed files with 224 additions and 3 deletions

View File

@@ -43,4 +43,11 @@ public class ContentControllerBase : ManagementApiControllerBase
ContentEditingOperationStatus.Unknown => StatusCode(StatusCodes.Status500InternalServerError, "Unknown error. Please see the log for more details."),
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown content operation status.")
};
protected IActionResult ContentCreatingOperationStatusResult(ContentCreatingOperationStatus status) =>
status switch
{
ContentCreatingOperationStatus.NotFound => NotFound("The content type could not be found"),
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown content operation status."),
};
}

View File

@@ -0,0 +1,48 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
public class AllowedChildrenByKeyDocumentController : DocumentControllerBase
{
private readonly IUmbracoMapper _umbracoMapper;
private readonly IContentCreatingService _contentCreatingService;
public AllowedChildrenByKeyDocumentController(IUmbracoMapper umbracoMapper, IContentCreatingService contentCreatingService)
{
_umbracoMapper = umbracoMapper;
_contentCreatingService = contentCreatingService;
}
[HttpGet("{id:guid}/allowed-document-types")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> AllowedChildrenByKey(Guid id, int skip = 0, int take = 100)
{
Attempt<PagedModel<IContentType>?, ContentCreatingOperationStatus> allowedChildrenAttempt = await _contentCreatingService.GetAllowedChildrenContentTypesAsync(id, skip, take);
if (allowedChildrenAttempt.Success is false)
{
return ContentCreatingOperationStatusResult(allowedChildrenAttempt.Status);
}
List<DocumentTypeResponseModel> viewModels = _umbracoMapper.MapEnumerable<IContentType, DocumentTypeResponseModel>(allowedChildrenAttempt.Result!.Items);
var pagedViewModel = new PagedViewModel<DocumentTypeResponseModel>
{
Total = allowedChildrenAttempt.Result.Total,
Items = viewModels,
};
return Ok(pagedViewModel);
}
}

View File

@@ -0,0 +1,41 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.Document;
public class AllowedChildrenOfRootDocumentController : DocumentControllerBase
{
private readonly IContentTypeService _contentTypeService;
private readonly IUmbracoMapper _umbracoMapper;
public AllowedChildrenOfRootDocumentController(IContentTypeService contentTypeService, IUmbracoMapper umbracoMapper)
{
_contentTypeService = contentTypeService;
_umbracoMapper = umbracoMapper;
}
[HttpGet("root/allowed-document-types")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> AllowedChildrenOfRoot(int skip = 0, int take = 100)
{
PagedModel<IContentType> allowedChildrenOfRoot = await _contentTypeService.GetAllAllowedAsRootAsync(skip, take);
List<DocumentTypeResponseModel> viewModels = _umbracoMapper.MapEnumerable<IContentType, DocumentTypeResponseModel>(allowedChildrenOfRoot.Items);
var pagedViewModel = new PagedViewModel<DocumentTypeResponseModel>
{
Total = allowedChildrenOfRoot.Total,
Items = viewModels,
};
return await Task.FromResult(Ok(pagedViewModel));
}
}

View File

@@ -1,5 +1,4 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Content;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;

View File

@@ -298,6 +298,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddUnique<ITagService, TagService>();
Services.AddUnique<IContentService, ContentService>();
Services.AddUnique<IContentEditingService, ContentEditingService>();
Services.AddUnique<IContentCreatingService, ContentCreatingService>();
Services.AddUnique<IContentVersionCleanupPolicy, DefaultContentVersionCleanupPolicy>();
Services.AddUnique<IMemberService, MemberService>();
Services.AddUnique<IMediaService, MediaService>();

View File

@@ -0,0 +1,52 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
public class ContentCreatingService : IContentCreatingService
{
private readonly IContentTypeService _contentTypeService;
private readonly IContentService _contentService;
private readonly IEntityService _entityService;
private readonly ICoreScopeProvider _coreScopeProvider;
public ContentCreatingService(IContentTypeService contentTypeService, IContentService contentService, IEntityService entityService, ICoreScopeProvider coreScopeProvider)
{
_contentTypeService = contentTypeService;
_contentService = contentService;
_entityService = entityService;
_coreScopeProvider = coreScopeProvider;
}
public async Task<Attempt<PagedModel<IContentType>?, ContentCreatingOperationStatus>> GetAllowedChildrenContentTypesAsync(Guid key, int skip, int take)
{
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
IContent? content = _contentService.GetById(key);
if (content is null)
{
return Attempt.FailWithStatus<PagedModel<IContentType>?, ContentCreatingOperationStatus>(ContentCreatingOperationStatus.NotFound, null);
}
// FIXME: When content gets a ContentTypeKey property, we no longer have to get the key from the entityService
Attempt<Guid> contentTypeKeyAttempt = _entityService.GetKey(content.ContentTypeId, UmbracoObjectTypes.DocumentType);
if (contentTypeKeyAttempt.Success is false)
{
// This should never happen, content shouldn't be able to exists without a document type
throw new InvalidOperationException("The document type could not be found.");
}
Attempt<PagedModel<IContentType>?, ContentTypeOperationStatus> contentTypeAttempt = await _contentTypeService.GetAllowedChildrenAsync(contentTypeKeyAttempt.Result, skip, take);
if (contentTypeAttempt.Success is false)
{
throw new InvalidOperationException("The document type could not be found.");
}
scope.Complete();
return Attempt.SucceedWithStatus(ContentCreatingOperationStatus.Success, contentTypeAttempt.Result);
}
}

View File

@@ -2,9 +2,11 @@ using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.Changes;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
@@ -82,6 +84,47 @@ public class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository,
}
}
/// <inheritdoc />
public Task<PagedModel<IContentType>> GetAllAllowedAsRootAsync(int skip, int take)
{
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
// that one is special because it works across content, media and member types
scope.ReadLock(Constants.Locks.ContentTypes, Constants.Locks.MediaTypes, Constants.Locks.MemberTypes);
IQuery<IContentType> query = ScopeProvider.CreateQuery<IContentType>().Where(x => x.AllowedAsRoot);
IEnumerable<IContentType> contentTypes = Repository.Get(query).ToArray();
var pagedModel = new PagedModel<IContentType>
{
Total = contentTypes.Count(),
Items = contentTypes.Skip(skip).Take(take)
};
return Task.FromResult(pagedModel);
}
public Task<Attempt<PagedModel<IContentType>?, ContentTypeOperationStatus>> GetAllowedChildrenAsync(Guid key, int skip, int take)
{
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
IContentType? parent = Get(key);
if (parent?.AllowedContentTypes is null)
{
return Task.FromResult(Attempt.FailWithStatus<PagedModel<IContentType>?, ContentTypeOperationStatus>(ContentTypeOperationStatus.NotFound, null));
}
IContentType[] allowedChildren = GetAll(parent.AllowedContentTypes.Select(x => x.Key)).ToArray();
var result = new PagedModel<IContentType>
{
Items = allowedChildren.Take(take).Skip(skip),
Total = allowedChildren.Length,
};
return Task.FromResult(Attempt.SucceedWithStatus<PagedModel<IContentType>?, ContentTypeOperationStatus>(ContentTypeOperationStatus.Success, result));
}
protected override void DeleteItemsOfTypes(IEnumerable<int> typeIds)
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope())

View File

@@ -0,0 +1,9 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
public interface IContentCreatingService
{
Task<Attempt<PagedModel<IContentType>?, ContentCreatingOperationStatus>> GetAllowedChildrenContentTypesAsync(Guid key, int skip, int take);
}

View File

@@ -1,4 +1,5 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
@@ -29,4 +30,16 @@ public interface IContentTypeService : IContentTypeBaseService<IContentType>
/// <param name="aliases"></param>
/// <returns></returns>
IEnumerable<int> GetAllContentTypeIds(string[] aliases);
/// <summary>
/// Returns all the content type allowed as root.
/// </summary>
/// <returns></returns>
Task<PagedModel<IContentType>> GetAllAllowedAsRootAsync(int skip, int take);
/// <summary>
/// Returns all content types allowed as children for a given content type key.
/// </summary>
/// <returns></returns>
Task<Attempt<PagedModel<IContentType>?, ContentTypeOperationStatus>> GetAllowedChildrenAsync(Guid key, int skip, int take);
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Cms.Core.Services.OperationStatus;
public enum ContentCreatingOperationStatus
{
Success,
NotFound
}

View File

@@ -6,5 +6,6 @@ public enum ContentTypeOperationStatus
DuplicateAlias,
InvalidAlias,
InvalidPropertyTypeAlias,
InvalidDataType
InvalidDataType,
NotFound
}