Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V14: Additional blueprint endpoints #16047

Merged
merged 11 commits into from
Apr 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public async Task<ActionResult> Item(
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,15 @@ status is ContentTypeStructureOperationStatus.Success
.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()),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public void DefineMaps(IUmbracoMapper mapper)
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
Expand Down Expand Up @@ -114,4 +115,11 @@ private void Map(ISimpleContentType source, DocumentTypeCollectionReferenceRespo
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Umbraco.Cms.Api.Management.ViewModels.Item;

namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;

public class DocumentTypeBlueprintItemResponseModel : NamedItemResponseModelBase
{
}
19 changes: 19 additions & 0 deletions src/Umbraco.Core/Services/ContentBlueprintEditingService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;

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

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v14/dev)

ℹ Getting worse: Primitive Obsession

The ratio of primitive types in function arguments increases from 57.78% to 60.42%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
Expand Down Expand Up @@ -31,6 +31,25 @@
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)
Expand Down
5 changes: 5 additions & 0 deletions src/Umbraco.Core/Services/IContentBlueprintEditingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading