V14: Additional blueprint endpoints (#16047)

* Fix /item endpoint response when 0 ids

* Adding GetPagedByContentTypeKeyAsync to IContentBlueprintEditingService

* Adding mapping for new DocumentTypeBlueprintItemResponseModel

* New endpoint and operation status handling

* Fix test cases

* Added tests

* Fixed partial test references

* Rename method

* Add ancestors endpoint for blueprints

* Change == to is

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Elitsa Marinovska
2024-04-15 13:49:08 +02:00
committed by GitHub
parent 2a889f502b
commit 3edc783ddb
12 changed files with 196 additions and 2 deletions

View File

@@ -29,6 +29,11 @@ public class ItemDocumentBlueprintController : DocumentBlueprintItemControllerBa
CancellationToken cancellationToken,
[FromQuery(Name = "id")] HashSet<Guid> ids)
{
if (ids.Count is 0)
{
return Ok(Enumerable.Empty<DocumentBlueprintItemResponseModel>());
}
IEnumerable<IDocumentEntitySlim> documents = _entityService
.GetAll(UmbracoObjectTypes.DocumentBlueprint, ids.ToArray())
.Select(x => x as IDocumentEntitySlim)

View File

@@ -0,0 +1,23 @@
using Asp.Versioning;
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;
[ApiVersion("1.0")]
public class AncestorsDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
{
public AncestorsDocumentBlueprintTreeController(IEntityService entityService, IDocumentPresentationFactory documentPresentationFactory)
: base(entityService, documentPresentationFactory)
{
}
[HttpGet("ancestors")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<DocumentBlueprintTreeItemResponseModel>), StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<DocumentBlueprintTreeItemResponseModel>>> Ancestors(CancellationToken cancellationToken, Guid descendantId)
=> await GetAncestors(descendantId);
}

View File

@@ -0,0 +1,56 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
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;
using Umbraco.Cms.Web.Common.Authorization;
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;
[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.SectionAccessContent)]
public class DocumentBlueprintForDocumentTypeController : DocumentTypeControllerBase
{
private readonly IContentBlueprintEditingService _contentBlueprintEditingService;
private readonly IUmbracoMapper _umbracoMapper;
public DocumentBlueprintForDocumentTypeController(IContentBlueprintEditingService contentBlueprintEditingService, IUmbracoMapper umbracoMapper)
{
_contentBlueprintEditingService = contentBlueprintEditingService;
_umbracoMapper = umbracoMapper;
}
[HttpGet("{id:guid}/blueprint")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeBlueprintItemResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> DocumentBlueprintByDocumentTypeKey(
CancellationToken cancellationToken,
Guid id,
int skip = 0,
int take = 100)
{
Attempt<PagedModel<IContent>?, ContentEditingOperationStatus> attempt = await _contentBlueprintEditingService.GetPagedByContentTypeAsync(id, skip, take);
if (attempt.Success is false)
{
return ContentEditingOperationStatusResult(attempt.Status);
}
List<DocumentTypeBlueprintItemResponseModel> viewModels = _umbracoMapper.MapEnumerable<IContent, DocumentTypeBlueprintItemResponseModel>(attempt.Result!.Items);
var pagedViewModel = new PagedViewModel<DocumentTypeBlueprintItemResponseModel>
{
Total = attempt.Result!.Total,
Items = viewModels,
};
return Ok(pagedViewModel);
}
}

View File

@@ -107,4 +107,15 @@ public abstract class DocumentTypeControllerBase : ManagementApiControllerBase
.Build()),
_ => new ObjectResult("Unknown content type structure operation status") { StatusCode = StatusCodes.Status500InternalServerError }
});
protected IActionResult ContentEditingOperationStatusResult(ContentEditingOperationStatus status) =>
OperationStatusResult(status, problemDetailsBuilder => status switch
{
ContentEditingOperationStatus.ContentTypeNotFound => NotFound(problemDetailsBuilder
.WithTitle("The specified document type was not found")
.Build()),
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
.WithTitle("Unknown content editing operation status")
.Build()),
});
}

View File

@@ -19,6 +19,7 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType,
mapper.Define<ISimpleContentType, DocumentTypeCollectionReferenceResponseModel>((_, _) => new DocumentTypeCollectionReferenceResponseModel(), Map);
mapper.Define<IContentEntitySlim, DocumentTypeReferenceResponseModel>((_, _) => new DocumentTypeReferenceResponseModel(), Map);
mapper.Define<IDocumentEntitySlim, DocumentTypeReferenceResponseModel>((_, _) => new DocumentTypeReferenceResponseModel(), Map);
mapper.Define<IContent, DocumentTypeBlueprintItemResponseModel>((_, _) => new DocumentTypeBlueprintItemResponseModel(), Map);
}
// Umbraco.Code.MapAll
@@ -114,4 +115,11 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType,
target.Alias = source.Alias;
target.Icon = source.Icon ?? string.Empty;
}
// Umbraco.Code.MapAll
private void Map(IContent source, DocumentTypeBlueprintItemResponseModel target, MapperContext context)
{
target.Id = source.Key;
target.Name = source.Name ?? string.Empty;
}
}

