V14: Copy and move endpoints for content and media types (#15553)

* Create new request models

* Create new structural operation status

* Handling of new structural operation status

* New CopyAsync and MoveAsync

* New endpoints

* Update schema

* Adding NotFound status

* Adding BadRequest response type

* Moving getbyid check to service

* Fix signature and implement get by id check in service

* Update OpenApi schema

* Use PUT instead of POST in "move" controllers

* Use PUT instead of POST in some more "move" controllers

---------

Co-authored-by: kjac <kja@umbraco.dk>
This commit is contained in:
Elitsa Marinovska
2024-01-12 08:59:21 +01:00
committed by GitHub
parent e291fae01e
commit c5c24d36d9
16 changed files with 881 additions and 6 deletions

View File

@@ -754,6 +754,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
#region Copy
[Obsolete("Please use CopyAsync. Will be removed in V15.")]
public TItem Copy(TItem original, string alias, string name, int parentId = -1)
{
TItem? parent = null;
@@ -768,6 +769,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
return Copy(original, alias, name, parent);
}
[Obsolete("Please use CopyAsync. Will be removed in V15.")]
public TItem Copy(TItem original, string alias, string name, TItem? parent)
{
if (original == null)
@@ -822,6 +824,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
return clone;
}
[Obsolete("Please use CopyAsync. Will be removed in V16.")]
public Attempt<OperationResult<MoveOperationStatusType, TItem>?> Copy(TItem copying, int containerId)
{
EventMessages eventMessages = EventMessagesFactory.Get();
@@ -894,10 +897,53 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
return OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, eventMessages, copy);
}
public async Task<Attempt<TItem?, ContentTypeStructureOperationStatus>> CopyAsync(Guid key, Guid? containerKey)
{
TItem? toCopy = await GetAsync(key);
if (toCopy is null)
{
return Attempt.FailWithStatus(ContentTypeStructureOperationStatus.NotFound, toCopy);
}
var containerId = GetContainerOrRootId(containerKey);
if (containerId is null)
{
return Attempt.FailWithStatus<TItem?, ContentTypeStructureOperationStatus>(ContentTypeStructureOperationStatus.ContainerNotFound, toCopy);
}
// using obsolete method for version control while it still exists
Attempt<OperationResult<MoveOperationStatusType, TItem>?> result = Copy(toCopy, containerId.Value);
return MapStatusTypeToAttempt(result.Result?.Entity, result.Result?.Result);
}
private int? GetContainerOrRootId(Guid? containerKey)
{
if (containerKey is null)
{
return Constants.System.Root;
}
EntityContainer? container = GetContainer(containerKey.Value);
return container?.Id;
}
private Attempt<TItem?, ContentTypeStructureOperationStatus> MapStatusTypeToAttempt(TItem? item, MoveOperationStatusType? resultStatus) =>
resultStatus switch
{
MoveOperationStatusType.Success => Attempt.SucceedWithStatus(ContentTypeStructureOperationStatus.Success, item),
MoveOperationStatusType.FailedParentNotFound => Attempt.FailWithStatus(ContentTypeStructureOperationStatus.ContainerNotFound, item),
MoveOperationStatusType.FailedCancelledByEvent => Attempt.FailWithStatus(ContentTypeStructureOperationStatus.CancelledByNotification, item),
MoveOperationStatusType.FailedNotAllowedByPath => Attempt.FailWithStatus(ContentTypeStructureOperationStatus.NotAllowedByPath, item),
_ => throw new NotImplementedException($"{nameof(ContentTypeStructureOperationStatus)} does not map to a corresponding {nameof(MoveOperationStatusType)}")
};
#endregion
#region Move
[Obsolete("Please use MoveAsync. Will be removed in V16.")]
public Attempt<OperationResult<MoveOperationStatusType>?> Move(TItem moving, int containerId)
{
EventMessages eventMessages = EventMessagesFactory.Get();
@@ -950,6 +996,27 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
return OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, eventMessages);
}
public async Task<Attempt<TItem?, ContentTypeStructureOperationStatus>> MoveAsync(Guid key, Guid? containerKey)
{
TItem? toMove = await GetAsync(key);
if (toMove is null)
{
return Attempt.FailWithStatus(ContentTypeStructureOperationStatus.NotFound, toMove);
}
var containerId = GetContainerOrRootId(containerKey);
if (containerId is null)
{
return Attempt.FailWithStatus<TItem?, ContentTypeStructureOperationStatus>(ContentTypeStructureOperationStatus.ContainerNotFound, toMove);
}
// using obsolete method for version control while it still exists
Attempt<OperationResult<MoveOperationStatusType>?> result = Move(toMove, containerId.Value);
return MapStatusTypeToAttempt(toMove, result.Result?.Result);
}
#endregion
#region Containers

View File

@@ -124,11 +124,19 @@ public interface IContentTypeBaseService<TItem> : IContentTypeBaseService, IServ
Attempt<OperationResult<OperationResultType, EntityContainer>?> RenameContainer(int id, string name, int userId = Constants.Security.SuperUserId);
[Obsolete("Please use MoveAsync. Will be removed in V16.")]
Attempt<OperationResult<MoveOperationStatusType>?> Move(TItem moving, int containerId);
[Obsolete("Please use CopyAsync. Will be removed in V16.")]
Attempt<OperationResult<MoveOperationStatusType, TItem>?> Copy(TItem copying, int containerId);
[Obsolete("Please use CopyAsync. Will be removed in V15.")]
TItem Copy(TItem original, string alias, string name, int parentId = -1);
[Obsolete("Please use CopyAsync. Will be removed in V15.")]
TItem Copy(TItem original, string alias, string name, TItem parent);
Task<Attempt<TItem?, ContentTypeStructureOperationStatus>> CopyAsync(Guid key, Guid? containerKey);
Task<Attempt<TItem?, ContentTypeStructureOperationStatus>> MoveAsync(Guid key, Guid? containerKey);
}

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Cms.Core.Services.OperationStatus;
public enum ContentTypeStructureOperationStatus
{
Success,
CancelledByNotification,
ContainerNotFound,
NotAllowedByPath,
NotFound
}