Add move/copy operations to datatype API (#13791)
* Add move/copy operations to datatype API * Add compat suppressions for new interface methods * Regenerate OpenAPI JSON * Update OpenApi.json * Ensure we can copy a datatype to root + make the target key nullable (explicit) for move and copy operation models * Handle parent not found status * Update the OpenAPI spec to reflect new nullability * Cleanup --------- Co-authored-by: Elitsa Marinovska <elm@umbraco.dk>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
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.DataType;
|
||||
|
||||
public class CopyDataTypeController : DataTypeControllerBase
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public CopyDataTypeController(IDataTypeService dataTypeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[HttpPost("{key:guid}/copy")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Copy(Guid key, DataTypeCopyModel dataTypeCopyModel)
|
||||
{
|
||||
IDataType? source = await _dataTypeService.GetAsync(key);
|
||||
if (source is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = await _dataTypeService.CopyAsync(source, dataTypeCopyModel.TargetKey, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? CreatedAtAction<ByKeyDataTypeController>(controller => nameof(controller.ByKey), result.Result.Key)
|
||||
: DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public abstract class DataTypeControllerBase : ManagementApiControllerBase
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the data type operation.")
|
||||
.Build()),
|
||||
DataTypeOperationStatus.ParentNotFound => NotFound("The targeted parent for the data type operation was not found."),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown data type operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
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.DataType;
|
||||
|
||||
public class MoveDataTypeController : DataTypeControllerBase
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public MoveDataTypeController(IDataTypeService dataTypeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
[HttpPost("{key:guid}/move")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Move(Guid key, DataTypeMoveModel dataTypeMoveModel)
|
||||
{
|
||||
IDataType? source = await _dataTypeService.GetAsync(key);
|
||||
if (source is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = await _dataTypeService.MoveAsync(source, dataTypeMoveModel.TargetKey, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
return result.Success
|
||||
? Ok()
|
||||
: DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
@@ -234,6 +234,100 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{key}/copy": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Data Type"
|
||||
],
|
||||
"operationId": "PostDataTypeByKeyCopy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DataTypeCopyModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created"
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetailsModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{key}/move": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Data Type"
|
||||
],
|
||||
"operationId": "PostDataTypeByKeyMove",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DataTypeMoveModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetailsModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/data-type/{key}/references": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -5745,6 +5839,17 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeCopyModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"targetKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeCreateModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
@@ -5807,6 +5912,17 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypeMoveModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"targetKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DataTypePropertyModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
|
||||
public class DataTypeCopyModel
|
||||
{
|
||||
public Guid? TargetKey { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
|
||||
public class DataTypeMoveModel
|
||||
{
|
||||
public Guid? TargetKey { get; set; }
|
||||
}
|
||||
@@ -455,6 +455,13 @@
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Services.IDataTypeService.CopyAsync(Umbraco.Cms.Core.Models.IDataType,System.Nullable{System.Guid},System.Int32)</Target>
|
||||
<Left>lib/net7.0/Umbraco.Core.dll</Left>
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Services.IDataTypeService.CreateAsync(Umbraco.Cms.Core.Models.IDataType,System.Int32)</Target>
|
||||
@@ -497,6 +504,13 @@
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Services.IDataTypeService.MoveAsync(Umbraco.Cms.Core.Models.IDataType,System.Nullable{System.Guid},System.Int32)</Target>
|
||||
<Left>lib/net7.0/Umbraco.Core.dll</Left>
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Services.IDataTypeService.UpdateAsync(Umbraco.Cms.Core.Models.IDataType,System.Int32)</Target>
|
||||
|
||||
@@ -355,50 +355,73 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
public Attempt<OperationResult<MoveOperationStatusType>?> Move(IDataType toMove, int parentId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
if (toMove.ParentId == parentId)
|
||||
Guid? containerKey = null;
|
||||
if (parentId > 0)
|
||||
{
|
||||
return OperationResult.Attempt.Fail(MoveOperationStatusType.FailedNotAllowedByPath, evtMsgs);
|
||||
// mimic obsolete Copy method behavior
|
||||
EntityContainer? container = GetContainer(parentId);
|
||||
if (container is null)
|
||||
{
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound);
|
||||
}
|
||||
|
||||
containerKey = container.Key;
|
||||
}
|
||||
|
||||
var moveInfo = new List<MoveEventInfo<IDataType>>();
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = MoveAsync(toMove, containerKey).GetAwaiter().GetResult();
|
||||
|
||||
// mimic old service behavior
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
return result.Status switch
|
||||
{
|
||||
DataTypeOperationStatus.Success => OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs),
|
||||
DataTypeOperationStatus.CancelledByNotification => OperationResult.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs),
|
||||
DataTypeOperationStatus.ParentNotFound => OperationResult.Attempt.Fail(MoveOperationStatusType.FailedParentNotFound, evtMsgs),
|
||||
_ => OperationResult.Attempt.Fail<MoveOperationStatusType>(MoveOperationStatusType.FailedNotAllowedByPath, evtMsgs, new InvalidOperationException($"Invalid operation status: {result.Status}")),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Attempt<IDataType, DataTypeOperationStatus>> MoveAsync(IDataType toMove, Guid? containerKey, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
{
|
||||
var moveEventInfo = new MoveEventInfo<IDataType>(toMove, toMove.Path, parentId);
|
||||
EntityContainer? container = null;
|
||||
var parentId = Constants.System.Root;
|
||||
if (containerKey.HasValue && containerKey.Value != Guid.Empty)
|
||||
{
|
||||
container = await _dataTypeContainerService.GetAsync(containerKey.Value);
|
||||
if (container is null)
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.ParentNotFound, toMove);
|
||||
}
|
||||
|
||||
var movingDataTypeNotification = new DataTypeMovingNotification(moveEventInfo, evtMsgs);
|
||||
parentId = container.Id;
|
||||
}
|
||||
|
||||
if (toMove.ParentId == parentId)
|
||||
{
|
||||
return Attempt.SucceedWithStatus(DataTypeOperationStatus.Success, toMove);
|
||||
}
|
||||
|
||||
var moveEventInfo = new MoveEventInfo<IDataType>(toMove, toMove.Path, parentId);
|
||||
var movingDataTypeNotification = new DataTypeMovingNotification(moveEventInfo, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(movingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs);
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.CancelledByNotification, toMove);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
EntityContainer? container = null;
|
||||
if (parentId > 0)
|
||||
{
|
||||
container = _dataTypeContainerRepository.Get(parentId);
|
||||
if (container == null)
|
||||
{
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound); // causes rollback
|
||||
}
|
||||
}
|
||||
moveInfo.AddRange(_dataTypeRepository.Move(toMove, container));
|
||||
_dataTypeRepository.Move(toMove, container);
|
||||
|
||||
scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, evtMsgs).WithStateFrom(movingDataTypeNotification));
|
||||
scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, eventMessages).WithStateFrom(movingDataTypeNotification));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
catch (DataOperationException<MoveOperationStatusType> ex)
|
||||
{
|
||||
scope.Complete(); // TODO: what are we doing here exactly?
|
||||
return OperationResult.Attempt.Fail(ex.Operation, evtMsgs);
|
||||
}
|
||||
Audit(AuditType.Move, userId, toMove.Id);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs);
|
||||
return Attempt.SucceedWithStatus(DataTypeOperationStatus.Success, toMove);
|
||||
}
|
||||
|
||||
[Obsolete("Use the method which specifies the userId parameter")]
|
||||
@@ -409,32 +432,50 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
public Attempt<OperationResult<MoveOperationStatusType, IDataType>?> Copy(IDataType copying, int containerId, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
IDataType copy;
|
||||
try
|
||||
Guid? containerKey = null;
|
||||
if (containerId > 0)
|
||||
{
|
||||
if (containerId > 0)
|
||||
// mimic obsolete Copy method behavior
|
||||
EntityContainer? container = GetContainer(containerId);
|
||||
if (container is null)
|
||||
{
|
||||
var container = GetContainer(containerId);
|
||||
if (container is null)
|
||||
{
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound); // causes rollback
|
||||
}
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound);
|
||||
}
|
||||
copy = copying.DeepCloneWithResetIdentities();
|
||||
|
||||
copy.Name += " (copy)"; // might not be unique
|
||||
copy.ParentId = containerId;
|
||||
|
||||
Save(copy, userId);
|
||||
containerKey = container.Key;
|
||||
}
|
||||
catch (DataOperationException<MoveOperationStatusType> ex)
|
||||
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = CopyAsync(copying, containerKey, userId).GetAwaiter().GetResult();
|
||||
|
||||
// mimic old service behavior
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
return result.Status switch
|
||||
{
|
||||
return OperationResult.Attempt.Fail<MoveOperationStatusType, IDataType>(ex.Operation, evtMsgs); // causes rollback
|
||||
DataTypeOperationStatus.Success => OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs, result.Result),
|
||||
DataTypeOperationStatus.CancelledByNotification => OperationResult.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs, result.Result),
|
||||
DataTypeOperationStatus.ParentNotFound => OperationResult.Attempt.Fail(MoveOperationStatusType.FailedParentNotFound, evtMsgs, result.Result),
|
||||
_ => OperationResult.Attempt.Fail(MoveOperationStatusType.FailedNotAllowedByPath, evtMsgs, result.Result, new InvalidOperationException($"Invalid operation status: {result.Status}")),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IDataType, DataTypeOperationStatus>> CopyAsync(IDataType toCopy, Guid? containerKey, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EntityContainer? container = null;
|
||||
if (containerKey.HasValue && containerKey.Value != Guid.Empty)
|
||||
{
|
||||
container = await _dataTypeContainerService.GetAsync(containerKey.Value);
|
||||
if (container is null)
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.ParentNotFound, toCopy);
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs, copy);
|
||||
IDataType copy = toCopy.DeepCloneWithResetIdentities();
|
||||
copy.Name += " (copy)"; // might not be unique
|
||||
copy.ParentId = container?.Id ?? Constants.System.Root;
|
||||
|
||||
return await SaveAsync(copy, () => DataTypeOperationStatus.Success, userId, AuditType.Copy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -442,6 +483,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="dataType"><see cref="IDataType"/> to save</param>
|
||||
/// <param name="userId">Id of the user issuing the save</param>
|
||||
[Obsolete("Please use CreateAsync or UpdateAsync. Will be removed in V15.")]
|
||||
public void Save(IDataType dataType, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
// mimic old service behavior
|
||||
|
||||
@@ -175,21 +175,32 @@ public interface IDataTypeService : IService
|
||||
/// <returns>Collection of <see cref="IDataType" /> configured for the property editor</returns>
|
||||
Task<IEnumerable<IDataType>> GetByEditorAliasAsync(string propertyEditorAlias);
|
||||
|
||||
[Obsolete("Please use MoveAsync instead. Will be removed in V15")]
|
||||
Attempt<OperationResult<MoveOperationStatusType>?> Move(IDataType toMove, int parentId);
|
||||
|
||||
[Obsolete("Use the method which specifies the userId parameter")]
|
||||
/// <summary>
|
||||
/// Moves a <see cref="IDataType"/> to a given container
|
||||
/// </summary>
|
||||
/// <param name="toMove">The data type that will be moved</param>
|
||||
/// <param name="containerKey">The container key where the data type will be moved to.</param>
|
||||
/// <param name="userId">The user that did the Move action</param>
|
||||
/// <returns></returns>
|
||||
Task<Attempt<IDataType, DataTypeOperationStatus>> MoveAsync(IDataType toMove, Guid? containerKey, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
[Obsolete("Please use CopyASync instead. Will be removed in V15")]
|
||||
Attempt<OperationResult<MoveOperationStatusType, IDataType>?> Copy(IDataType copying, int containerId) => Copy(copying, containerId, Constants.Security.SuperUserId);
|
||||
|
||||
[Obsolete("Please use CopyASync instead. Will be removed in V15")]
|
||||
Attempt<OperationResult<MoveOperationStatusType, IDataType>?> Copy(IDataType copying, int containerId, int userId = Constants.Security.SuperUserId) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Copies the give <see cref="IDataType"/> to a given container
|
||||
/// We have the default implementation here to avoid breaking changes for the user
|
||||
/// Copies a <see cref="IDataType"/> to a given container
|
||||
/// </summary>
|
||||
/// <param name="copying">The data type that will be copied</param>
|
||||
/// <param name="containerId">The container ID under where the data type will be copied</param>
|
||||
/// <param name="toCopy">The data type that will be copied</param>
|
||||
/// <param name="containerKey">The container key where the data type will be copied to.</param>
|
||||
/// <param name="userId">The user that did the Copy action</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
Attempt<OperationResult<MoveOperationStatusType, IDataType>?> Copy(IDataType copying, int containerId, int userId = Constants.Security.SuperUserId) => throw new NotImplementedException();
|
||||
Task<Attempt<IDataType, DataTypeOperationStatus>> CopyAsync(IDataType toCopy, Guid? containerKey, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Performs validation for the configuration data of a given data type.
|
||||
|
||||
@@ -7,5 +7,6 @@ public enum DataTypeOperationStatus
|
||||
InvalidConfiguration,
|
||||
InvalidName,
|
||||
InvalidId,
|
||||
NotFound
|
||||
NotFound,
|
||||
ParentNotFound
|
||||
}
|
||||
|
||||
@@ -22,8 +22,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
{
|
||||
private IDataValueEditorFactory DataValueEditorFactory => GetRequiredService<IDataValueEditorFactory>();
|
||||
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
|
||||
|
||||
private IFileService FileService => GetRequiredService<IFileService>();
|
||||
@@ -167,6 +170,180 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_DataType_In_Container()
|
||||
{
|
||||
var container = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" })).Result;
|
||||
|
||||
var result = await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext,
|
||||
ParentId = container.Id
|
||||
});
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.IsNotNull(result.Result);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
|
||||
var dataType = await DataTypeService.GetAsync(result.Result.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(container.Id, dataType.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Move_DataType_To_Container()
|
||||
{
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
})).Result;
|
||||
|
||||
var container = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" })).Result;
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(Constants.System.Root, dataType.ParentId);
|
||||
|
||||
var result = await DataTypeService.MoveAsync(dataType, container.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(container.Id, dataType.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Move_DataType_To_Root()
|
||||
{
|
||||
var container = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" })).Result;
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext,
|
||||
ParentId = container.Id
|
||||
})).Result;
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(container.Id, dataType.ParentId);
|
||||
|
||||
var result = await DataTypeService.MoveAsync(dataType, null);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(Constants.System.Root, dataType.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Copy_DataType_To_Root()
|
||||
{
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
})).Result;
|
||||
|
||||
var result = await DataTypeService.CopyAsync(dataType, null);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
Assert.IsNotNull(result.Result);
|
||||
Assert.AreNotEqual(dataType.Key, result.Result.Key);
|
||||
Assert.AreNotEqual(dataType.Name, result.Result.Name);
|
||||
|
||||
IDataType copy = await DataTypeService.GetAsync(result.Result.Key);
|
||||
Assert.IsNotNull(copy);
|
||||
Assert.AreEqual(Constants.System.Root, copy.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Copy_DataType_To_Container()
|
||||
{
|
||||
var container = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" })).Result;
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
})).Result;
|
||||
|
||||
var result = await DataTypeService.CopyAsync(dataType, container.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
Assert.IsNotNull(result.Result);
|
||||
Assert.AreNotEqual(dataType.Key, result.Result.Key);
|
||||
Assert.AreNotEqual(dataType.Name, result.Result.Name);
|
||||
|
||||
IDataType copy = await DataTypeService.GetAsync(result.Result.Key);
|
||||
Assert.IsNotNull(copy);
|
||||
Assert.AreEqual(container.Id, copy.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Copy_DataType_Between_Containers()
|
||||
{
|
||||
var container1 = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container 1" })).Result;
|
||||
var container2 = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container 2" })).Result;
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext,
|
||||
ParentId = container1.Id
|
||||
})).Result;
|
||||
|
||||
var result = await DataTypeService.CopyAsync(dataType, container2.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
Assert.IsNotNull(result.Result);
|
||||
Assert.AreNotEqual(dataType.Key, result.Result.Key);
|
||||
Assert.AreNotEqual(dataType.Name, result.Result.Name);
|
||||
|
||||
IDataType original = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(original);
|
||||
Assert.AreEqual(container1.Id, original.ParentId);
|
||||
|
||||
IDataType copy = await DataTypeService.GetAsync(result.Result.Key);
|
||||
Assert.IsNotNull(copy);
|
||||
Assert.AreEqual(container2.Id, copy.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Copy_DataType_From_Container_To_Root()
|
||||
{
|
||||
var container1 = (await DataTypeContainerService.CreateAsync(new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container 1" })).Result;
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext,
|
||||
ParentId = container1.Id
|
||||
})).Result;
|
||||
|
||||
var result = await DataTypeService.CopyAsync(dataType, null);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
Assert.IsNotNull(result.Result);
|
||||
Assert.AreNotEqual(dataType.Key, result.Result.Key);
|
||||
Assert.AreNotEqual(dataType.Name, result.Result.Name);
|
||||
|
||||
IDataType original = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(original);
|
||||
Assert.AreEqual(container1.Id, original.ParentId);
|
||||
|
||||
IDataType copy = await DataTypeService.GetAsync(result.Result.Key);
|
||||
Assert.IsNotNull(copy);
|
||||
Assert.AreEqual(Constants.System.Root, copy.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Create_DataType_With_Empty_Name()
|
||||
{
|
||||
@@ -219,4 +396,27 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => DataTypeService.Save(dataTypeDefinition));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Move_DataType_To_Non_Existing_Container()
|
||||
{
|
||||
var dataType = (await DataTypeService.CreateAsync(
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
})).Result;
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(Constants.System.Root, dataType.ParentId);
|
||||
|
||||
var result = await DataTypeService.MoveAsync(dataType, Guid.NewGuid());
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.ParentNotFound, result.Status);
|
||||
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(dataType);
|
||||
Assert.AreEqual(Constants.System.Root, dataType.ParentId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user