View File

@@ -0,0 +1,7 @@
using Umbraco.Cms.Api.Management.ViewModels.Item;
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
public class DocumentTypeBlueprintItemResponseModel : NamedItemResponseModelBase
{
}

View File

@@ -31,6 +31,25 @@ internal sealed class ContentBlueprintEditingService
return await Task.FromResult(blueprint);
}
public async Task<Attempt<PagedModel<IContent>?, ContentEditingOperationStatus>> GetPagedByContentTypeAsync(Guid contentTypeKey, int skip, int take)
{
IContentType? contentType = await ContentTypeService.GetAsync(contentTypeKey);
if (contentType is null)
{
return Attempt.FailWithStatus<PagedModel<IContent>?, ContentEditingOperationStatus>(ContentEditingOperationStatus.ContentTypeNotFound, null);
}
IContent[] blueprints = ContentService.GetBlueprintsForContentTypes([contentType.Id]).ToArray();
var result = new PagedModel<IContent>
{
Items = blueprints.Skip(skip).Take(take),
Total = blueprints.Length,
};
return Attempt.SucceedWithStatus<PagedModel<IContent>?, ContentEditingOperationStatus>(ContentEditingOperationStatus.Success, result);
}
public async Task<Attempt<ContentCreateResult, ContentEditingOperationStatus>> CreateAsync(ContentBlueprintCreateModel createModel, Guid userKey)
{
if (await ValidateCulturesAsync(createModel) is false)

View File

@@ -8,6 +8,11 @@ public interface IContentBlueprintEditingService
{
Task<IContent?> GetAsync(Guid key);
Task<Attempt<PagedModel<IContent>?, ContentEditingOperationStatus>> GetPagedByContentTypeAsync(
Guid contentTypeKey,
int skip,
int take);
Task<Attempt<ContentCreateResult, ContentEditingOperationStatus>> CreateAsync(ContentBlueprintCreateModel createModel, Guid userKey);
Task<Attempt<ContentCreateResult, ContentEditingOperationStatus>> CreateFromContentAsync(Guid contentKey, string name, Guid? key, Guid userKey);

View File

@@ -5,7 +5,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
public partial class ContentBlueprintEditingServiceTests
{
[TestCase(true)]
[TestCase(true)]
[TestCase(false)]
public async Task Can_Get(bool variant)
{
var blueprint = await (variant ? CreateVariantContentBlueprint() : CreateInvariantContentBlueprint());

View File

@@ -0,0 +1,54 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
public partial class ContentBlueprintEditingServiceTests
{
[Test]
public async Task Can_Get_Paged()
{
var contentType = CreateInvariantContentType();
for (var i = 1; i < 6; i++)
{
var createModel = new ContentBlueprintCreateModel
{
ContentTypeKey = contentType.Key,
InvariantName = $"Blueprint {i}",
};
await ContentBlueprintEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
}
var result = await ContentBlueprintEditingService.GetPagedByContentTypeAsync(contentType.Key, 0, 2);
var pagedResult = result.Result;
Assert.Multiple(() =>
{
Assert.IsTrue(result.Success);
Assert.AreEqual(ContentEditingOperationStatus.Success, result.Status);
Assert.IsNotNull(pagedResult);
});
Assert.Multiple(() =>
{
Assert.AreEqual(2, pagedResult.Items.Count());
Assert.AreEqual(5, pagedResult.Total);
});
}
[Test]
public async Task Cannot_Get_Paged_With_Non_Existing_Content_Type()
{
var result = await ContentBlueprintEditingService.GetPagedByContentTypeAsync(Guid.NewGuid(), 0, 10);
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.AreEqual(ContentEditingOperationStatus.ContentTypeNotFound, result.Status);
Assert.IsNull(result.Result);
});
}
}

View File

@@ -5,7 +5,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
public partial class ContentEditingServiceTests
{
[TestCase(true)]
[TestCase(true)]
[TestCase(false)]
public async Task Can_Get(bool variant)
{
var content = await (variant ? CreateVariantContent() : CreateInvariantContent());

View File

@@ -139,6 +139,12 @@
<Compile Update="Umbraco.Infrastructure\Services\ContentBlueprintEditingServiceTests.Get.cs">
<DependentUpon>ContentBlueprintEditingServiceTests.cs</DependentUpon>
</Compile>
<Compile Update="Umbraco.Infrastructure\Services\ContentBlueprintEditingServiceTests.GetPagedByContentTypeKey.cs">
<DependentUpon>ContentBlueprintEditingServiceTests.cs</DependentUpon>
</Compile>
<Compile Update="Umbraco.Infrastructure\Services\ContentBlueprintEditingServiceTests.Move.cs">
<DependentUpon>ContentBlueprintEditingServiceTests.cs</DependentUpon>
</Compile>
<Compile Update="Umbraco.Infrastructure\Services\ContentBlueprintEditingServiceTests.Update.cs">
<DependentUpon>ContentBlueprintEditingServiceTests.cs</DependentUpon>
</Compile>