V13: relation type controller (#13856)

* Create RelationTypeControllerBase.cs

* Implement ByKey endpoint

* Move controllers

* Implement ByRelationTypeKeyRelationController

* Refactor relation service to use skip/take in interface

* change httpget

* Implement object types endpoint

* Add ObjectTypeViewModelFactory.cs

* Change signature to IActionResult

* Implement CreateRelationTypeController.cs

* Implement CreateRelationTypeController.cs

* Add optional key parameter to RelationType

* Add optional key to RelationTypeSavingViewModel

* Update ProducsResponseType

* Implement UpdateRelationTypeController

* Update routes

* Implement DeleteRelationTypeController

* Remove key from UpdatingViewmoDel

* Implement UpdatingViewModel factory method

* Update relationType factory

* Fix paging for object types

* Implement CreateAsync

* Update create action to use CreateAsync

* Implement update async

* Add default implementations to avoid breaking changes

* actually use userIds

* Add ProducesResponseType status 400 to endpoints

* Implement new DeleteAsync method

* Update OpenApi.json

* Add mapping to DI

* Update 404 to return proper HTTP status

* Add not found to ByKey action

* Rename viewmodels to request/response

* Update OpenApi.json

* All RelationServiceTests.cs

* Add allowed object types test

* Fix grouping

* Implement AllowedRelationObjectTypesService.cs

* Refactor view model factory to use new service

* Implement non-happy path tests

* Implement allowed child/parent object types

* refactor test to return proper relationTypeOperationStatus

* Add can create relation type with key

* Add Id test

* formatting

* Refactor service to have validation logic in save method

* move object types endpoint to own silo

* Delete unused using

* Update OpenApi.json

* Update OpenApi.json

* Update tests to user proper child/parent

* Implement RelationTypeBaseModel

* Update OpenApi.json

* Use array instead of enumerable

* Rename view model factory to presentation factory

* Rename GetPagedByRelationTypeKey

* Rename IRelationViewModelFactory

* Rename IRelationTypeViewModelFactory

* Update error description

* Update OpenApi.json

* Update OpenApi.json

---------

Co-authored-by: Zeegaan <nge@umbraco.dk>
Co-authored-by: Nikolaj <nikolajlauridsen@protonmail.ch>
This commit is contained in:
Nikolaj Geisle
2023-03-01 08:51:17 +01:00
committed by GitHub
parent a00421602e
commit 27ca36db53
33 changed files with 1282 additions and 17 deletions

View File

@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
namespace Umbraco.Cms.Api.Management.Controllers.ObjectTypes;
public class AllowedObjectTypesController : ObjectTypesControllerBase
{
private readonly IObjectTypePresentationFactory _objectTypePresentationFactory;
public AllowedObjectTypesController(IObjectTypePresentationFactory objectTypePresentationFactory) => _objectTypePresentationFactory = objectTypePresentationFactory;
[HttpGet]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<ObjectTypeResponseModel>), StatusCodes.Status200OK)]
public async Task<IActionResult> Allowed(int skip = 0, int take = 100)
{
ObjectTypeResponseModel[] objectTypes = _objectTypePresentationFactory.Create().ToArray();
return await Task.FromResult(Ok(new PagedViewModel<ObjectTypeResponseModel>
{
Total = objectTypes.Length,
Items = objectTypes.Skip(skip).Take(take),
}));
}
}

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Routing;
namespace Umbraco.Cms.Api.Management.Controllers.ObjectTypes;
[ApiController]
[VersionedApiBackOfficeRoute("object-types")]
[ApiExplorerSettings(GroupName = "Object Types")]
[ApiVersion("1.0")]
public class ObjectTypesControllerBase : ManagementApiControllerBase
{
}

View File

@@ -12,14 +12,14 @@ namespace Umbraco.Cms.Api.Management.Controllers.Relation;
public class ByChildRelationController : RelationControllerBase
{
private readonly IRelationService _relationService;
private readonly IRelationViewModelFactory _relationViewModelFactory;
private readonly IRelationPresentationFactory _relationPresentationFactory;
public ByChildRelationController(
IRelationService relationService,
IRelationViewModelFactory relationViewModelFactory)
IRelationPresentationFactory relationPresentationFactory)
{
_relationService = relationService;
_relationViewModelFactory = relationViewModelFactory;
_relationPresentationFactory = relationPresentationFactory;
}
[HttpGet("child-relation/{childId:int}")]
@@ -34,12 +34,12 @@ public class ByChildRelationController : RelationControllerBase
{
if (string.IsNullOrWhiteSpace(relationTypeAlias) == false)
{
result = _relationViewModelFactory.CreateMultiple(relations.Where(x =>
result = _relationPresentationFactory.CreateMultiple(relations.Where(x =>
x.RelationType.Alias.InvariantEquals(relationTypeAlias))).ToArray();
}
else
{
result = _relationViewModelFactory.CreateMultiple(relations).ToArray();
result = _relationPresentationFactory.CreateMultiple(relations).ToArray();
}
}

View File

@@ -10,12 +10,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Relation;
public class ByIdRelationController : RelationControllerBase
{
private readonly IRelationService _relationService;
private readonly IRelationViewModelFactory _relationViewModelFactory;
private readonly IRelationPresentationFactory _relationPresentationFactory;
public ByIdRelationController(IRelationService relationService, IRelationViewModelFactory relationViewModelFactory)
public ByIdRelationController(IRelationService relationService, IRelationPresentationFactory relationPresentationFactory)
{
_relationService = relationService;
_relationViewModelFactory = relationViewModelFactory;
_relationPresentationFactory = relationPresentationFactory;
}
[HttpGet("{id:int}")]
@@ -30,6 +30,6 @@ public class ByIdRelationController : RelationControllerBase
return NotFound();
}
return await Task.FromResult(Ok(_relationViewModelFactory.Create(relation)));
return await Task.FromResult(Ok(_relationPresentationFactory.Create(relation)));
}
}

