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
Expand Up @@ -41,14 +41,17 @@ public async Task<IActionResult> All(
Attempt<PagedModel<ContentVersionMeta>?, ContentVersionOperationStatus> attempt =
await _contentVersionService.GetPagedContentVersionsAsync(documentId, culture, skip, take);

if (attempt.Success is false)
{
return MapFailure(attempt.Status);
}

var pagedViewModel = new PagedViewModel<DocumentVersionItemResponseModel>
{
Total = attempt.Result!.Total,
Items = await _documentVersionPresentationFactory.CreateMultipleAsync(attempt.Result!.Items),
};

return attempt.Success
? Ok(pagedViewModel)
: MapFailure(attempt.Status);
return Ok(pagedViewModel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,26 @@ namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
public abstract class DocumentVersionControllerBase : ManagementApiControllerBase
{
protected IActionResult MapFailure(ContentVersionOperationStatus status)
internal static IActionResult MapFailure(ContentVersionOperationStatus status)
=> OperationStatusResult(status, problemDetailsBuilder => status switch
{
ContentVersionOperationStatus.NotFound => NotFound(problemDetailsBuilder
ContentVersionOperationStatus.NotFound => new NotFoundObjectResult(problemDetailsBuilder
.WithTitle("The requested version could not be found")
.Build()),
ContentVersionOperationStatus.ContentNotFound => NotFound(problemDetailsBuilder
ContentVersionOperationStatus.ContentNotFound => new NotFoundObjectResult(problemDetailsBuilder
.WithTitle("The requested document could not be found")
.Build()),
ContentVersionOperationStatus.InvalidSkipTake => SkipTakeToPagingProblem(),
ContentVersionOperationStatus.RollBackFailed => BadRequest(problemDetailsBuilder
ContentVersionOperationStatus.RollBackFailed => new BadRequestObjectResult(problemDetailsBuilder
.WithTitle("Rollback failed")
.WithDetail("An unspecified error occurred while rolling back the requested version. Please check the logs for additional information.")),
ContentVersionOperationStatus.RollBackCanceled => BadRequest(problemDetailsBuilder
.WithDetail(
"An unspecified error occurred while rolling back the requested version. Please check the logs for additional information.")),
ContentVersionOperationStatus.RollBackCanceled => new BadRequestObjectResult(problemDetailsBuilder
.WithTitle("Request cancelled by notification")
.WithDetail("The request to roll back was cancelled by a notification handler.")
.Build()),
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
_ => new ObjectResult(problemDetailsBuilder
.WithTitle("Unknown content version operation status.")
.Build()),
.Build()) { StatusCode = StatusCodes.Status500InternalServerError },
});
}
Comment thread
kjac marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Element;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.ElementVersion;

[ApiVersion("1.0")]
public class AllElementVersionController : ElementVersionControllerBase
{
private readonly IElementVersionService _elementVersionService;
private readonly IElementVersionPresentationFactory _elementVersionPresentationFactory;

public AllElementVersionController(
IElementVersionService elementVersionService,
IElementVersionPresentationFactory elementVersionPresentationFactory)
{
_elementVersionService = elementVersionService;
_elementVersionPresentationFactory = elementVersionPresentationFactory;
}

[MapToApiVersion("1.0")]
[HttpGet]
[ProducesResponseType(typeof(PagedViewModel<ElementVersionItemResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> All(
CancellationToken cancellationToken,
[Required] Guid elementId,
string? culture,
int skip = 0,
int take = 100)
{
Attempt<PagedModel<ContentVersionMeta>?, ContentVersionOperationStatus> attempt =
await _elementVersionService.GetPagedContentVersionsAsync(elementId, culture, skip, take);

if (attempt.Success is false)
{
return MapFailure(attempt.Status);
}

var pagedViewModel = new PagedViewModel<ElementVersionItemResponseModel>
{
Total = attempt.Result!.Total,
Items = await _elementVersionPresentationFactory.CreateMultipleAsync(attempt.Result!.Items),
};

return Ok(pagedViewModel);
}

Check warning on line 56 in src/Umbraco.Cms.Api.Management/Controllers/ElementVersion/AllElementVersionController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (v17/feature/global-elements)

❌ New issue: Excess Number of Function Arguments

All has 5 arguments, max arguments = 4. This function has too many arguments, indicating a lack of encapsulation. Avoid adding more arguments.
}
Comment thread
kjac marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Element;
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.ElementVersion;

[ApiVersion("1.0")]
public class ByKeyElementVersionController : ElementVersionControllerBase
{
private readonly IElementVersionService _elementVersionService;
private readonly IUmbracoMapper _umbracoMapper;

public ByKeyElementVersionController(
IElementVersionService elementVersionService,
IUmbracoMapper umbracoMapper)
{
_elementVersionService = elementVersionService;
_umbracoMapper = umbracoMapper;
}

[MapToApiVersion("1.0")]
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(ElementVersionResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ByKey(CancellationToken cancellationToken, Guid id)
{
Attempt<IElement?, ContentVersionOperationStatus> attempt =
await _elementVersionService.GetAsync(id);

return attempt.Success
? Ok(_umbracoMapper.Map<ElementVersionResponseModel>(attempt.Result))
: MapFailure(attempt.Status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.DocumentVersion;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.ElementVersion;

[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Element}-version")]
[ApiExplorerSettings(GroupName = $"{nameof(Constants.UdiEntityType.Element)} Version")]
public abstract class ElementVersionControllerBase : ManagementApiControllerBase
{
protected IActionResult MapFailure(ContentVersionOperationStatus status)
=> DocumentVersionControllerBase.MapFailure(status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.ElementVersion;

[ApiVersion("1.0")]
public class RollbackElementVersionController : ElementVersionControllerBase
{
private readonly IElementVersionService _elementVersionService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public RollbackElementVersionController(
IElementVersionService elementVersionService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_elementVersionService = elementVersionService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

[MapToApiVersion("1.0")]
[HttpPost("{id:guid}/rollback")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Rollback(CancellationToken cancellationToken, Guid id, string? culture)
{
Attempt<IElement?, ContentVersionOperationStatus> getContentAttempt = await _elementVersionService.GetAsync(id);
if (getContentAttempt.Success is false || getContentAttempt.Result is null)
{
return MapFailure(getContentAttempt.Status);
}


// TODO ELEMENTS: handle auth
// IElement element = getContentAttempt.Result;
// AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
// User,
// ContentPermissionResource.WithKeys(ActionRollback.ActionLetter, element.Key),
// AuthorizationPolicies.ContentPermissionByResource);
//
// if (!authorizationResult.Succeeded)
// {
// return Forbidden();
// }

Attempt<ContentVersionOperationStatus> rollBackAttempt =
await _elementVersionService.RollBackAsync(id, culture, CurrentUserKey(_backOfficeSecurityAccessor));

return rollBackAttempt.Success
? Ok()
: MapFailure(rollBackAttempt.Result);
}
}
Comment thread
kjac marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.ElementVersion;

[ApiVersion("1.0")]
public class UpdatePreventCleanupElementVersionController : ElementVersionControllerBase
{
private readonly IElementVersionService _elementVersionService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public UpdatePreventCleanupElementVersionController(
IElementVersionService elementVersionService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_elementVersionService = elementVersionService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

[MapToApiVersion("1.0")]
[HttpPut("{id:guid}/prevent-cleanup")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Set(CancellationToken cancellationToken, Guid id, bool preventCleanup)
{
Attempt<ContentVersionOperationStatus> attempt =
await _elementVersionService.SetPreventCleanupAsync(id, preventCleanup, CurrentUserKey(_backOfficeSecurityAccessor));

return attempt.Success
? Ok()
: MapFailure(attempt.Result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ protected static IActionResult OperationStatusResult<TEnum>(TEnum status, Func<P
where TEnum : Enum
=> result(new ProblemDetailsBuilder().WithOperationStatus(status));

protected BadRequestObjectResult SkipTakeToPagingProblem() =>
BadRequest(new ProblemDetails
protected static BadRequestObjectResult SkipTakeToPagingProblem() =>
new(new ProblemDetails
{
Title = "Invalid skip/take",
Detail = "Skip must be a multiple of take - i.e. skip = 10, take = 5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ internal static IUmbracoBuilder AddElements(this IUmbracoBuilder builder)
{
builder.Services.AddTransient<IElementPresentationFactory, ElementPresentationFactory>();
builder.Services.AddTransient<IElementEditingPresentationFactory, ElementEditingPresentationFactory>();
builder.Services.AddTransient<IElementVersionPresentationFactory, ElementVersionPresentationFactory>();

builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<ElementMapDefinition>();
.Add<ElementMapDefinition>()
.Add<ElementVersionMapDefinition>();

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Api.Management.ViewModels.Element;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;

namespace Umbraco.Cms.Api.Management.Factories;

internal abstract class ContentVersionPresentationFactoryBase<TVersionItemResponseModel>
{
private readonly IEntityService _entityService;
private readonly IUserIdKeyResolver _userIdKeyResolver;

protected abstract UmbracoObjectTypes ItemObjectType { get; }

protected ContentVersionPresentationFactoryBase(IEntityService entityService, IUserIdKeyResolver userIdKeyResolver)
{
_entityService = entityService;
_userIdKeyResolver = userIdKeyResolver;
}

protected abstract TVersionItemResponseModel VersionItemResponseModelFactory(
Comment thread
kjac marked this conversation as resolved.
Guid versionId,
ReferenceByIdModel item,
ReferenceByIdModel documentType,
ReferenceByIdModel user,
DateTimeOffset versionDate,
bool isCurrentPublishedVersion,
bool isCurrentDraftVersion,
bool preventCleanup);

public async Task<TVersionItemResponseModel> CreateAsync(ContentVersionMeta contentVersion)
{
ReferenceByIdModel userReference = contentVersion.UserId switch
{
Constants.Security.UnknownUserId => new ReferenceByIdModel(),
_ => new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
};

return VersionItemResponseModelFactory(
contentVersion.VersionId.ToGuid(), // this is a magic guid since versions do not have keys in the DB
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentId, ItemObjectType).Result),
new ReferenceByIdModel(
_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
.Result),
userReference,
new DateTimeOffset(contentVersion.VersionDate),
contentVersion.CurrentPublishedVersion,
contentVersion.CurrentDraftVersion,
contentVersion.PreventCleanup);
}

public async Task<IEnumerable<TVersionItemResponseModel>> CreateMultipleAsync(IEnumerable<ContentVersionMeta> contentVersions)
=> await CreateMultipleImplAsync(contentVersions).ToArrayAsync();

private async IAsyncEnumerable<TVersionItemResponseModel> CreateMultipleImplAsync(IEnumerable<ContentVersionMeta> contentVersions)
{
foreach (ContentVersionMeta contentVersion in contentVersions)
{
yield return await CreateAsync(contentVersion);
}
}
}
Loading