Align the data type API (#13760)
* Make data type CRUD operations async using Attempt pattern * Refactor data type container management to its own service + add unit tests for it * Add compatability suppression for new interface methods and unit test changes
This commit is contained in:
@@ -24,12 +24,12 @@ public class ByKeyDataTypeController : DataTypeControllerBase
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<DataTypeViewModel>> ByKey(Guid key)
|
||||
{
|
||||
IDataType? dataType = _dataTypeService.GetDataType(key);
|
||||
IDataType? dataType = await _dataTypeService.GetAsync(key);
|
||||
if (dataType == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await Task.FromResult(Ok(_umbracoMapper.Map<DataTypeViewModel>(dataType)));
|
||||
return Ok(_umbracoMapper.Map<DataTypeViewModel>(dataType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
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;
|
||||
|
||||
@@ -24,21 +26,15 @@ public class CreateDataTypeController : DataTypeControllerBase
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> Create(DataTypeCreateModel dataTypeCreateModel)
|
||||
public async Task<IActionResult> Create(DataTypeCreateModel dataTypeCreateModel)
|
||||
{
|
||||
IDataType? created = _umbracoMapper.Map<IDataType>(dataTypeCreateModel);
|
||||
if (created == null)
|
||||
{
|
||||
return BadRequest("Could not map the POSTed model to a data type");
|
||||
}
|
||||
IDataType? created = _umbracoMapper.Map<IDataType>(dataTypeCreateModel)!;
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = await _dataTypeService.CreateAsync(created, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
ProblemDetails? validationIssues = Save(created, _dataTypeService, _backOfficeSecurityAccessor);
|
||||
if (validationIssues != null)
|
||||
{
|
||||
return BadRequest(validationIssues);
|
||||
}
|
||||
|
||||
return await Task.FromResult(CreatedAtAction<ByKeyDataTypeController>(controller => nameof(controller.ByKey), created.Key));
|
||||
return result.Success
|
||||
? CreatedAtAction<ByKeyDataTypeController>(controller => nameof(controller.ByKey), created.Key)
|
||||
: DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
|
||||
@@ -16,19 +13,22 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType;
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class DataTypeControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
protected static ProblemDetails? Save(IDataType dataType, IDataTypeService dataTypeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
ValidationResult[] validationResults = dataTypeService.ValidateConfigurationData(dataType).ToArray();
|
||||
if (validationResults.Any())
|
||||
protected IActionResult DataTypeOperationStatusResult(DataTypeOperationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
return new ProblemDetailsBuilder()
|
||||
DataTypeOperationStatus.InvalidConfiguration => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Invalid data type configuration")
|
||||
.WithDetail(string.Join(Environment.NewLine, validationResults.Select(r => r.ErrorMessage)))
|
||||
.Build();
|
||||
}
|
||||
|
||||
dataTypeService.Save(dataType, CurrentUserId(backOfficeSecurityAccessor));
|
||||
|
||||
return null;
|
||||
}
|
||||
.WithDetail("The supplied data type configuration was not valid. Please see the log for more details.")
|
||||
.Build()),
|
||||
DataTypeOperationStatus.NotFound => NotFound("The data type could not be found"),
|
||||
DataTypeOperationStatus.InvalidName => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Invalid data type name")
|
||||
.WithDetail("The data type name must be non-empty and no longer than 255 characters.")
|
||||
.Build()),
|
||||
DataTypeOperationStatus.CancelledByNotification => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the data type operation.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown data type operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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;
|
||||
|
||||
@@ -21,18 +22,14 @@ public class DeleteDataTypeController : DataTypeControllerBase
|
||||
[HttpDelete("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> Delete(Guid key)
|
||||
public async Task<IActionResult> Delete(Guid key)
|
||||
{
|
||||
IDataType? dataType = _dataTypeService.GetDataType(key);
|
||||
if (dataType == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
Attempt<IDataType?, DataTypeOperationStatus> result = await _dataTypeService.DeleteAsync(key, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
// one might expect this method to have an Attempt return value, but no - it has no
|
||||
// return value, we'll just have to assume it succeeds
|
||||
_dataTypeService.Delete(dataType, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
return await Task.FromResult(Ok());
|
||||
return result.Success
|
||||
? Ok()
|
||||
: DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
|
||||
public class ByKeyDataTypeFolderController : DataTypeFolderControllerBase
|
||||
{
|
||||
public ByKeyDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeService dataTypeService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeService)
|
||||
public ByKeyDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeContainerService dataTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,6 +17,5 @@ public class ByKeyDataTypeFolderController : DataTypeFolderControllerBase
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(FolderViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<FolderViewModel>> ByKey(Guid key)
|
||||
=> await Task.FromResult(GetFolder(key));
|
||||
public async Task<IActionResult> ByKey(Guid key) => await GetFolderAsync(key);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
|
||||
public class CreateDataTypeFolderController : DataTypeFolderControllerBase
|
||||
{
|
||||
public CreateDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeService dataTypeService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeService)
|
||||
public CreateDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeContainerService dataTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
public async Task<ActionResult> Create(FolderCreateModel folderCreateModel)
|
||||
=> await Task.FromResult(CreateFolder<ByKeyDataTypeFolderController>(folderCreateModel, controller => nameof(controller.ByKey)));
|
||||
public async Task<IActionResult> Create(FolderCreateModel folderCreateModel)
|
||||
=> await CreateFolderAsync<ByKeyDataTypeFolderController>(folderCreateModel, controller => nameof(controller.ByKey));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
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.Folder;
|
||||
|
||||
@@ -11,26 +14,44 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/folder")]
|
||||
[ApiExplorerSettings(GroupName = "Data Type")]
|
||||
public abstract class DataTypeFolderControllerBase : FolderManagementControllerBase
|
||||
public abstract class DataTypeFolderControllerBase : FolderManagementControllerBase<DataTypeContainerOperationStatus>
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly IDataTypeContainerService _dataTypeContainerService;
|
||||
|
||||
protected DataTypeFolderControllerBase(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeService dataTypeService)
|
||||
protected DataTypeFolderControllerBase(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeContainerService dataTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor) =>
|
||||
_dataTypeService = dataTypeService;
|
||||
_dataTypeContainerService = dataTypeContainerService;
|
||||
|
||||
protected override EntityContainer? GetContainer(Guid key)
|
||||
=> _dataTypeService.GetContainer(key);
|
||||
protected override Guid ContainerObjectType => Constants.ObjectTypes.DataType;
|
||||
|
||||
protected override EntityContainer? GetContainer(int containerId)
|
||||
=> _dataTypeService.GetContainer(containerId);
|
||||
protected override async Task<EntityContainer?> GetContainerAsync(Guid key)
|
||||
=> await _dataTypeContainerService.GetAsync(key);
|
||||
|
||||
protected override Attempt<OperationResult<OperationResultType, EntityContainer>?> CreateContainer(int parentId, string name, int userId)
|
||||
=> _dataTypeService.CreateContainer(parentId, Guid.NewGuid(), name, userId);
|
||||
protected override async Task<EntityContainer?> GetParentContainerAsync(EntityContainer container)
|
||||
=> await _dataTypeContainerService.GetParentAsync(container);
|
||||
|
||||
protected override Attempt<OperationResult?> SaveContainer(EntityContainer container, int userId)
|
||||
=> _dataTypeService.SaveContainer(container, userId);
|
||||
protected override async Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> CreateContainerAsync(EntityContainer container, Guid? parentId, int userId)
|
||||
=> await _dataTypeContainerService.CreateAsync(container, parentId, userId);
|
||||
|
||||
protected override Attempt<OperationResult?> DeleteContainer(int containerId, int userId)
|
||||
=> _dataTypeService.DeleteContainer(containerId, userId);
|
||||
protected override async Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> UpdateContainerAsync(EntityContainer container, int userId)
|
||||
=> await _dataTypeContainerService.UpdateAsync(container, userId);
|
||||
|
||||
protected override async Task<Attempt<EntityContainer?, DataTypeContainerOperationStatus>> DeleteContainerAsync(Guid id, int userId)
|
||||
=> await _dataTypeContainerService.DeleteAsync(id, userId);
|
||||
|
||||
protected override IActionResult OperationStatusResult(DataTypeContainerOperationStatus status)
|
||||
=> status switch
|
||||
{
|
||||
DataTypeContainerOperationStatus.NotFound => NotFound("The data type folder could not be found"),
|
||||
DataTypeContainerOperationStatus.ParentNotFound => NotFound("The data type parent folder could not be found"),
|
||||
DataTypeContainerOperationStatus.NotEmpty => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("The folder is not empty")
|
||||
.WithDetail("The data type folder must be empty to perform this action.")
|
||||
.Build()),
|
||||
DataTypeContainerOperationStatus.CancelledByNotification => BadRequest(new ProblemDetailsBuilder()
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the data type folder operation.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown data type folder operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
|
||||
public class DeleteDataTypeFolderController : DataTypeFolderControllerBase
|
||||
{
|
||||
public DeleteDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeService dataTypeService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeService)
|
||||
public DeleteDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeContainerService dataTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,6 +17,5 @@ public class DeleteDataTypeFolderController : DataTypeFolderControllerBase
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> Delete(Guid key)
|
||||
=> await Task.FromResult(DeleteFolder(key));
|
||||
public async Task<IActionResult> Delete(Guid key) => await DeleteFolderAsync(key);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Umbraco.Cms.Api.Management.Controllers.DataType.Folder;
|
||||
|
||||
public class UpdateDataTypeFolderController : DataTypeFolderControllerBase
|
||||
{
|
||||
public UpdateDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeService dataTypeService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeService)
|
||||
public UpdateDataTypeFolderController(IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IDataTypeContainerService dataTypeContainerService)
|
||||
: base(backOfficeSecurityAccessor, dataTypeContainerService)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,6 +17,5 @@ public class UpdateDataTypeFolderController : DataTypeFolderControllerBase
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> Update(Guid key, FolderUpdateModel folderUpdateModel)
|
||||
=> await Task.FromResult(UpdateFolder(key, folderUpdateModel));
|
||||
public async Task<IActionResult> Update(Guid key, FolderUpdateModel folderUpdateModel) => await UpdateFolderAsync(key, folderUpdateModel);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
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.DataType;
|
||||
|
||||
@@ -23,17 +23,15 @@ public class ReferencesDataTypeController : DataTypeControllerBase
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DataTypeReferenceViewModel[]), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<DataTypeReferenceViewModel[]>> References(Guid key)
|
||||
public async Task<IActionResult> References(Guid key)
|
||||
{
|
||||
IDataType? dataType = _dataTypeService.GetDataType(key);
|
||||
if (dataType == null)
|
||||
Attempt<IReadOnlyDictionary<Udi, IEnumerable<string>>, DataTypeOperationStatus> result = await _dataTypeService.GetReferencesAsync(key);
|
||||
if (result.Success == false)
|
||||
{
|
||||
return NotFound();
|
||||
return DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> usages = _dataTypeService.GetReferences(dataType.Id);
|
||||
DataTypeReferenceViewModel[] viewModels = _dataTypeReferenceViewModelFactory.CreateDataTypeReferenceViewModels(usages).ToArray();
|
||||
|
||||
return await Task.FromResult(Ok(viewModels));
|
||||
DataTypeReferenceViewModel[] viewModels = _dataTypeReferenceViewModelFactory.CreateDataTypeReferenceViewModels(result.Result).ToArray();
|
||||
return Ok(viewModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DataType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
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;
|
||||
|
||||
@@ -25,23 +26,21 @@ public class UpdateDataTypeController : DataTypeControllerBase
|
||||
[HttpPut("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> Update(Guid key, DataTypeUpdateModel dataTypeViewModel)
|
||||
public async Task<IActionResult> Update(Guid key, DataTypeUpdateModel dataTypeViewModel)
|
||||
{
|
||||
IDataType? current = _dataTypeService.GetDataType(key);
|
||||
IDataType? current = await _dataTypeService.GetAsync(key);
|
||||
if (current == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
IDataType updated = _umbracoMapper.Map(dataTypeViewModel, current);
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = await _dataTypeService.UpdateAsync(updated, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
ProblemDetails? validationIssues = Save(updated, _dataTypeService, _backOfficeSecurityAccessor);
|
||||
if (validationIssues != null)
|
||||
{
|
||||
return BadRequest(validationIssues);
|
||||
}
|
||||
|
||||
return await Task.FromResult(Ok());
|
||||
return result.Success
|
||||
? Ok()
|
||||
: DataTypeOperationStatusResult(result.Status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Folder;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers;
|
||||
|
||||
public abstract class FolderManagementControllerBase : ManagementApiControllerBase
|
||||
public abstract class FolderManagementControllerBase<TStatus> : ManagementApiControllerBase
|
||||
where TStatus : Enum
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
protected FolderManagementControllerBase(IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
=> _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
|
||||
protected ActionResult GetFolder(Guid key)
|
||||
protected async Task<IActionResult> GetFolderAsync(Guid key)
|
||||
{
|
||||
EntityContainer? container = GetContainer(key);
|
||||
EntityContainer? container = await GetContainerAsync(key);
|
||||
if (container == null)
|
||||
{
|
||||
return NotFound($"Could not find the folder with key: {key}");
|
||||
}
|
||||
|
||||
EntityContainer? parentContainer = container.ParentId > 0
|
||||
? GetContainer(container.ParentId)
|
||||
: null;
|
||||
EntityContainer? parentContainer = await GetParentContainerAsync(container);
|
||||
|
||||
// we could implement a mapper for this but it seems rather overkill at this point
|
||||
return Ok(new FolderViewModel
|
||||
@@ -37,35 +34,25 @@ public abstract class FolderManagementControllerBase : ManagementApiControllerBa
|
||||
});
|
||||
}
|
||||
|
||||
protected ActionResult CreateFolder<TCreatedActionController>(
|
||||
protected async Task<IActionResult> CreateFolderAsync<TCreatedActionController>(
|
||||
FolderCreateModel folderCreateModel,
|
||||
Expression<Func<TCreatedActionController, string>> createdAction)
|
||||
{
|
||||
EntityContainer? parentContainer = folderCreateModel.ParentKey.HasValue
|
||||
? GetContainer(folderCreateModel.ParentKey.Value)
|
||||
: null;
|
||||
var container = new EntityContainer(ContainerObjectType) { Name = folderCreateModel.Name };
|
||||
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>?> result = CreateContainer(
|
||||
parentContainer?.Id ?? Constants.System.Root,
|
||||
folderCreateModel.Name,
|
||||
Attempt<EntityContainer, TStatus> result = await CreateContainerAsync(
|
||||
container,
|
||||
folderCreateModel.ParentKey,
|
||||
CurrentUserId(_backOfficeSecurityAccessor));
|
||||
|
||||
if (result.Success == false)
|
||||
{
|
||||
ProblemDetails problemDetails = new ProblemDetailsBuilder()
|
||||
.WithTitle("Unable to create the folder")
|
||||
.WithDetail(result.Exception?.Message ?? FallbackProblemDetail(result.Result))
|
||||
.Build();
|
||||
return BadRequest(problemDetails);
|
||||
}
|
||||
|
||||
EntityContainer container = result.Result!.Entity!;
|
||||
return CreatedAtAction(createdAction, container.Key);
|
||||
return result.Success
|
||||
? CreatedAtAction(createdAction, result.Result.Key)
|
||||
: OperationStatusResult(result.Status);
|
||||
}
|
||||
|
||||
protected ActionResult UpdateFolder(Guid key, FolderUpdateModel folderUpdateModel)
|
||||
protected async Task<IActionResult> UpdateFolderAsync(Guid key, FolderUpdateModel folderUpdateModel)
|
||||
{
|
||||
EntityContainer? container = GetContainer(key);
|
||||
EntityContainer? container = await GetContainerAsync(key);
|
||||
if (container == null)
|
||||
{
|
||||
return NotFound($"Could not find the folder with key: {key}");
|
||||
@@ -73,50 +60,31 @@ public abstract class FolderManagementControllerBase : ManagementApiControllerBa
|
||||
|
||||
container.Name = folderUpdateModel.Name;
|
||||
|
||||
Attempt<OperationResult?> result = SaveContainer(container, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
if (result.Success == false)
|
||||
{
|
||||
ProblemDetails problemDetails = new ProblemDetailsBuilder()
|
||||
.WithTitle("Unable to update the folder")
|
||||
.WithDetail(result.Exception?.Message ?? FallbackProblemDetail(result.Result))
|
||||
.Build();
|
||||
return BadRequest(problemDetails);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
Attempt<EntityContainer, TStatus> result = await UpdateContainerAsync(container, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
return result.Success
|
||||
? Ok()
|
||||
: OperationStatusResult(result.Status);
|
||||
}
|
||||
|
||||
protected ActionResult DeleteFolder(Guid key)
|
||||
protected async Task<IActionResult> DeleteFolderAsync(Guid key)
|
||||
{
|
||||
EntityContainer? container = GetContainer(key);
|
||||
if (container == null)
|
||||
{
|
||||
return NotFound($"Could not find the folder with key: {key}");
|
||||
}
|
||||
|
||||
Attempt<OperationResult?> result = DeleteContainer(container.Id, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
if (result.Success == false)
|
||||
{
|
||||
ProblemDetails problemDetails = new ProblemDetailsBuilder()
|
||||
.WithTitle("Unable to delete the folder")
|
||||
.WithDetail(result.Exception?.Message ?? FallbackProblemDetail(result.Result))
|
||||
.Build();
|
||||
return BadRequest(problemDetails);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
Attempt<EntityContainer?, TStatus> result = await DeleteContainerAsync(key, CurrentUserId(_backOfficeSecurityAccessor));
|
||||
return result.Success
|
||||
? Ok()
|
||||
: OperationStatusResult(result.Status);
|
||||
}
|
||||
|
||||
private static string FallbackProblemDetail(OperationResult<OperationResultType>? result)
|
||||
=> result != null ? $"The reported operation result was: {result.Result}" : "Check the log for additional details";
|
||||
protected abstract Guid ContainerObjectType { get; }
|
||||
|
||||
protected abstract EntityContainer? GetContainer(Guid key);
|
||||
protected abstract Task<EntityContainer?> GetContainerAsync(Guid key);
|
||||
|
||||
protected abstract EntityContainer? GetContainer(int containerId);
|
||||
protected abstract Task<EntityContainer?> GetParentContainerAsync(EntityContainer container);
|
||||
|
||||
protected abstract Attempt<OperationResult?> SaveContainer(EntityContainer container, int userId);
|
||||
protected abstract Task<Attempt<EntityContainer, TStatus>> CreateContainerAsync(EntityContainer container, Guid? parentId, int userId);
|
||||
|
||||
protected abstract Attempt<OperationResult<OperationResultType, EntityContainer>?> CreateContainer(int parentId, string name, int userId);
|
||||
protected abstract Task<Attempt<EntityContainer, TStatus>> UpdateContainerAsync(EntityContainer container, int userId);
|
||||
|
||||
protected abstract Attempt<OperationResult?> DeleteContainer(int containerId, int userId);
|
||||
protected abstract Task<Attempt<EntityContainer?, TStatus>> DeleteContainerAsync(Guid id, int userId);
|
||||
|
||||
protected abstract IActionResult OperationStatusResult(TStatus status);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ public abstract class LanguageControllerBase : ManagementApiControllerBase
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the language operation.")
|
||||
.Build()),
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown dictionary operation status")
|
||||
_ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown language operation status")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Services.Implement.DataTypeService.#ctor(Umbraco.Cms.Core.PropertyEditors.IDataValueEditorFactory,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Microsoft.Extensions.Logging.ILoggerFactory,Umbraco.Cms.Core.Events.IEventMessagesFactory,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeRepository,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeContainerRepository,Umbraco.Cms.Core.Persistence.Repositories.IAuditRepository,Umbraco.Cms.Core.Persistence.Repositories.IEntityRepository,Umbraco.Cms.Core.Persistence.Repositories.IContentTypeRepository,Umbraco.Cms.Core.IO.IIOHelper,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.Services.ILocalizationService,Umbraco.Cms.Core.Strings.IShortStringHelper,Umbraco.Cms.Core.Serialization.IJsonSerializer)</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.Deploy.IDataTypeConfigurationConnector.FromArtifact(Umbraco.Cms.Core.Models.IDataType,System.String,Umbraco.Cms.Core.Deploy.IContextCache)</Target>
|
||||
@@ -294,6 +301,55 @@
|
||||
<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>
|
||||
<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.DeleteAsync(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.GetAsync(System.Guid)</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.GetAsync(System.String)</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.GetByEditorAliasAsync(System.String)</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.GetReferencesAsync(System.Guid)</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>
|
||||
<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.ValidateConfigurationData(Umbraco.Cms.Core.Models.IDataType)</Target>
|
||||
|
||||
@@ -282,6 +282,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
Services.AddUnique<IUserService, UserService>();
|
||||
Services.AddUnique<ILocalizationService, LocalizationService>();
|
||||
Services.AddUnique<IDictionaryItemService, DictionaryItemService>();
|
||||
Services.AddUnique<IDataTypeContainerService, DataTypeContainerService>();
|
||||
Services.AddUnique<ILanguageService, LanguageService>();
|
||||
Services.AddUnique<IMacroService, MacroService>();
|
||||
Services.AddUnique<IMemberGroupService, MemberGroupService>();
|
||||
|
||||
180
src/Umbraco.Core/Services/DataTypeContainerService.cs
Normal file
180
src/Umbraco.Core/Services/DataTypeContainerService.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
internal sealed class DataTypeContainerService : RepositoryService, IDataTypeContainerService
|
||||
{
|
||||
private readonly IDataTypeContainerRepository _dataTypeContainerRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IEntityRepository _entityRepository;
|
||||
|
||||
public DataTypeContainerService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDataTypeContainerRepository dataTypeContainerRepository,
|
||||
IAuditRepository auditRepository,
|
||||
IEntityRepository entityRepository)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_dataTypeContainerRepository = dataTypeContainerRepository;
|
||||
_auditRepository = auditRepository;
|
||||
_entityRepository = entityRepository;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<EntityContainer?> GetAsync(Guid id)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return await Task.FromResult(_dataTypeContainerRepository.Get(id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<EntityContainer?> GetParentAsync(EntityContainer container)
|
||||
=> await Task.FromResult(GetParent(container));
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<EntityContainer?> GetParentAsync(IDataType dataType)
|
||||
=> await Task.FromResult(GetParent(dataType));
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> CreateAsync(EntityContainer container, Guid? parentId = null, int userId = Constants.Security.SuperUserId)
|
||||
=> await SaveAsync(
|
||||
container,
|
||||
userId,
|
||||
() =>
|
||||
{
|
||||
if (container.Id > 0)
|
||||
{
|
||||
return DataTypeContainerOperationStatus.InvalidId;
|
||||
}
|
||||
|
||||
EntityContainer? parentContainer = parentId.HasValue
|
||||
? _dataTypeContainerRepository.Get(parentId.Value)
|
||||
: null;
|
||||
|
||||
if (parentId.HasValue && parentContainer == null)
|
||||
{
|
||||
return DataTypeContainerOperationStatus.ParentNotFound;
|
||||
}
|
||||
|
||||
container.ParentId = parentContainer?.Id ?? Constants.System.Root;
|
||||
return DataTypeContainerOperationStatus.Success;
|
||||
},
|
||||
AuditType.New);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> UpdateAsync(EntityContainer container, int userId = Constants.Security.SuperUserId)
|
||||
=> await SaveAsync(
|
||||
container,
|
||||
userId,
|
||||
() =>
|
||||
{
|
||||
if (container.Id == 0)
|
||||
{
|
||||
return DataTypeContainerOperationStatus.InvalidId;
|
||||
}
|
||||
|
||||
if (container.IsPropertyDirty(nameof(EntityContainer.ParentId)))
|
||||
{
|
||||
LoggerFactory.CreateLogger<DataTypeContainerService>().LogWarning($"Cannot use {nameof(UpdateAsync)} to change the container parent. Move the container instead.");
|
||||
return DataTypeContainerOperationStatus.ParentNotFound;
|
||||
}
|
||||
|
||||
return DataTypeContainerOperationStatus.Success;
|
||||
},
|
||||
AuditType.New);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<EntityContainer?, DataTypeContainerOperationStatus>> DeleteAsync(Guid id, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
|
||||
EntityContainer? container = _dataTypeContainerRepository.Get(id);
|
||||
if (container == null)
|
||||
{
|
||||
return Attempt.FailWithStatus<EntityContainer?, DataTypeContainerOperationStatus>(DataTypeContainerOperationStatus.NotFound, null);
|
||||
}
|
||||
|
||||
// 'container' here does not know about its children, so we need
|
||||
// to get it again from the entity repository, as a light entity
|
||||
IEntitySlim? entity = _entityRepository.Get(container.Id);
|
||||
if (entity?.HasChildren is true)
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus<EntityContainer?, DataTypeContainerOperationStatus>(DataTypeContainerOperationStatus.NotEmpty, container);
|
||||
}
|
||||
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
|
||||
var deletingEntityContainerNotification = new EntityContainerDeletingNotification(container, eventMessages);
|
||||
if (await scope.Notifications.PublishCancelableAsync(deletingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus<EntityContainer?, DataTypeContainerOperationStatus>(DataTypeContainerOperationStatus.CancelledByNotification, container);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Delete(container);
|
||||
|
||||
Audit(AuditType.Delete, userId, container.Id);
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerDeletedNotification(container, eventMessages).WithStateFrom(deletingEntityContainerNotification));
|
||||
|
||||
return Attempt.SucceedWithStatus<EntityContainer?, DataTypeContainerOperationStatus>(DataTypeContainerOperationStatus.Success, container);
|
||||
}
|
||||
|
||||
private async Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> SaveAsync(EntityContainer container, int userId, Func<DataTypeContainerOperationStatus> operationValidation, AuditType auditType)
|
||||
{
|
||||
if (container.ContainedObjectType != Constants.ObjectTypes.DataType)
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeContainerOperationStatus.InvalidObjectType, container);
|
||||
}
|
||||
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
|
||||
DataTypeContainerOperationStatus operationValidationStatus = operationValidation();
|
||||
if (operationValidationStatus != DataTypeContainerOperationStatus.Success)
|
||||
{
|
||||
return Attempt.FailWithStatus(operationValidationStatus, container);
|
||||
}
|
||||
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingEntityContainerNotification = new EntityContainerSavingNotification(container, eventMessages);
|
||||
if (await scope.Notifications.PublishCancelableAsync(savingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus(DataTypeContainerOperationStatus.CancelledByNotification, container);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
|
||||
Audit(auditType, userId, container.Id);
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerSavedNotification(container, eventMessages).WithStateFrom(savingEntityContainerNotification));
|
||||
|
||||
return Attempt.SucceedWithStatus(DataTypeContainerOperationStatus.Success, container);
|
||||
}
|
||||
|
||||
private EntityContainer? GetParent(ITreeEntity treeEntity)
|
||||
{
|
||||
if (treeEntity.ParentId == Constants.System.Root)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _dataTypeContainerRepository.Get(treeEntity.ParentId);
|
||||
}
|
||||
|
||||
private void Audit(AuditType type, int userId, int objectId)
|
||||
=> _auditRepository.Save(new AuditItem(objectId, type, userId, UmbracoObjectTypes.DataTypeContainer.GetName()));
|
||||
}
|
||||
@@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Exceptions;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -28,60 +28,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
private readonly IDataTypeContainerRepository _dataTypeContainerRepository;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IEntityRepository _entityRepository;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IEditorConfigurationParser _editorConfigurationParser;
|
||||
private readonly IDataTypeContainerService _dataTypeContainerService;
|
||||
|
||||
[Obsolete("Please use constructor that takes an ")]
|
||||
public DataTypeService(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDataTypeRepository dataTypeRepository,
|
||||
IDataTypeContainerRepository dataTypeContainerRepository,
|
||||
IAuditRepository auditRepository,
|
||||
IEntityRepository entityRepository,
|
||||
IContentTypeRepository contentTypeRepository,
|
||||
IIOHelper ioHelper,
|
||||
ILocalizedTextService localizedTextService,
|
||||
ILocalizationService localizationService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer)
|
||||
: this(
|
||||
dataValueEditorFactory,
|
||||
provider,
|
||||
loggerFactory,
|
||||
eventMessagesFactory,
|
||||
dataTypeRepository,
|
||||
dataTypeContainerRepository,
|
||||
auditRepository,
|
||||
entityRepository,
|
||||
contentTypeRepository,
|
||||
ioHelper,
|
||||
localizedTextService,
|
||||
localizationService,
|
||||
shortStringHelper,
|
||||
jsonSerializer,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IEditorConfigurationParser>())
|
||||
{
|
||||
_dataValueEditorFactory = dataValueEditorFactory;
|
||||
_dataTypeRepository = dataTypeRepository;
|
||||
_dataTypeContainerRepository = dataTypeContainerRepository;
|
||||
_auditRepository = auditRepository;
|
||||
_entityRepository = entityRepository;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_ioHelper = ioHelper;
|
||||
_localizedTextService = localizedTextService;
|
||||
_localizationService = localizationService;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor that takes less parameters. Will be removed in V15.")]
|
||||
public DataTypeService(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
ICoreScopeProvider provider,
|
||||
@@ -98,31 +49,55 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IEditorConfigurationParser editorConfigurationParser)
|
||||
: this(
|
||||
provider,
|
||||
loggerFactory,
|
||||
eventMessagesFactory,
|
||||
dataTypeRepository,
|
||||
dataValueEditorFactory,
|
||||
auditRepository,
|
||||
contentTypeRepository,
|
||||
ioHelper,
|
||||
editorConfigurationParser)
|
||||
{
|
||||
}
|
||||
|
||||
public DataTypeService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDataTypeRepository dataTypeRepository,
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
IAuditRepository auditRepository,
|
||||
IContentTypeRepository contentTypeRepository,
|
||||
IIOHelper ioHelper,
|
||||
IEditorConfigurationParser editorConfigurationParser)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_dataValueEditorFactory = dataValueEditorFactory;
|
||||
_dataTypeRepository = dataTypeRepository;
|
||||
_dataTypeContainerRepository = dataTypeContainerRepository;
|
||||
_auditRepository = auditRepository;
|
||||
_entityRepository = entityRepository;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_ioHelper = ioHelper;
|
||||
_localizedTextService = localizedTextService;
|
||||
_localizationService = localizationService;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_editorConfigurationParser = editorConfigurationParser;
|
||||
|
||||
// resolve dependencies for obsolete methods through the static service provider, so they don't pollute the constructor signature
|
||||
_dataTypeContainerService = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerService>();
|
||||
_dataTypeContainerRepository = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerRepository>();
|
||||
}
|
||||
|
||||
#region Containers
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public Attempt<OperationResult<OperationResultType, EntityContainer>?> CreateContainer(int parentId, Guid key, string name, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using (ScopeProvider.CreateCoreScope(autoComplete:true))
|
||||
{
|
||||
try
|
||||
{
|
||||
Guid? parentKey = parentId > 0 ? _dataTypeContainerRepository.Get(parentId)?.Key : null;
|
||||
|
||||
var container = new EntityContainer(Constants.ObjectTypes.DataType)
|
||||
{
|
||||
Name = name,
|
||||
@@ -131,21 +106,15 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
Key = key
|
||||
};
|
||||
|
||||
var savingEntityContainerNotification = new EntityContainerSavingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingEntityContainerNotification))
|
||||
Attempt<EntityContainer, DataTypeContainerOperationStatus> result = _dataTypeContainerService.CreateAsync(container, parentKey, userId).GetAwaiter().GetResult();
|
||||
|
||||
// mimic old service behavior
|
||||
return result.Status switch
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs, container);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerSavedNotification(container, evtMsgs).WithStateFrom(savingEntityContainerNotification));
|
||||
|
||||
// TODO: Audit trail ?
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs, container);
|
||||
DataTypeContainerOperationStatus.CancelledByNotification => OperationResult.Attempt.Cancel(evtMsgs, container),
|
||||
DataTypeContainerOperationStatus.Success => OperationResult.Attempt.Succeed(evtMsgs, container),
|
||||
_ => throw new InvalidOperationException($"Invalid operation status: {result.Status}")
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -154,24 +123,25 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public EntityContainer? GetContainer(int containerId)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _dataTypeContainerRepository.Get(containerId);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public EntityContainer? GetContainer(Guid containerId)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _dataTypeContainerRepository.Get(containerId);
|
||||
}
|
||||
=> _dataTypeContainerService.GetAsync(containerId).GetAwaiter().GetResult();
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public IEnumerable<EntityContainer> GetContainers(string name, int level)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _dataTypeContainerRepository.Get(name, level);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public IEnumerable<EntityContainer> GetContainers(IDataType dataType)
|
||||
{
|
||||
var ancestorIds = dataType.Path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries)
|
||||
@@ -186,51 +156,43 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return GetContainers(ancestorIds);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public IEnumerable<EntityContainer> GetContainers(int[] containerIds)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _dataTypeContainerRepository.GetMany(containerIds);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public Attempt<OperationResult?> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
if (container.ContainedObjectType != Constants.ObjectTypes.DataType)
|
||||
using (ScopeProvider.CreateCoreScope(autoComplete:true))
|
||||
{
|
||||
var ex = new InvalidOperationException("Not a " + Constants.ObjectTypes.DataType + " container.");
|
||||
return OperationResult.Attempt.Fail(evtMsgs, ex);
|
||||
}
|
||||
var isNew = container.Id == 0;
|
||||
Guid? parentKey = isNew && container.ParentId > 0 ? _dataTypeContainerRepository.Get(container.ParentId)?.Key : null;
|
||||
|
||||
if (container.HasIdentity && container.IsPropertyDirty("ParentId"))
|
||||
{
|
||||
var ex = new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
|
||||
return OperationResult.Attempt.Fail(evtMsgs, ex);
|
||||
}
|
||||
Attempt<EntityContainer, DataTypeContainerOperationStatus> result = isNew
|
||||
? _dataTypeContainerService.CreateAsync(container, parentKey, userId).GetAwaiter().GetResult()
|
||||
: _dataTypeContainerService.UpdateAsync(container, userId).GetAwaiter().GetResult();
|
||||
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
{
|
||||
var savingEntityContainerNotification = new EntityContainerSavingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingEntityContainerNotification))
|
||||
// mimic old service behavior
|
||||
return result.Status switch
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerSavedNotification(container, evtMsgs).WithStateFrom(savingEntityContainerNotification));
|
||||
scope.Complete();
|
||||
DataTypeContainerOperationStatus.Success => OperationResult.Attempt.Succeed(evtMsgs),
|
||||
DataTypeContainerOperationStatus.CancelledByNotification => OperationResult.Attempt.Cancel(evtMsgs),
|
||||
DataTypeContainerOperationStatus.ParentNotFound => OperationResult.Attempt.Fail(evtMsgs, new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.")),
|
||||
DataTypeContainerOperationStatus.InvalidObjectType => OperationResult.Attempt.Fail(evtMsgs, new InvalidOperationException("Not a " + Constants.ObjectTypes.DataType + " container.")),
|
||||
_ => OperationResult.Attempt.Fail(evtMsgs, new InvalidOperationException($"Invalid operation status: {result.Status}"))
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Audit trail ?
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public Attempt<OperationResult?> DeleteContainer(int containerId, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using (ScopeProvider.CreateCoreScope(autoComplete:true))
|
||||
{
|
||||
EntityContainer? container = _dataTypeContainerRepository.Get(containerId);
|
||||
if (container == null)
|
||||
@@ -238,36 +200,23 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return OperationResult.Attempt.NoOperation(evtMsgs);
|
||||
}
|
||||
|
||||
// 'container' here does not know about its children, so we need
|
||||
// to get it again from the entity repository, as a light entity
|
||||
IEntitySlim? entity = _entityRepository.Get(container.Id);
|
||||
if (entity?.HasChildren ?? false)
|
||||
Attempt<EntityContainer?, DataTypeContainerOperationStatus> result = _dataTypeContainerService.DeleteAsync(container.Key, userId).GetAwaiter().GetResult();
|
||||
// mimic old service behavior
|
||||
return result.Status switch
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.Fail(new OperationResult(OperationResultType.FailedCannot, evtMsgs));
|
||||
}
|
||||
|
||||
var deletingEntityContainerNotification = new EntityContainerDeletingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.Fail(new OperationResult(OperationResultType.FailedCancelledByEvent, evtMsgs));
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Delete(container);
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerDeletedNotification(container, evtMsgs).WithStateFrom(deletingEntityContainerNotification));
|
||||
scope.Complete();
|
||||
DataTypeContainerOperationStatus.Success => OperationResult.Attempt.Succeed(evtMsgs),
|
||||
DataTypeContainerOperationStatus.NotEmpty => Attempt.Fail(new OperationResult(OperationResultType.FailedCannot, evtMsgs)),
|
||||
DataTypeContainerOperationStatus.CancelledByNotification => Attempt.Fail(new OperationResult(OperationResultType.FailedCancelledByEvent, evtMsgs)),
|
||||
_ => OperationResult.Attempt.Fail(evtMsgs, new InvalidOperationException($"Invalid operation status: {result.Status}"))
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Audit trail ?
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
}
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
public Attempt<OperationResult<OperationResultType, EntityContainer>?> RenameContainer(int id, string name, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using (ScopeProvider.CreateCoreScope(autoComplete:true))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -281,19 +230,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
container.Name = name;
|
||||
|
||||
var renamingEntityContainerNotification = new EntityContainerRenamingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(renamingEntityContainerNotification))
|
||||
Attempt<EntityContainer, DataTypeContainerOperationStatus> result = _dataTypeContainerService.UpdateAsync(container, userId).GetAwaiter().GetResult();
|
||||
// mimic old service behavior
|
||||
return result.Status switch
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs, container);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new EntityContainerRenamedNotification(container, evtMsgs).WithStateFrom(renamingEntityContainerNotification));
|
||||
|
||||
return OperationResult.Attempt.Succeed(OperationResultType.Success, evtMsgs, container);
|
||||
DataTypeContainerOperationStatus.Success => OperationResult.Attempt.Succeed(OperationResultType.Success, evtMsgs, container),
|
||||
DataTypeContainerOperationStatus.CancelledByNotification => OperationResult.Attempt.Cancel(evtMsgs, container),
|
||||
_ => OperationResult.Attempt.Fail<EntityContainer>(evtMsgs, new InvalidOperationException($"Invalid operation status: {result.Status}"))
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -309,12 +253,17 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the <see cref="IDataType"/></param>
|
||||
/// <returns><see cref="IDataType"/></returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
public IDataType? GetDataType(string name)
|
||||
=> GetAsync(name).GetAwaiter().GetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IDataType?> GetAsync(string name)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IDataType? dataType = _dataTypeRepository.Get(Query<IDataType>().Where(x => x.Name == name))?.FirstOrDefault();
|
||||
ConvertMissingEditorOfDataTypeToLabel(dataType);
|
||||
return dataType;
|
||||
return await Task.FromResult(dataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -322,6 +271,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="IDataType"/></param>
|
||||
/// <returns><see cref="IDataType"/></returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
public IDataType? GetDataType(int id)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
@@ -335,13 +285,17 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="id">Unique guid Id of the DataType</param>
|
||||
/// <returns><see cref="IDataType"/></returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
public IDataType? GetDataType(Guid id)
|
||||
=> GetAsync(id).GetAwaiter().GetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IDataType?> GetAsync(Guid id)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => x.Key == id);
|
||||
IDataType? dataType = _dataTypeRepository.Get(query).FirstOrDefault();
|
||||
IDataType? dataType = GetDataTypeFromRepository(id);
|
||||
ConvertMissingEditorOfDataTypeToLabel(dataType);
|
||||
return dataType;
|
||||
return await Task.FromResult(dataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -349,13 +303,18 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="propertyEditorAlias">Alias of the property editor</param>
|
||||
/// <returns>Collection of <see cref="IDataType"/> objects with a matching control id</returns>
|
||||
[Obsolete("Please use GetByEditorAliasAsync. Will be removed in V15.")]
|
||||
public IEnumerable<IDataType> GetByEditorAlias(string propertyEditorAlias)
|
||||
=> GetByEditorAliasAsync(propertyEditorAlias).GetAwaiter().GetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<IDataType>> GetByEditorAliasAsync(string propertyEditorAlias)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => x.EditorAlias == propertyEditorAlias);
|
||||
IEnumerable<IDataType> dataType = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataType);
|
||||
return dataType;
|
||||
IEnumerable<IDataType> dataTypes = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
return await Task.FromResult(dataTypes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -485,19 +444,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Id of the user issuing the save</param>
|
||||
public void Save(IDataType dataType, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
dataType.CreatorId = userId;
|
||||
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
var saveEventArgs = new SaveEventArgs<IDataType>(dataType);
|
||||
|
||||
var savingDataTypeNotification = new DataTypeSavingNotification(dataType, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// mimic old service behavior
|
||||
if (string.IsNullOrWhiteSpace(dataType.Name))
|
||||
{
|
||||
throw new ArgumentException("Cannot save datatype with empty name.");
|
||||
@@ -508,19 +455,44 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
throw new InvalidOperationException("Name cannot be more than 255 characters in length.");
|
||||
}
|
||||
|
||||
_dataTypeRepository.Save(dataType);
|
||||
|
||||
scope.Notifications.Publish(new DataTypeSavedNotification(dataType, evtMsgs).WithStateFrom(savingDataTypeNotification));
|
||||
|
||||
Audit(AuditType.Save, userId, dataType.Id);
|
||||
scope.Complete();
|
||||
Attempt<IDataType, DataTypeOperationStatus> result = SaveAsync(
|
||||
dataType,
|
||||
() => DataTypeOperationStatus.Success,
|
||||
userId,
|
||||
AuditType.Sort).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IDataType, DataTypeOperationStatus>> CreateAsync(IDataType dataType, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
if (dataType.Id != 0)
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.InvalidId, dataType);
|
||||
}
|
||||
|
||||
return await SaveAsync(dataType, () => DataTypeOperationStatus.Success, userId, AuditType.New);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IDataType, DataTypeOperationStatus>> UpdateAsync(IDataType dataType, int userId = Constants.Security.SuperUserId)
|
||||
=> await SaveAsync(
|
||||
dataType,
|
||||
() =>
|
||||
{
|
||||
IDataType? current = _dataTypeRepository.Get(dataType.Id);
|
||||
return current == null
|
||||
? DataTypeOperationStatus.NotFound
|
||||
: DataTypeOperationStatus.Success;
|
||||
},
|
||||
userId,
|
||||
AuditType.New);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a collection of <see cref="IDataType"/>
|
||||
/// </summary>
|
||||
/// <param name="dataTypeDefinitions"><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(IEnumerable<IDataType> dataTypeDefinitions, int userId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
@@ -557,14 +529,25 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="dataType"><see cref="IDataType"/> to delete</param>
|
||||
/// <param name="userId">Optional Id of the user issuing the deletion</param>
|
||||
public void Delete(IDataType dataType, int userId = Constants.Security.SuperUserId)
|
||||
=> DeleteAsync(dataType.Key, userId).GetAwaiter().GetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IDataType?, DataTypeOperationStatus>> DeleteAsync(Guid id, int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
var deletingDataTypeNotification = new DataTypeDeletingNotification(dataType, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingDataTypeNotification))
|
||||
|
||||
IDataType? dataType = GetDataTypeFromRepository(id);
|
||||
if (dataType == null)
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.NotFound, dataType);
|
||||
}
|
||||
|
||||
var deletingDataTypeNotification = new DataTypeDeletingNotification(dataType, eventMessages);
|
||||
if (await scope.Notifications.PublishCancelableAsync(deletingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
return Attempt.FailWithStatus<IDataType?, DataTypeOperationStatus>(DataTypeOperationStatus.CancelledByNotification, dataType);
|
||||
}
|
||||
|
||||
// find ContentTypes using this IDataTypeDefinition on a PropertyType, and delete
|
||||
@@ -599,19 +582,37 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_dataTypeRepository.Delete(dataType);
|
||||
|
||||
scope.Notifications.Publish(new DataTypeDeletedNotification(dataType, evtMsgs).WithStateFrom(deletingDataTypeNotification));
|
||||
scope.Notifications.Publish(new DataTypeDeletedNotification(dataType, eventMessages).WithStateFrom(deletingDataTypeNotification));
|
||||
|
||||
Audit(AuditType.Delete, userId, dataType.Id);
|
||||
|
||||
scope.Complete();
|
||||
|
||||
return Attempt.SucceedWithStatus<IDataType?, DataTypeOperationStatus>(DataTypeOperationStatus.Success, dataType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Please use GetReferencesAsync. Will be deleted in V15.")]
|
||||
public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete:true);
|
||||
return _dataTypeRepository.FindUsages(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IReadOnlyDictionary<Udi, IEnumerable<string>>, DataTypeOperationStatus>> GetReferencesAsync(Guid id)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete:true);
|
||||
IDataType? dataType = GetDataTypeFromRepository(id);
|
||||
if (dataType == null)
|
||||
{
|
||||
return Attempt.FailWithStatus<IReadOnlyDictionary<Udi, IEnumerable<string>>, DataTypeOperationStatus>(DataTypeOperationStatus.NotFound, new Dictionary<Udi, IEnumerable<string>>());
|
||||
}
|
||||
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> usages = _dataTypeRepository.FindUsages(dataType.Id);
|
||||
return await Task.FromResult(Attempt.SucceedWithStatus(DataTypeOperationStatus.Success, usages));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ValidationResult> ValidateConfigurationData(IDataType dataType)
|
||||
{
|
||||
@@ -624,10 +625,67 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
: configurationEditor.Validate(dataType.ConfigurationData);
|
||||
}
|
||||
|
||||
private void Audit(AuditType type, int userId, int objectId)
|
||||
private async Task<Attempt<IDataType, DataTypeOperationStatus>> SaveAsync(
|
||||
IDataType dataType,
|
||||
Func<DataTypeOperationStatus> operationValidation,
|
||||
int userId,
|
||||
AuditType auditType)
|
||||
{
|
||||
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
|
||||
IEnumerable<ValidationResult> validationResults = ValidateConfigurationData(dataType);
|
||||
if (validationResults.Any())
|
||||
{
|
||||
LoggerFactory
|
||||
.CreateLogger<DataTypeService>()
|
||||
.LogError($"Invalid data type configuration for data type: {dataType.Name} - validation error messages: {string.Join(Environment.NewLine, validationResults.Select(r => r.ErrorMessage))}");
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.InvalidConfiguration, dataType);
|
||||
}
|
||||
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
dataType.CreatorId = userId;
|
||||
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
|
||||
DataTypeOperationStatus status = operationValidation();
|
||||
if (status != DataTypeOperationStatus.Success)
|
||||
{
|
||||
return Attempt.FailWithStatus(status, dataType);
|
||||
}
|
||||
|
||||
var savingDataTypeNotification = new DataTypeSavingNotification(dataType, eventMessages);
|
||||
if (await scope.Notifications.PublishCancelableAsync(savingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.CancelledByNotification, dataType);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dataType.Name))
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.InvalidName, dataType);
|
||||
}
|
||||
|
||||
if (dataType.Name is { Length: > 255 })
|
||||
{
|
||||
return Attempt.FailWithStatus(DataTypeOperationStatus.InvalidName, dataType);
|
||||
}
|
||||
|
||||
_dataTypeRepository.Save(dataType);
|
||||
|
||||
scope.Notifications.Publish(new DataTypeSavedNotification(dataType, eventMessages).WithStateFrom(savingDataTypeNotification));
|
||||
|
||||
Audit(auditType, userId, dataType.Id);
|
||||
scope.Complete();
|
||||
|
||||
return Attempt.SucceedWithStatus(DataTypeOperationStatus.Success, dataType);
|
||||
}
|
||||
|
||||
private IDataType? GetDataTypeFromRepository(Guid id)
|
||||
{
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => x.Key == id);
|
||||
IDataType? dataType = _dataTypeRepository.Get(query).FirstOrDefault();
|
||||
return dataType;
|
||||
}
|
||||
|
||||
private void Audit(AuditType type, int userId, int objectId)
|
||||
=> _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
|
||||
}
|
||||
}
|
||||
|
||||
47
src/Umbraco.Core/Services/IDataTypeContainerService.cs
Normal file
47
src/Umbraco.Core/Services/IDataTypeContainerService.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
public interface IDataTypeContainerService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a data type container
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the data type container to get.</param>
|
||||
/// <returns></returns>
|
||||
Task<EntityContainer?> GetAsync(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent container of a data type container
|
||||
/// </summary>
|
||||
/// <param name="container">The container whose parent container to get.</param>
|
||||
/// <returns></returns>
|
||||
Task<EntityContainer?> GetParentAsync(EntityContainer container);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new data type container
|
||||
/// </summary>
|
||||
/// <param name="container">The container to create.</param>
|
||||
/// <param name="parentId">The ID of the parent container to create the new container under.</param>
|
||||
/// <param name="userId">The ID of the user issuing the creation.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>If no parent ID is supplied, the container will be created at the data type tree root.</remarks>
|
||||
Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> CreateAsync(EntityContainer container, Guid? parentId = null, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing data type container
|
||||
/// </summary>
|
||||
/// <param name="container">The container to create.</param>
|
||||
/// <param name="userId">The ID of the user issuing the update.</param>
|
||||
/// <returns></returns>
|
||||
Task<Attempt<EntityContainer, DataTypeContainerOperationStatus>> UpdateAsync(EntityContainer container, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a data type container
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the container to delete.</param>
|
||||
/// <param name="userId">The ID of the user issuing the deletion.</param>
|
||||
/// <returns></returns>
|
||||
Task<Attempt<EntityContainer?, DataTypeContainerOperationStatus>> DeleteAsync(Guid id, int userId = Constants.Security.SuperUserId);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
@@ -14,24 +15,41 @@ public interface IDataTypeService : IService
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Please use GetReferencesAsync. Will be deleted in V15.")]
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a dictionary of content type <see cref="Udi" />s and the property type aliases that use a <see cref="IDataType" />
|
||||
/// </summary>
|
||||
/// <param name="id">The guid Id of the <see cref="IDataType" /></param>
|
||||
/// <returns></returns>
|
||||
Task<Attempt<IReadOnlyDictionary<Udi, IEnumerable<string>>, DataTypeOperationStatus>> GetReferencesAsync(Guid id);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>?> CreateContainer(int parentId, Guid key, string name, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
Attempt<OperationResult?> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
EntityContainer? GetContainer(int containerId);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
EntityContainer? GetContainer(Guid containerId);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
IEnumerable<EntityContainer> GetContainers(string folderName, int level);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
IEnumerable<EntityContainer> GetContainers(IDataType dataType);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
IEnumerable<EntityContainer> GetContainers(int[] containerIds);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
Attempt<OperationResult?> DeleteContainer(int containerId, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
[Obsolete("Please use IDataTypeContainerService for all data type container operations. Will be removed in V15.")]
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>?> RenameContainer(int id, string name, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
@@ -41,6 +59,7 @@ public interface IDataTypeService : IService
|
||||
/// <returns>
|
||||
/// <see cref="IDataType" />
|
||||
/// </returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
IDataType? GetDataType(string name);
|
||||
|
||||
/// <summary>
|
||||
@@ -50,6 +69,7 @@ public interface IDataTypeService : IService
|
||||
/// <returns>
|
||||
/// <see cref="IDataType" />
|
||||
/// </returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
IDataType? GetDataType(int id);
|
||||
|
||||
/// <summary>
|
||||
@@ -59,8 +79,27 @@ public interface IDataTypeService : IService
|
||||
/// <returns>
|
||||
/// <see cref="IDataType" />
|
||||
/// </returns>
|
||||
[Obsolete("Please use GetAsync. Will be removed in V15.")]
|
||||
IDataType? GetDataType(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IDataType" /> by its Name
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the <see cref="IDataType" /></param>
|
||||
/// <returns>
|
||||
/// <see cref="IDataType" />
|
||||
/// </returns>
|
||||
Task<IDataType?> GetAsync(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IDataType" /> by its unique guid Id
|
||||
/// </summary>
|
||||
/// <param name="id">Unique guid Id of the DataType</param>
|
||||
/// <returns>
|
||||
/// <see cref="IDataType" />
|
||||
/// </returns>
|
||||
Task<IDataType?> GetAsync(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IDataType" /> objects or those with the ids passed in
|
||||
/// </summary>
|
||||
@@ -73,6 +112,7 @@ public interface IDataTypeService : IService
|
||||
/// </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.")]
|
||||
void Save(IDataType dataType, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
@@ -80,8 +120,23 @@ public interface IDataTypeService : IService
|
||||
/// </summary>
|
||||
/// <param name="dataTypeDefinitions"><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.")]
|
||||
void Save(IEnumerable<IDataType> dataTypeDefinitions, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IDataType" />
|
||||
/// </summary>
|
||||
/// <param name="dataType"><see cref="IDataType" /> to create</param>
|
||||
/// <param name="userId">Id of the user issuing the creation</param>
|
||||
Task<Attempt<IDataType, DataTypeOperationStatus>> CreateAsync(IDataType dataType, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing <see cref="IDataType" />
|
||||
/// </summary>
|
||||
/// <param name="dataType"><see cref="IDataType" /> to update</param>
|
||||
/// <param name="userId">Id of the user issuing the update</param>
|
||||
Task<Attempt<IDataType, DataTypeOperationStatus>> UpdateAsync(IDataType dataType, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an <see cref="IDataType" />
|
||||
/// </summary>
|
||||
@@ -91,15 +146,35 @@ public interface IDataTypeService : IService
|
||||
/// </remarks>
|
||||
/// <param name="dataType"><see cref="IDataType" /> to delete</param>
|
||||
/// <param name="userId">Id of the user issuing the deletion</param>
|
||||
[Obsolete("Please use DeleteAsync. Will be removed in V15.")]
|
||||
void Delete(IDataType dataType, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an <see cref="IDataType" />
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that deleting a <see cref="IDataType" /> will remove
|
||||
/// all the <see cref="IPropertyType" /> data that references this <see cref="IDataType" />.
|
||||
/// </remarks>
|
||||
/// <param name="id">The guid Id of the <see cref="IDataType" /> to delete</param>
|
||||
/// <param name="userId">Id of the user issuing the deletion</param>
|
||||
Task<Attempt<IDataType?, DataTypeOperationStatus>> DeleteAsync(Guid id, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IDataType" /> by its control Id
|
||||
/// </summary>
|
||||
/// <param name="propertyEditorAlias">Alias of the property editor</param>
|
||||
/// <returns>Collection of <see cref="IDataType" /> objects with a matching control id</returns>
|
||||
[Obsolete("Please use GetByEditorAliasAsync. Will be removed in V15.")]
|
||||
IEnumerable<IDataType> GetByEditorAlias(string propertyEditorAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IDataType" /> for a given property editor
|
||||
/// </summary>
|
||||
/// <param name="propertyEditorAlias">Alias of the property editor</param>
|
||||
/// <returns>Collection of <see cref="IDataType" /> configured for the property editor</returns>
|
||||
Task<IEnumerable<IDataType>> GetByEditorAliasAsync(string propertyEditorAlias);
|
||||
|
||||
Attempt<OperationResult<MoveOperationStatusType>?> Move(IDataType toMove, int parentId);
|
||||
|
||||
[Obsolete("Use the method which specifies the userId parameter")]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
public enum DataTypeContainerOperationStatus
|
||||
{
|
||||
Success,
|
||||
CancelledByNotification,
|
||||
InvalidObjectType,
|
||||
InvalidId,
|
||||
NotFound,
|
||||
ParentNotFound,
|
||||
NotEmpty
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
public enum DataTypeOperationStatus
|
||||
{
|
||||
Success,
|
||||
CancelledByNotification,
|
||||
InvalidConfiguration,
|
||||
InvalidName,
|
||||
InvalidId,
|
||||
NotFound
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services.DataTypeServiceTests.DataTypeService_Can_Persist_New_DataTypeDefinition</Target>
|
||||
<Left>lib/net7.0/Umbraco.Tests.Integration.dll</Left>
|
||||
<Right>lib/net7.0/Umbraco.Tests.Integration.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services.LocalizationServiceTests.Can_Create_DictionaryItem_At_Root_With_Identity</Target>
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Infrastructure.Services;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Tests covering the DataTypeContainerService
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class DataTypeContainerServiceTests : UmbracoIntegrationTest
|
||||
{
|
||||
private IDataTypeContainerService DataTypeContainerService => GetRequiredService<IDataTypeContainerService>();
|
||||
|
||||
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
||||
|
||||
private IDataValueEditorFactory DataValueEditorFactory => GetRequiredService<IDataValueEditorFactory>();
|
||||
|
||||
private IConfigurationEditorJsonSerializer ConfigurationEditorJsonSerializer => GetRequiredService<IConfigurationEditorJsonSerializer>();
|
||||
|
||||
private IEditorConfigurationParser EditorConfigurationParser => GetRequiredService<IEditorConfigurationParser>();
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_Container_At_Root()
|
||||
{
|
||||
EntityContainer toCreate = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
|
||||
var result = await DataTypeContainerService.CreateAsync(toCreate);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(toCreate.Key);
|
||||
Assert.NotNull(created);
|
||||
Assert.AreEqual("Root Container", created.Name);
|
||||
Assert.AreEqual(Constants.System.Root, created.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_Child_Container()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
var result = await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.NotNull(created);
|
||||
Assert.AreEqual("Child Container", created.Name);
|
||||
Assert.AreEqual(root.Id, child.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Update_Container_At_Root()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer toUpdate = await DataTypeContainerService.GetAsync(root.Key);
|
||||
Assert.NotNull(toUpdate);
|
||||
|
||||
toUpdate.Name += " UPDATED";
|
||||
var result = await DataTypeContainerService.UpdateAsync(toUpdate);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var updated = await DataTypeContainerService.GetAsync(toUpdate.Key);
|
||||
Assert.NotNull(updated);
|
||||
Assert.AreEqual("Root Container UPDATED", updated.Name);
|
||||
Assert.AreEqual(Constants.System.Root, updated.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Update_Child_Container()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
|
||||
EntityContainer toUpdate = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.NotNull(toUpdate);
|
||||
|
||||
toUpdate.Name += " UPDATED";
|
||||
var result = await DataTypeContainerService.UpdateAsync(toUpdate);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var updated = await DataTypeContainerService.GetAsync(toUpdate.Key);
|
||||
Assert.NotNull(updated);
|
||||
Assert.AreEqual("Child Container UPDATED", updated.Name);
|
||||
Assert.AreEqual(root.Id, updated.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_Container_At_Root()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(root.Key);
|
||||
Assert.NotNull(created);
|
||||
Assert.AreEqual("Root Container", created.Name);
|
||||
Assert.AreEqual(Constants.System.Root, created.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_Child_Container()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.IsNotNull(created);
|
||||
Assert.AreEqual("Child Container", created.Name);
|
||||
Assert.AreEqual(root.Id, child.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Delete_Container_At_Root()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
var result = await DataTypeContainerService.DeleteAsync(root.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var current = await DataTypeContainerService.GetAsync(root.Key);
|
||||
Assert.IsNull(current);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Delete_Child_Container()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
|
||||
var result = await DataTypeContainerService.DeleteAsync(child.Key);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.Success, result.Status);
|
||||
|
||||
var current = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.IsNull(current);
|
||||
|
||||
current = await DataTypeContainerService.GetAsync(root.Key);
|
||||
Assert.IsNotNull(current);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Create_Child_Container_Below_Invalid_Parent()
|
||||
{
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
var result = await DataTypeContainerService.CreateAsync(child, Guid.NewGuid());
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.ParentNotFound, result.Status);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.IsNull(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Create_Child_Container_With_Explicit_Id()
|
||||
{
|
||||
EntityContainer toCreate = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container", Id = 1234 };
|
||||
|
||||
var result = await DataTypeContainerService.CreateAsync(toCreate);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.InvalidId, result.Status);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(toCreate.Key);
|
||||
Assert.IsNull(created);
|
||||
}
|
||||
|
||||
[TestCase(Constants.ObjectTypes.Strings.DocumentType)]
|
||||
[TestCase(Constants.ObjectTypes.Strings.MediaType)]
|
||||
public async Task Cannot_Create_Container_With_Invalid_Contained_Type(string containedObjectType)
|
||||
{
|
||||
EntityContainer toCreate = new EntityContainer(new Guid(containedObjectType)) { Name = "Root Container" };
|
||||
|
||||
var result = await DataTypeContainerService.CreateAsync(toCreate);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.InvalidObjectType, result.Status);
|
||||
|
||||
var created = await DataTypeContainerService.GetAsync(toCreate.Key);
|
||||
Assert.IsNull(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Update_Container_Parent()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer root2 = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container 2" };
|
||||
await DataTypeContainerService.CreateAsync(root2);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
|
||||
EntityContainer toUpdate = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.IsNotNull(toUpdate);
|
||||
|
||||
toUpdate.ParentId = root2.Id;
|
||||
var result = await DataTypeContainerService.UpdateAsync(toUpdate);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.ParentNotFound, result.Status);
|
||||
|
||||
var current = await DataTypeContainerService.GetAsync(child.Key);
|
||||
Assert.IsNotNull(current);
|
||||
Assert.AreEqual(root.Id, child.ParentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Delete_Container_With_Child_Container()
|
||||
{
|
||||
EntityContainer root = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(root);
|
||||
|
||||
EntityContainer child = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Child Container" };
|
||||
await DataTypeContainerService.CreateAsync(child, root.Key);
|
||||
|
||||
var result = await DataTypeContainerService.DeleteAsync(root.Key);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.NotEmpty, result.Status);
|
||||
|
||||
var current = await DataTypeContainerService.GetAsync(root.Key);
|
||||
Assert.IsNotNull(current);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Delete_Container_With_Child_DataType()
|
||||
{
|
||||
EntityContainer container = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "Root Container" };
|
||||
await DataTypeContainerService.CreateAsync(container);
|
||||
|
||||
IDataType dataType =
|
||||
new DataType(new TextboxPropertyEditor(DataValueEditorFactory, IOHelper, EditorConfigurationParser), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = Guid.NewGuid().ToString(),
|
||||
DatabaseType = ValueStorageType.Nvarchar,
|
||||
ParentId = container.Id
|
||||
};
|
||||
await DataTypeService.CreateAsync(dataType);
|
||||
|
||||
var result = await DataTypeContainerService.DeleteAsync(container.Key);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.NotEmpty, result.Status);
|
||||
|
||||
var currentContainer = await DataTypeContainerService.GetAsync(container.Key);
|
||||
Assert.IsNotNull(currentContainer);
|
||||
|
||||
var currentDataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.IsNotNull(currentDataType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Delete_Non_Existing_Container()
|
||||
{
|
||||
var result = await DataTypeContainerService.DeleteAsync(Guid.NewGuid());
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeContainerOperationStatus.NotFound, result.Status);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
@@ -31,8 +31,10 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
private IConfigurationEditorJsonSerializer ConfigurationEditorJsonSerializer =>
|
||||
GetRequiredService<IConfigurationEditorJsonSerializer>();
|
||||
|
||||
private IEditorConfigurationParser EditorConfigurationParser => GetRequiredService<IEditorConfigurationParser>();
|
||||
|
||||
[Test]
|
||||
public void DataTypeService_Can_Persist_New_DataTypeDefinition()
|
||||
public async Task Can_Create_New_DataTypeDefinition()
|
||||
{
|
||||
// Act
|
||||
IDataType dataType =
|
||||
@@ -41,22 +43,104 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
Name = "Testing Textfield",
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
};
|
||||
DataTypeService.Save(dataType);
|
||||
var result = await DataTypeService.CreateAsync(dataType);
|
||||
Assert.True(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
|
||||
// Assert
|
||||
Assert.That(dataType, Is.Not.Null);
|
||||
Assert.That(dataType.HasIdentity, Is.True);
|
||||
|
||||
dataType = DataTypeService.GetDataType(dataType.Id);
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.That(dataType, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DataTypeService_Can_Delete_Textfield_DataType_And_Clear_Usages()
|
||||
public async Task Can_Update_Existing_DataTypeDefinition()
|
||||
{
|
||||
// Arrange
|
||||
var textfieldId = "Umbraco.Textbox";
|
||||
var dataTypeDefinitions = DataTypeService.GetByEditorAlias(textfieldId);
|
||||
IDataType? dataType = (await DataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.TextBox)).FirstOrDefault();
|
||||
Assert.NotNull(dataType);
|
||||
|
||||
// Act
|
||||
dataType.Name += " UPDATED";
|
||||
var result = await DataTypeService.UpdateAsync(dataType);
|
||||
Assert.True(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
|
||||
// Assert
|
||||
dataType = await DataTypeService.GetAsync(dataType.Key);
|
||||
Assert.NotNull(dataType);
|
||||
Assert.True(dataType.Name.EndsWith(" UPDATED"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_All_By_Editor_Alias()
|
||||
{
|
||||
// Arrange
|
||||
async Task<IDataType> CreateTextBoxDataType()
|
||||
{
|
||||
IDataType dataType =
|
||||
new DataType(new TextboxPropertyEditor(DataValueEditorFactory, IOHelper, EditorConfigurationParser), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = Guid.NewGuid().ToString(),
|
||||
DatabaseType = ValueStorageType.Nvarchar
|
||||
};
|
||||
var result = await DataTypeService.CreateAsync(dataType);
|
||||
Assert.True(result.Success);
|
||||
return result.Result;
|
||||
}
|
||||
|
||||
IDataType dataType1 = await CreateTextBoxDataType();
|
||||
IDataType dataType2 = await CreateTextBoxDataType();
|
||||
IDataType dataType3 = await CreateTextBoxDataType();
|
||||
|
||||
// Act
|
||||
IEnumerable<IDataType> dataTypes = await DataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.TextBox);
|
||||
|
||||
// Assert
|
||||
Assert.True(dataTypes.Count() >= 3);
|
||||
Assert.True(dataTypes.All(dataType => dataType.EditorAlias == Constants.PropertyEditors.Aliases.TextBox));
|
||||
Assert.NotNull(dataTypes.FirstOrDefault(dataType => dataType.Key == dataType1.Key));
|
||||
Assert.NotNull(dataTypes.FirstOrDefault(dataType => dataType.Key == dataType2.Key));
|
||||
Assert.NotNull(dataTypes.FirstOrDefault(dataType => dataType.Key == dataType3.Key));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_By_Id()
|
||||
{
|
||||
// Arrange
|
||||
IDataType? dataType = (await DataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.TextBox)).FirstOrDefault();
|
||||
Assert.NotNull(dataType);
|
||||
|
||||
// Act
|
||||
IDataType? actual = await DataTypeService.GetAsync(dataType.Key);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actual);
|
||||
Assert.AreEqual(dataType.Key, actual.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_By_Name()
|
||||
{
|
||||
// Arrange
|
||||
IDataType? dataType = (await DataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.TextBox)).FirstOrDefault();
|
||||
Assert.NotNull(dataType);
|
||||
|
||||
// Act
|
||||
IDataType? actual = await DataTypeService.GetAsync(dataType.Name);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actual);
|
||||
Assert.AreEqual(dataType.Key, actual.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataTypeService_Can_Delete_Textfield_DataType_And_Clear_Usages()
|
||||
{
|
||||
// Arrange
|
||||
var dataTypeDefinitions = await DataTypeService.GetByEditorAliasAsync(Constants.PropertyEditors.Aliases.TextBox);
|
||||
var template = TemplateBuilder.CreateTextPageTemplate();
|
||||
FileService.SaveTemplate(template);
|
||||
var doctype =
|
||||
@@ -65,10 +149,14 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
|
||||
// Act
|
||||
var definition = dataTypeDefinitions.First();
|
||||
var definitionId = definition.Id;
|
||||
DataTypeService.Delete(definition);
|
||||
var definitionKey = definition.Key;
|
||||
var result = await DataTypeService.DeleteAsync(definitionKey);
|
||||
Assert.True(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.Success, result.Status);
|
||||
Assert.NotNull(result.Result);
|
||||
Assert.AreEqual(definitionKey, result.Result.Key);
|
||||
|
||||
var deletedDefinition = DataTypeService.GetDataType(definitionId);
|
||||
var deletedDefinition = await DataTypeService.GetAsync(definitionKey);
|
||||
|
||||
// Assert
|
||||
Assert.That(deletedDefinition, Is.Null);
|
||||
@@ -79,6 +167,44 @@ public class DataTypeServiceTests : UmbracoIntegrationTest
|
||||
Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Create_DataType_With_Empty_Name()
|
||||
{
|
||||
// Act
|
||||
var dataTypeDefinition =
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = string.Empty,
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await DataTypeService.CreateAsync(dataTypeDefinition);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.InvalidName, result.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Create_DataType_With_Too_Long_Name()
|
||||
{
|
||||
// Act
|
||||
var dataTypeDefinition =
|
||||
new DataType(new LabelPropertyEditor(DataValueEditorFactory, IOHelper), ConfigurationEditorJsonSerializer)
|
||||
{
|
||||
Name = new string('a', 256),
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await DataTypeService.CreateAsync(dataTypeDefinition);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(DataTypeOperationStatus.InvalidName, result.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cannot_Save_DataType_With_Empty_Name()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user