View File

@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Relation;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.New.Cms.Core.Models;
namespace Umbraco.Cms.Api.Management.Controllers.Relation;
public class ByRelationTypeKeyRelationController : RelationControllerBase
{
private readonly IRelationService _relationService;
private readonly IRelationPresentationFactory _relationPresentationFactory;
public ByRelationTypeKeyRelationController(IRelationService relationService, IRelationPresentationFactory relationPresentationFactory)
{
_relationService = relationService;
_relationPresentationFactory = relationPresentationFactory;
}
[HttpGet("type/{key:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<RelationViewModel>), StatusCodes.Status200OK)]
public async Task<IActionResult> ByRelationTypeKey(Guid key, int skip = 0, int take = 100)
{
Attempt<PagedModel<IRelation>, RelationOperationStatus> relationsAttempt = await _relationService.GetPagedByRelationTypeKey(key, skip, take);
if (relationsAttempt.Success is false)
{
return await Task.FromResult(RelationOperationStatusResult(relationsAttempt.Status));
}
IEnumerable<RelationViewModel> mappedRelations = relationsAttempt.Result.Items.Select(_relationPresentationFactory.Create);
return await Task.FromResult(Ok(new PagedViewModel<RelationViewModel>
{
Total = relationsAttempt.Result.Total,
Items = mappedRelations,
}));
}
}

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.Builders;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Api.Management.Controllers.Relation;
@@ -10,5 +12,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Relation;
// TODO: Implement Authentication
public abstract class RelationControllerBase : ManagementApiControllerBase
{
protected IActionResult RelationOperationStatusResult(RelationOperationStatus status) =>
status switch
{
RelationOperationStatus.RelationTypeNotFound => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Relation type not found")
.WithDetail("The relation type could not be found.")
.Build()),
};
}

View File

@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Controllers.RelationType.Query;
public class ByKeyRelationTypeController : RelationTypeControllerBase
{
private readonly IRelationService _relationService;
private readonly IUmbracoMapper _mapper;
public ByKeyRelationTypeController(IRelationService relationService, IUmbracoMapper mapper)
{
_relationService = relationService;
_mapper = mapper;
}
[HttpGet("{key:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(RelationTypeResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> ByKey(Guid key)
{
IRelationType? relationType = _relationService.GetRelationTypeById(key);
if (relationType is null)
{
return NotFound();
}
RelationTypeResponseModel mappedRelationType = _mapper.Map<RelationTypeResponseModel>(relationType)!;
return await Task.FromResult(Ok(mappedRelationType));
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
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.RelationType.Query;
public class CreateRelationTypeController : RelationTypeControllerBase
{
private readonly IRelationService _relationService;
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public CreateRelationTypeController(IRelationService relationService,
IRelationTypePresentationFactory relationTypePresentationFactory,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_relationService = relationService;
_relationTypePresentationFactory = relationTypePresentationFactory;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[HttpPost]
[MapToApiVersion("1.0")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create(CreateRelationTypeRequestModel createRelationTypeRequestModel)
{
IRelationType relationTypePersisted = _relationTypePresentationFactory.CreateRelationType(createRelationTypeRequestModel);
Attempt<IRelationType, RelationTypeOperationStatus> result = await _relationService.CreateAsync(relationTypePersisted, CurrentUserId(_backOfficeSecurityAccessor));
return result.Success
? await Task.FromResult(CreatedAtAction<ByKeyRelationTypeController>(controller => nameof(controller.ByKey), relationTypePersisted.Key))
: RelationTypeOperationStatusResult(result.Status);
}
}

View File

@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Api.Management.Controllers.RelationType.Query;
public class DeleteRelationTypeController : RelationTypeControllerBase
{
private readonly IRelationService _relationService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public DeleteRelationTypeController(IRelationService relationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_relationService = relationService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[HttpDelete("{key:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Delete(Guid key)
{
Attempt<IRelationType?, RelationTypeOperationStatus> result = await _relationService.DeleteAsync(key, CurrentUserId(_backOfficeSecurityAccessor));
return result.Success ? await Task.FromResult(Ok()) : RelationTypeOperationStatusResult(result.Status);
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.Builders;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Api.Management.Controllers.RelationType.Query;
[ApiController]
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}")]
[ApiExplorerSettings(GroupName = "Relation Type")]
[ApiVersion("1.0")]
public class RelationTypeControllerBase : ManagementApiControllerBase
{
protected IActionResult RelationTypeOperationStatusResult(RelationTypeOperationStatus status) =>
status switch
{
RelationTypeOperationStatus.InvalidId => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Invalid id")
.WithDetail("Can not assign an Id when creating a relation type")
.Build()),
RelationTypeOperationStatus.CancelledByNotification => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Cancelled by notification")
.WithDetail("A notification handler prevented the relation type operation.")
.Build()),
RelationTypeOperationStatus.KeyAlreadyExists => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Key already exists")
.WithDetail("An entity with the given key already exists")
.Build()),
RelationTypeOperationStatus.NotFound => NotFound(new ProblemDetailsBuilder()
.WithTitle("Relation type not found")
.WithDetail("A relation type with the given key does not exist")
.Build()),
RelationTypeOperationStatus.InvalidChildObjectType => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Invalid child object type")
.WithDetail("The child object type is not allowed")
.Build()),
RelationTypeOperationStatus.InvalidParentObjectType => BadRequest(new ProblemDetailsBuilder()
.WithTitle("Invalid parent object type")
.WithDetail("The parent object type is not allowed")
.Build()),
};
}

View File

@@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.Builders;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
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.RelationType.Query;
public class UpdateRelationTypeController : RelationTypeControllerBase
{
private readonly IRelationService _relationService;
private readonly IRelationTypePresentationFactory _relationTypePresentationFactory;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public UpdateRelationTypeController(
IRelationService relationService,
IRelationTypePresentationFactory relationTypePresentationFactory,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_relationService = relationService;
_relationTypePresentationFactory = relationTypePresentationFactory;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[HttpPut("{key:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(RelationTypeResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Update(Guid key, UpdateRelationTypeRequestModel updateRelationTypeSavingViewModel)
{
IRelationType? persistedRelationType = _relationService.GetRelationTypeById(key);
if (persistedRelationType is null)
{
ProblemDetails problemDetails = new ProblemDetailsBuilder()
.WithTitle("Could not find relation type")
.WithDetail($"Relation type with key {key} could not be found")
.Build();
return NotFound(problemDetails);
}
_relationTypePresentationFactory.MapUpdateModelToRelationType(updateRelationTypeSavingViewModel, persistedRelationType);
Attempt<IRelationType, RelationTypeOperationStatus> result = await _relationService.UpdateAsync(persistedRelationType, CurrentUserId(_backOfficeSecurityAccessor));
return result.Success ? await Task.FromResult(Ok()) : RelationTypeOperationStatusResult(result.Status);
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Mapping.RelationType;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
namespace Umbraco.Cms.Api.Management.DependencyInjection;
internal static class RelationTypesBuilderExtensions
{
internal static IUmbracoBuilder AddRelationTypes(this IUmbracoBuilder builder)
{
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<RelationTypeViewModelsMapDefinition>();
builder.Services.AddTransient<IObjectTypePresentationFactory, ObjectTypePresentationFactory>();
builder.Services.AddTransient<IRelationTypePresentationFactory, RelationTypePresentationFactory>();
return builder;
}
}

View File

@@ -12,7 +12,7 @@ internal static class TrackedReferencesBuilderExtensions
{
internal static IUmbracoBuilder AddTrackedReferences(this IUmbracoBuilder builder)
{
builder.Services.AddTransient<IRelationViewModelFactory, RelationViewModelFactory>();
builder.Services.AddTransient<IRelationPresentationFactory, RelationPresentationFactory>();
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<TrackedReferenceViewModelsMapDefinition>()

View File

@@ -0,0 +1,8 @@
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
namespace Umbraco.Cms.Api.Management.Factories;
public interface IObjectTypePresentationFactory
{
IEnumerable<ObjectTypeResponseModel> Create();
}

View File

@@ -3,7 +3,7 @@ using Umbraco.Cms.Api.Management.ViewModels.Relation;
namespace Umbraco.Cms.Api.Management.Factories;
public interface IRelationViewModelFactory
public interface IRelationPresentationFactory
{
RelationViewModel Create(IRelation relation);

View File

@@ -0,0 +1,11 @@
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Api.Management.Factories;
public interface IRelationTypePresentationFactory
{
IRelationType CreateRelationType(CreateRelationTypeRequestModel createRelationTypeRequestModel);
void MapUpdateModelToRelationType(UpdateRelationTypeRequestModel updateRelationTypeRequestModel, IRelationType target);
}

View File

@@ -0,0 +1,24 @@
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Factories;
public class ObjectTypePresentationFactory : IObjectTypePresentationFactory
{
private readonly IRelationService _relationService;
public ObjectTypePresentationFactory(IRelationService relationService)
{
_relationService = relationService;
}
public IEnumerable<ObjectTypeResponseModel> Create()
=> _relationService
.GetAllowedObjectTypes()
.Select(umbracoObjectType => new ObjectTypeResponseModel
{
Id = umbracoObjectType.GetGuid(),
Name = umbracoObjectType.GetFriendlyName(),
});
}

View File

@@ -6,12 +6,12 @@ using Umbraco.Cms.Api.Management.ViewModels.Relation;
namespace Umbraco.Cms.Api.Management.Factories;
public class RelationViewModelFactory : IRelationViewModelFactory
public class RelationPresentationFactory : IRelationPresentationFactory
{
private readonly IRelationService _relationService;
private readonly IUmbracoMapper _umbracoMapper;
public RelationViewModelFactory(IRelationService relationService, IUmbracoMapper umbracoMapper)
public RelationPresentationFactory(IRelationService relationService, IUmbracoMapper umbracoMapper)
{
_relationService = relationService;
_umbracoMapper = umbracoMapper;

View File

@@ -0,0 +1,37 @@
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Factories;
public class RelationTypePresentationFactory : IRelationTypePresentationFactory
{
private readonly IShortStringHelper _shortStringHelper;
public RelationTypePresentationFactory(IShortStringHelper shortStringHelper) => _shortStringHelper = shortStringHelper;
public IRelationType CreateRelationType(CreateRelationTypeRequestModel createRelationTypeRequestModel) =>
new RelationType(
createRelationTypeRequestModel.Name,
createRelationTypeRequestModel.Name.ToSafeAlias(_shortStringHelper, true),
createRelationTypeRequestModel.IsBidirectional,
createRelationTypeRequestModel.ParentObjectType,
createRelationTypeRequestModel.ChildObjectType,
createRelationTypeRequestModel.IsDependency,
createRelationTypeRequestModel.Key);
public void MapUpdateModelToRelationType(UpdateRelationTypeRequestModel updateRelationTypeRequestModel, IRelationType target)
{
target.Name = updateRelationTypeRequestModel.Name;
target.Alias = updateRelationTypeRequestModel.Name.ToSafeAlias(_shortStringHelper, true);
target.ChildObjectType = updateRelationTypeRequestModel.ChildObjectType;
target.IsBidirectional = updateRelationTypeRequestModel.IsBidirectional;
if (target is IRelationTypeWithIsDependency targetWithIsDependency)
{
targetWithIsDependency.IsDependency = updateRelationTypeRequestModel.IsDependency;
}
target.ParentObjectType = updateRelationTypeRequestModel.ParentObjectType;
}
}

View File

@@ -37,6 +37,7 @@ public class ManagementApiComposer : IComposer
.AddTrackedReferences()
.AddDataTypes()
.AddTemplates()
.AddRelationTypes()
.AddLogViewer()
.AddUserGroups()
.AddPackages()

View File

@@ -0,0 +1,47 @@
using Umbraco.Cms.Api.Management.ViewModels.RelationType;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Mapping.RelationType;
public class RelationTypeViewModelsMapDefinition : IMapDefinition
{
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<IRelationType, RelationTypeResponseModel>((_, _) => new RelationTypeResponseModel(), Map);
}
// Umbraco.Code.MapAll
private void Map(IRelationType source, RelationTypeResponseModel target, MapperContext context)
{
target.ChildObjectType = source.ChildObjectType;
target.IsBidirectional = source.IsBidirectional;
if (source is IRelationTypeWithIsDependency sourceWithIsDependency)
{
target.IsDependency = sourceWithIsDependency.IsDependency;
}
target.Key = source.Key;
target.Name = source.Name ?? string.Empty;
target.Alias = source.Alias;
target.ParentObjectType = source.ParentObjectType;
target.Path = "-1," + source.Id;
target.IsSystemRelationType = source.IsSystemRelationType();
// Set the "friendly" and entity names for the parent and child object types
if (source.ParentObjectType.HasValue)
{
UmbracoObjectTypes objType = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType.Value);
target.ParentObjectTypeName = objType.GetFriendlyName();
}
if (source.ChildObjectType.HasValue)
{
UmbracoObjectTypes objType = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType.Value);
target.ChildObjectTypeName = objType.GetFriendlyName();
}
}
}

View File

@@ -3784,6 +3784,46 @@
}
}
},
"/umbraco/management/api/v1/object-types": {
"get": {
"tags": [
"Object Types"
],
"operationId": "GetObjectTypes",
"parameters": [
{
"name": "skip",
"in": "query",
"schema": {
"type": "integer",
"format": "int32",
"default": 0
}
},
{
"name": "take",
"in": "query",
"schema": {
"type": "integer",
"format": "int32",
"default": 100
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PagedObjectTypeResponseModel"
}
}
}
}
}
}
},
"/umbraco/management/api/v1/package/{name}/run-migration": {
"post": {
"tags": [
@@ -4511,6 +4551,188 @@
}
}
},
"/umbraco/management/api/v1/relation-type": {
"post": {
"tags": [
"Relation Type"
],
"operationId": "PostRelationType",
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/CreateRelationTypeRequestModel"
}
]
}
}
}
},
"responses": {
"201": {
"description": "Created",
"headers": {
"Location": {
"description": "Location of the newly created resource",
"schema": {
"type": "string",
"description": "Location of the newly created resource",
"format": "uri"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetailsModel"
}
}
}
}
}
}
},
"/umbraco/management/api/v1/relation-type/{key}": {
"get": {
"tags": [
"Relation Type"
],
"operationId": "GetRelationTypeByKey",
"parameters": [
{
"name": "key",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/RelationTypeResponseModel"
}
]
}
}
}
},
"404": {
"description": "Not Found"
}
}
},
"delete": {
"tags": [
"Relation Type"
],
"operationId": "DeleteRelationTypeByKey",
"parameters": [
{
"name": "key",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"responses": {
"200": {
"description": "Success"
},
"404": {
"description": "Not Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetailsModel"
}
}
}
}
}
},
"put": {
"tags": [
"Relation Type"
],
"operationId": "PutRelationTypeByKey",
"parameters": [
{
"name": "key",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/UpdateRelationTypeRequestModel"
}
]
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/RelationTypeResponseModel"
}
]
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetailsModel"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetailsModel"
}
}
}
}
}
}
},
"/umbraco/management/api/v1/tree/relation-type/item": {
"get": {
"tags": [
@@ -4687,6 +4909,55 @@
}
}
},
"/umbraco/management/api/v1/relation/type/{key}": {
"get": {
"tags": [
"Relation"
],
"operationId": "GetRelationTypeByKey",
"parameters": [
{
"name": "key",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
},
{
"name": "skip",
"in": "query",
"schema": {
"type": "integer",
"format": "int32",
"default": 0
}
},
{
"name": "take",
"in": "query",
"schema": {
"type": "integer",
"format": "int32",
"default": 100
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PagedRelationModel"
}
}
}
}
}
}
},
"/umbraco/management/api/v1/tree/script/children": {
"get": {
"tags": [
@@ -6569,6 +6840,22 @@
},
"additionalProperties": false
},
"CreateRelationTypeRequestModel": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/RelationTypeBaseModel"
}
],
"properties": {
"key": {
"type": "string",
"format": "uuid",
"nullable": true
}
},
"additionalProperties": false
},
"CultureModel": {
"type": "object",
"properties": {
@@ -7919,6 +8206,20 @@
"type": "integer",
"format": "int32"
},
"ObjectTypeResponseModel": {
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true
},
"id": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
"OkResultModel": {
"type": "object",
"properties": {
@@ -8567,6 +8868,30 @@
},
"additionalProperties": false
},
"PagedObjectTypeResponseModel": {
"required": [
"items",
"total"
],
"type": "object",
"properties": {
"total": {
"type": "integer",
"format": "int64"
},
"items": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/components/schemas/ObjectTypeResponseModel"
}
]
}
}
},
"additionalProperties": false
},
"PagedPackageDefinitionModel": {
"required": [
"items",
@@ -9136,6 +9461,64 @@
},
"additionalProperties": false
},
"RelationTypeBaseModel": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"isBidirectional": {
"type": "boolean"
},
"parentObjectType": {
"type": "string",
"format": "uuid",
"nullable": true
},
"childObjectType": {
"type": "string",
"format": "uuid",
"nullable": true
},
"isDependency": {
"type": "boolean"
}
},
"additionalProperties": false
},
"RelationTypeResponseModel": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/RelationTypeBaseModel"
}
],
"properties": {
"key": {
"type": "string",
"format": "uuid"
},
"alias": {
"type": "string",
"nullable": true
},
"path": {
"type": "string"
},
"isSystemRelationType": {
"type": "boolean"
},
"parentObjectTypeName": {
"type": "string",
"nullable": true
},
"childObjectTypeName": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"RuntimeLevelModel": {
"enum": [
"Unknown",
@@ -9495,6 +9878,15 @@
},
"additionalProperties": false
},
"UpdateRelationTypeRequestModel": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/RelationTypeBaseModel"
}
],
"additionalProperties": false
},
"UpgradeSettingsModel": {
"type": "object",
"properties": {

View File

@@ -0,0 +1,6 @@
namespace Umbraco.Cms.Api.Management.ViewModels.RelationType;
public class CreateRelationTypeRequestModel : RelationTypeBaseModel
{
public Guid? Key { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Cms.Api.Management.ViewModels.RelationType;
public class ObjectTypeResponseModel
{
public string? Name { get; set; }
public Guid Id { get; set; }
}

View File

@@ -0,0 +1,29 @@
namespace Umbraco.Cms.Api.Management.ViewModels.RelationType;
public class RelationTypeBaseModel
{
/// <summary>
/// Gets or sets the name of the model.
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false)
/// </summary>
public bool IsBidirectional { get; set; }
/// <summary>
/// Gets or sets the parent object type ID.
/// </summary>
public Guid? ParentObjectType { get; set; }
/// <summary>
/// Gets or sets the child object type ID.
/// </summary>
public Guid? ChildObjectType { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
public bool IsDependency { get; set; }
}

View File

@@ -0,0 +1,22 @@
namespace Umbraco.Cms.Api.Management.ViewModels.RelationType;
public class RelationTypeResponseModel : RelationTypeBaseModel
{
public Guid Key { get; set; }
public string? Alias { get; set; }
public string Path { get; set; } = string.Empty;
public bool IsSystemRelationType { get; set; }
/// <summary>
/// Gets or sets the Parent's object type name.
/// </summary>
public string? ParentObjectTypeName { get; set; }
/// <summary>
/// Gets or sets the Child's object type name.
/// </summary>
public string? ChildObjectTypeName { get; set; }
}

View File

@@ -0,0 +1,5 @@
namespace Umbraco.Cms.Api.Management.ViewModels.RelationType;
public class UpdateRelationTypeRequestModel : RelationTypeBaseModel
{
}

View File

@@ -22,7 +22,8 @@ public class RelationType : EntityBase, IRelationTypeWithIsDependency
{
}
public RelationType(string? name, string? alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency){
public RelationType(string? name, string? alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency, Guid? key = null)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
@@ -47,6 +48,11 @@ public class RelationType : EntityBase, IRelationTypeWithIsDependency
nameof(alias));
}
if (key.HasValue)
{
Key = key.Value;
}
_name = name;
_alias = alias;
_isBidirectional = isBidrectional;

View File

@@ -1,5 +1,7 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.New.Cms.Core.Models;
namespace Umbraco.Cms.Core.Services;
@@ -169,7 +171,18 @@ public interface IRelationService : IService
/// <param name="pageSize"></param>
/// <param name="totalChildren"></param>
/// <returns></returns>
IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering? ordering = null);
IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering? ordering = null);/// <summary>
/// <summary>
/// Gets a paged result of <see cref="IRelation" />
/// </summary>
/// <param name="key"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="ordering"></param>
/// <returns></returns>
Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKey(Guid key, int skip, int take, Ordering? ordering = null);
/// <summary>
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity" />
@@ -333,6 +346,22 @@ public interface IRelationService : IService
/// <param name="relationType">RelationType to Save</param>
void Save(IRelationType relationType);
/// <summary>
/// Saves a <see cref="IRelationType" />
/// </summary>
/// <param name="relationType">RelationType to Save</param>
/// <param name="userId">Id of the user thats saving the relation type</param>
/// <returns>A <see cref="Attempt"/> with a status of whether the operations was a success or failure</returns>
Task<Attempt<IRelationType, RelationTypeOperationStatus>> CreateAsync(IRelationType relationType, int userId) => throw new NotImplementedException();
/// <summary>
/// Saves a <see cref="IRelationType" />
/// </summary>
/// <param name="relationType">RelationType to Save</param>
/// <param name="userId">Id of the user thats saving the relation type</param>
/// <returns>A <see cref="Attempt"/> with a status of whether the operations was a success or failure</returns>
Task<Attempt<IRelationType, RelationTypeOperationStatus>> UpdateAsync(IRelationType relationType, int userId) => throw new NotImplementedException();
/// <summary>
/// Deletes a <see cref="IRelation" />
/// </summary>
@@ -345,9 +374,23 @@ public interface IRelationService : IService
/// <param name="relationType">RelationType to Delete</param>
void Delete(IRelationType relationType);
/// <summary>
/// Deletes a <see cref="IRelationType" />
/// </summary>
/// <param name="key">Key of the relation type to delete</param>
/// <param name="userId">Id of the user that is deleting the relation type</param>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
Task<Attempt<IRelationType?, RelationTypeOperationStatus>> DeleteAsync(Guid key, int userId) => throw new NotImplementedException();
/// <summary>
/// Deletes all <see cref="IRelation" /> objects based on the passed in <see cref="IRelationType" />
/// </summary>
/// <param name="relationType"><see cref="IRelationType" /> to Delete Relations for</param>
void DeleteRelationsOfType(IRelationType relationType);
/// <summary>
/// Gets all allowed parent/child object types for a given <see cref="IRelationType" /> />
/// </summary>
/// <returns>All of the allowed <see cref="UmbracoObjectTypes"/>.</returns>
IEnumerable<UmbracoObjectTypes> GetAllowedObjectTypes();
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Cms.Core.Services.OperationStatus;
public enum RelationOperationStatus
{
Success,
RelationTypeNotFound,
}

View File

@@ -0,0 +1,12 @@
namespace Umbraco.Cms.Core.Services.OperationStatus;
public enum RelationTypeOperationStatus
{
Success,
NotFound,
KeyAlreadyExists,
CancelledByNotification,
InvalidId,
InvalidChildObjectType,
InvalidParentObjectType,
}

View File

@@ -6,7 +6,9 @@ using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Extensions;
using Umbraco.New.Cms.Core.Models;
namespace Umbraco.Cms.Core.Services;
@@ -17,7 +19,14 @@ public class RelationService : RepositoryService, IRelationService
private readonly IRelationRepository _relationRepository;
private readonly IRelationTypeRepository _relationTypeRepository;
public RelationService(ICoreScopeProvider uowProvider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IEntityService entityService, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, IAuditRepository auditRepository)
public RelationService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IEntityService entityService,
IRelationRepository relationRepository,
IRelationTypeRepository relationTypeRepository,
IAuditRepository auditRepository)
: base(uowProvider, loggerFactory, eventMessagesFactory)
{
_relationRepository = relationRepository;
@@ -239,6 +248,24 @@ public class RelationService : RepositoryService, IRelationService
}
}
public async Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKey(Guid key, int skip, int take, Ordering? ordering = null)
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
{
IRelationType? relationType = _relationTypeRepository.Get(key);
if (relationType is null)
{
return await Task.FromResult(Attempt.FailWithStatus<PagedModel<IRelation>, RelationOperationStatus>(RelationOperationStatus.RelationTypeNotFound, null!));
}
PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize);
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
IEnumerable<IRelation> relations = _relationRepository.GetPagedRelationsByQuery(query, pageNumber, pageSize, out var totalRecords, ordering);
return await Task.FromResult(Attempt.SucceedWithStatus(RelationOperationStatus.Success, new PagedModel<IRelation>(totalRecords, relations)));
}
}
/// <inheritdoc />
public IUmbracoEntity? GetChildEntityFromRelation(IRelation relation)
{
@@ -512,6 +539,72 @@ public class RelationService : RepositoryService, IRelationService
}
}
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> CreateAsync(IRelationType relationType, int userId)
{
if (relationType.Id != 0)
{
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidId, relationType);
}
return await SaveAsync(
relationType,
() => _relationTypeRepository.Get(relationType.Key) is not null ? RelationTypeOperationStatus.KeyAlreadyExists : RelationTypeOperationStatus.Success,
AuditType.New,
$"Created relation type: {relationType.Name}",
userId);
}
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> UpdateAsync(IRelationType relationType, int userId) =>
await SaveAsync(
relationType,
() => _relationTypeRepository.Get(relationType.Key) is null ? RelationTypeOperationStatus.NotFound : RelationTypeOperationStatus.Success,
AuditType.Save,
$"Created relation type: {relationType.Name}",
userId);
private async Task<Attempt<IRelationType, RelationTypeOperationStatus>> SaveAsync(IRelationType relationType, Func<RelationTypeOperationStatus> operationValidation, AuditType auditType, string auditMessage, int userId)
{
// Validate that parent & child object types are allowed
UmbracoObjectTypes[] allowedObjectTypes = GetAllowedObjectTypes().ToArray();
var childObjectTypeAllowed = allowedObjectTypes.Any(x => x.GetGuid() == relationType.ChildObjectType);
if (childObjectTypeAllowed is false)
{
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidChildObjectType, relationType);
}
var parentObjectTypeAllowed = allowedObjectTypes.Any(x => x.GetGuid() == relationType.ParentObjectType);
if (parentObjectTypeAllowed is false)
{
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidParentObjectType, relationType);
}
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
RelationTypeOperationStatus status = operationValidation();
if (status != RelationTypeOperationStatus.Success)
{
return Attempt.FailWithStatus(status, relationType);
}
EventMessages eventMessages = EventMessagesFactory.Get();
var savingNotification = new RelationTypeSavingNotification(relationType, eventMessages);
if (scope.Notifications.PublishCancelable(savingNotification))
{
scope.Complete();
return Attempt.FailWithStatus(RelationTypeOperationStatus.CancelledByNotification, relationType);
}
_relationTypeRepository.Save(relationType);
Audit(auditType, userId, relationType.Id, auditMessage);
scope.Complete();
scope.Notifications.Publish(
new RelationTypeSavedNotification(relationType, eventMessages).WithStateFrom(savingNotification));
}
return await Task.FromResult(Attempt.SucceedWithStatus(RelationTypeOperationStatus.Success, relationType));
}
/// <inheritdoc />
public void Delete(IRelation relation)
{
@@ -552,6 +645,32 @@ public class RelationService : RepositoryService, IRelationService
}
}
public async Task<Attempt<IRelationType?, RelationTypeOperationStatus>> DeleteAsync(Guid key, int userId)
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
IRelationType? relationType = _relationTypeRepository.Get(key);
if (relationType is null)
{
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.NotFound, null);
}
EventMessages eventMessages = EventMessagesFactory.Get();
var deletingNotification = new RelationTypeDeletingNotification(relationType, eventMessages);
if (scope.Notifications.PublishCancelable(deletingNotification))
{
scope.Complete();
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.CancelledByNotification, null);
}
_relationTypeRepository.Delete(relationType);
Audit(AuditType.Delete, userId, relationType.Id, "Deleted relation type");
scope.Notifications.Publish(new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
scope.Complete();
return await Task.FromResult(Attempt.SucceedWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.Success, relationType));
}
}
/// <inheritdoc />
public void DeleteRelationsOfType(IRelationType relationType)
{
@@ -584,6 +703,21 @@ public class RelationService : RepositoryService, IRelationService
}
}
public IEnumerable<UmbracoObjectTypes> GetAllowedObjectTypes() =>
new[]
{
UmbracoObjectTypes.Document,
UmbracoObjectTypes.Media,
UmbracoObjectTypes.Member,
UmbracoObjectTypes.DocumentType,
UmbracoObjectTypes.MediaType,
UmbracoObjectTypes.MemberType,
UmbracoObjectTypes.DataType,
UmbracoObjectTypes.MemberGroup,
UmbracoObjectTypes.ROOT,
UmbracoObjectTypes.RecycleBin,
};
#region Private Methods
private IRelationType? GetRelationType(string relationTypeAlias)

