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:
Kenn Jacobsen
2023-02-10 15:11:49 +01:00
committed by GitHub
parent 525d73d205
commit 50f19549b6
11 changed files with 533 additions and 54 deletions

View File

@@ -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);
}
}

View File

@@ -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")
};
}

View File

@@ -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);
}
}

View File

@@ -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": {

View File

@@ -0,0 +1,6 @@
namespace Umbraco.Cms.Api.Management.ViewModels.DataType;
public class DataTypeCopyModel
{
public Guid? TargetKey { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace Umbraco.Cms.Api.Management.ViewModels.DataType;
public class DataTypeMoveModel
{
public Guid? TargetKey { get; set; }
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -7,5 +7,6 @@ public enum DataTypeOperationStatus
InvalidConfiguration,
InvalidName,
InvalidId,
NotFound
NotFound,
ParentNotFound
}

View File

@@ -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);
}
}