View File

@@ -0,0 +1,150 @@
using Castle.Components.DictionaryAdapter.Xml;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Builders.Extensions;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class RelationServiceTests : UmbracoIntegrationTest
{
private IRelationService RelationService => GetRequiredService<IRelationService>();
[Test]
[TestCase(true, true)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public async Task Can_Create_Relation_Types_With_Bi_Directional_And_Is_Dependency(bool isBiDirectional, bool isDependency)
{
IRelationTypeWithIsDependency relationType = new RelationTypeBuilder()
.WithChildObjectType(Constants.ObjectTypes.DocumentType)
.WithParentObjectType(Constants.ObjectTypes.MediaType)
.WithIsBidirectional(isBiDirectional)
.WithIsDependency(isDependency)
.Build();
Attempt<IRelationType, RelationTypeOperationStatus> result = await RelationService.CreateAsync(relationType, Constants.Security.SuperUserId);
Assert.Multiple(() =>
{
Assert.IsTrue(result.Success);
Assert.AreEqual(RelationTypeOperationStatus.Success, result.Status);
});
AssertRelationTypesAreSame(relationType, result.Result);
}
[Test]
[TestCase(Constants.ObjectTypes.Strings.Document, Constants.ObjectTypes.Strings.Media)]
[TestCase(Constants.ObjectTypes.Strings.Member, Constants.ObjectTypes.Strings.DocumentType)]
[TestCase(Constants.ObjectTypes.Strings.MediaType, Constants.ObjectTypes.Strings.MemberType)]
[TestCase(Constants.ObjectTypes.Strings.DataType, Constants.ObjectTypes.Strings.MemberGroup)]
[TestCase(Constants.ObjectTypes.Strings.Document, Constants.ObjectTypes.Strings.ContentRecycleBin)]
[TestCase(Constants.ObjectTypes.Strings.Document, Constants.ObjectTypes.Strings.SystemRoot)]
public async Task Can_Create_Relation_Types_With_Allowed_Object_Types(string childObjectTypeGuid, string parentObjectTypeGuid)
{
IRelationTypeWithIsDependency relationType = new RelationTypeBuilder()
.WithChildObjectType(new Guid(childObjectTypeGuid))
.WithParentObjectType(new Guid(parentObjectTypeGuid))
.Build();
Attempt<IRelationType, RelationTypeOperationStatus> result = await RelationService.CreateAsync(relationType, Constants.Security.SuperUserId);
Assert.Multiple(() =>
{
Assert.IsTrue(result.Success);
Assert.AreEqual(RelationTypeOperationStatus.Success, result.Status);
});
AssertRelationTypesAreSame(relationType, result.Result);
}
[Test]
[TestCase(Constants.ObjectTypes.Strings.Document, "53E492BD-F242-40A7-8F21-7D649463DD23", RelationTypeOperationStatus.InvalidChildObjectType)]
[TestCase("E7524E34-F84F-43DE-92E2-25999785B7EA", Constants.ObjectTypes.Strings.DataType, RelationTypeOperationStatus.InvalidParentObjectType)]
[TestCase("00000000-0000-0000-0000-000000000000", Constants.ObjectTypes.Strings.Document, RelationTypeOperationStatus.InvalidParentObjectType)]
[TestCase(Constants.ObjectTypes.Strings.IdReservation, Constants.ObjectTypes.Strings.Document, RelationTypeOperationStatus.InvalidParentObjectType)]
public async Task Cannot_Create_Relation_Types_With_Disallowed_Object_Types(string parentObjectTypeGuid, string childObjectTypeGuid, RelationTypeOperationStatus relationTypeOperationStatus)
{
IRelationTypeWithIsDependency relationType = new RelationTypeBuilder()
.WithChildObjectType(new Guid(childObjectTypeGuid))
.WithParentObjectType(new Guid(parentObjectTypeGuid))
.Build();
Attempt<IRelationType, RelationTypeOperationStatus> result = await RelationService.CreateAsync(relationType, Constants.Security.SuperUserId);
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.AreEqual(relationTypeOperationStatus, result.Status);
});
}
[Test]
public async Task Can_Create_Relation_Type_With_Key()
{
const string key = "82E7631C-0417-460C-91C1-F65784627143";
IRelationTypeWithIsDependency relationType = new RelationTypeBuilder()
.WithChildObjectType(Constants.ObjectTypes.DocumentType)
.WithParentObjectType(Constants.ObjectTypes.DocumentType)
.WithKey(new Guid(key))
.Build();
Attempt<IRelationType, RelationTypeOperationStatus> result = await RelationService.CreateAsync(relationType, Constants.Security.SuperUserId);
Assert.Multiple(() =>
{
Assert.IsTrue(result.Success);
Assert.AreEqual(RelationTypeOperationStatus.Success, result.Status);
Assert.IsTrue(string.Equals(key, result.Result.Key.ToString(), StringComparison.OrdinalIgnoreCase));
});
AssertRelationTypesAreSame(relationType, result.Result);
}
[Test]
[TestCase(-1000000)]
[TestCase(-1)]
[TestCase(100)]
[TestCase(10000000)]
public async Task Cannot_Create_Relation_Types_With_Id(int id)
{
IRelationTypeWithIsDependency relationType = new RelationTypeBuilder()
.WithChildObjectType(Constants.ObjectTypes.DocumentType)
.WithParentObjectType(Constants.ObjectTypes.DocumentType)
.WithId(id)
.Build();
Attempt<IRelationType, RelationTypeOperationStatus> result = await RelationService.CreateAsync(relationType, Constants.Security.SuperUserId);
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.AreEqual(RelationTypeOperationStatus.InvalidId, result.Status);
});
}
private void AssertRelationTypesAreSame(IRelationTypeWithIsDependency relationType, IRelationType result)
{
Assert.Multiple(() =>
{
Assert.AreEqual(relationType.Name, result.Name);
Assert.AreEqual(relationType.ParentObjectType, result.ParentObjectType);
Assert.AreEqual(relationType.ChildObjectType, result.ChildObjectType);
Assert.AreEqual(relationType.IsBidirectional, result.IsBidirectional);
var asWithDependency = (IRelationTypeWithIsDependency)result;
Assert.AreEqual(relationType.IsDependency, asWithDependency.IsDependency);
});
var persistedRelationType = RelationService.GetRelationTypeById(result.Key);
Assert.AreEqual(result, persistedRelationType);
}
}