From b17a7689ab559e4a55148ac9c8f51b2d7d068b65 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 28 Aug 2023 11:16:56 +0200 Subject: [PATCH] Fixed up the relation management api endpoints to use guids and paging (#14706) * Fixed up the relation management api endpoints to use guids and paging * Cleanup * Updated OpenApi.json --- .../Relation/ByChildRelationController.cs | 36 ++++---- .../Relation/ByIdRelationController.cs | 37 -------- .../ByRelationTypeKeyRelationController.cs | 8 +- .../TrackedReferencesBuilderExtensions.cs | 4 +- .../Factories/RelationPresentationFactory.cs | 17 +++- .../RelationViewModelsMapDefinition.cs | 22 ----- src/Umbraco.Cms.Api.Management/OpenApi.json | 90 ++----------------- .../Relation/RelationResponseModel.cs | 4 +- .../Repositories/IRelationRepository.cs | 2 + src/Umbraco.Core/Services/IRelationService.cs | 4 +- src/Umbraco.Core/Services/RelationService.cs | 10 ++- .../Implement/RelationRepository.cs | 22 +++++ 12 files changed, 81 insertions(+), 175 deletions(-) delete mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Relation/ByIdRelationController.cs delete mode 100644 src/Umbraco.Cms.Api.Management/Mapping/Relation/RelationViewModelsMapDefinition.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByChildRelationController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByChildRelationController.cs index 8a71ea700c..b93fe47ec6 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByChildRelationController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByChildRelationController.cs @@ -6,6 +6,8 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels.Relation; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Controllers.Relation; @@ -24,31 +26,25 @@ public class ByChildRelationController : RelationControllerBase _relationPresentationFactory = relationPresentationFactory; } - [HttpGet("child-relation/{childId:int}")] + /// + /// Gets a paged list of relations by the unique relation child keys. + /// + /// + /// Use case: When you wanna restore a deleted item, this is used to find the old location + /// + [HttpGet("child-relation/{childId:guid}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> ByChild(int childId, int skip = 0, int take = 100, string? relationTypeAlias = "") + public async Task ByChild(Guid childId, int skip = 0, int take = 100, string? relationTypeAlias = "") { - IRelation[] relations = _relationService.GetByChildId(childId).ToArray(); - RelationResponseModel[] result = Array.Empty(); + PagedModel relationsAttempt = await _relationService.GetPagedByChildKeyAsync(childId, skip, take, relationTypeAlias); - if (relations.Any()) - { - if (string.IsNullOrWhiteSpace(relationTypeAlias) == false) - { - result = _relationPresentationFactory.CreateMultiple(relations.Where(x => - x.RelationType.Alias.InvariantEquals(relationTypeAlias))).ToArray(); - } - else - { - result = _relationPresentationFactory.CreateMultiple(relations).ToArray(); - } - } + IEnumerable mappedRelations = relationsAttempt.Items.Select(_relationPresentationFactory.Create); - return await Task.FromResult(new PagedViewModel + return await Task.FromResult(Ok(new PagedViewModel { - Total = result.Length, - Items = result.Skip(skip).Take(take), - }); + Total = relationsAttempt.Total, + Items = mappedRelations, + })); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByIdRelationController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByIdRelationController.cs deleted file mode 100644 index 1ad45a5912..0000000000 --- a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByIdRelationController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Api.Management.Factories; -using Umbraco.Cms.Api.Management.ViewModels.Relation; - -namespace Umbraco.Cms.Api.Management.Controllers.Relation; - -[ApiVersion("1.0")] -public class ByIdRelationController : RelationControllerBase -{ - private readonly IRelationService _relationService; - private readonly IRelationPresentationFactory _relationPresentationFactory; - - public ByIdRelationController(IRelationService relationService, IRelationPresentationFactory relationPresentationFactory) - { - _relationService = relationService; - _relationPresentationFactory = relationPresentationFactory; - } - - [HttpGet("{id:int}")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(RelationResponseModel), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - public async Task ById(int id) - { - IRelation? relation = _relationService.GetById(id); - if (relation is null) - { - return RelationNotFound(); - } - - return await Task.FromResult(Ok(_relationPresentationFactory.Create(relation))); - } -} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs index aaca170877..f0d904ec20 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs @@ -23,13 +23,19 @@ public class ByRelationTypeKeyRelationController : RelationControllerBase _relationPresentationFactory = relationPresentationFactory; } + /// + /// Gets a paged list of relations by the unique relation key. + /// + /// + /// Use case: On a relation type page you can see all created relations of this type. + /// [HttpGet("type/{id:guid}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status404NotFound)] public async Task ByRelationTypeKey(Guid id, int skip = 0, int take = 100) { - Attempt, RelationOperationStatus> relationsAttempt = await _relationService.GetPagedByRelationTypeKey(id, skip, take); + Attempt, RelationOperationStatus> relationsAttempt = await _relationService.GetPagedByRelationTypeKeyAsync(id, skip, take); if (relationsAttempt.Success is false) { diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/TrackedReferencesBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/TrackedReferencesBuilderExtensions.cs index 9bb387d2f4..d94d0649f6 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/TrackedReferencesBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/TrackedReferencesBuilderExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.Factories; -using Umbraco.Cms.Api.Management.Mapping.Relation; using Umbraco.Cms.Api.Management.Mapping.TrackedReferences; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Mapping; @@ -16,8 +15,7 @@ internal static class TrackedReferencesBuilderExtensions builder.WithCollectionBuilder() .Add() - .Add() - .Add(); + .Add(); return builder; } diff --git a/src/Umbraco.Cms.Api.Management/Factories/RelationPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/RelationPresentationFactory.cs index 52d86d2102..4a8e1ad3ef 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/RelationPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/RelationPresentationFactory.cs @@ -9,17 +9,26 @@ namespace Umbraco.Cms.Api.Management.Factories; public class RelationPresentationFactory : IRelationPresentationFactory { private readonly IRelationService _relationService; - private readonly IUmbracoMapper _umbracoMapper; + private readonly IEntityService _entityService; - public RelationPresentationFactory(IRelationService relationService, IUmbracoMapper umbracoMapper) + public RelationPresentationFactory(IRelationService relationService, IEntityService entityService) { _relationService = relationService; - _umbracoMapper = umbracoMapper; + _entityService = entityService; } public RelationResponseModel Create(IRelation relation) { - RelationResponseModel relationResponseModel = _umbracoMapper.Map(relation)!; + var child = _entityService.Get(relation.ChildId)!; + var parent = _entityService.Get(relation.ParentId)!; + + RelationResponseModel relationResponseModel = new RelationResponseModel() + { + ChildId = child.Key, + Comment = relation.Comment, + CreateDate = relation.CreateDate, + ParentId = parent.Key, + }; Tuple? entities = _relationService.GetEntitiesFromRelation(relation); if (entities is not null) diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Relation/RelationViewModelsMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Relation/RelationViewModelsMapDefinition.cs deleted file mode 100644 index d3137bfcd6..0000000000 --- a/src/Umbraco.Cms.Api.Management/Mapping/Relation/RelationViewModelsMapDefinition.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Api.Management.ViewModels.Relation; - -namespace Umbraco.Cms.Api.Management.Mapping.Relation; - -public class RelationViewModelsMapDefinition : IMapDefinition -{ - public void DefineMaps(IUmbracoMapper mapper) - { - mapper.Define((source, context) => new RelationResponseModel(), Map); - } - - // Umbraco.Code.MapAll -ParentName -ChildName - private void Map(IRelation source, RelationResponseModel target, MapperContext context) - { - target.ChildId = source.ChildId; - target.Comment = source.Comment; - target.CreateDate = source.CreateDate; - target.ParentId = source.ParentId; - } -} diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 2f7186d500..06c7657cbd 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -11407,84 +11407,6 @@ ] } }, - "/umbraco/management/api/v1/relation/{id}": { - "get": { - "tags": [ - "Relation" - ], - "operationId": "GetRelationById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/RelationResponseModel" - } - ] - } - }, - "text/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/RelationResponseModel" - } - ] - } - }, - "text/plain": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/RelationResponseModel" - } - ] - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - }, - "security": [ - { - "Backoffice User": [ ] - } - ] - } - }, "/umbraco/management/api/v1/relation/child-relation/{childId}": { "get": { "tags": [ @@ -11497,8 +11419,8 @@ "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } }, { @@ -21091,16 +21013,16 @@ "type": "object", "properties": { "parentId": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" }, "parentName": { "type": "string", "nullable": true }, "childId": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" }, "childName": { "type": "string", diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Relation/RelationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Relation/RelationResponseModel.cs index 88291bfe02..4aefe4891d 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Relation/RelationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Relation/RelationResponseModel.cs @@ -9,7 +9,7 @@ public class RelationResponseModel /// Gets or sets the Parent Id of the Relation (Source). /// [ReadOnly(true)] - public int ParentId { get; set; } + public Guid ParentId { get; set; } /// /// Gets or sets the Parent Name of the relation (Source). @@ -21,7 +21,7 @@ public class RelationResponseModel /// Gets or sets the Child Id of the Relation (Destination). /// [ReadOnly(true)] - public int ChildId { get; set; } + public Guid ChildId { get; set; } /// /// Gets or sets the Child Name of the relation (Destination). diff --git a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs index 8077a80dc1..6a7202feaa 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Core.Persistence.Repositories; @@ -33,4 +34,5 @@ public interface IRelationRepository : IReadWriteQueryRepository IEnumerable GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes); IEnumerable GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes); + Task> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias); } diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs index 3cc9ca2638..bd823b08c5 100644 --- a/src/Umbraco.Core/Services/IRelationService.cs +++ b/src/Umbraco.Core/Services/IRelationService.cs @@ -181,7 +181,7 @@ public interface IRelationService : IService /// /// /// - Task, RelationOperationStatus>> GetPagedByRelationTypeKey(Guid key, int skip, int take, Ordering? ordering = null); + Task, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null); /// /// Gets the Child object from a Relation as an @@ -392,4 +392,6 @@ public interface IRelationService : IService /// /// All of the allowed . IEnumerable GetAllowedObjectTypes(); + + Task> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias); } diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 52dad36f35..9d5a392834 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -273,7 +273,15 @@ public class RelationService : RepositoryService, IRelationService } } - public async Task, RelationOperationStatus>> GetPagedByRelationTypeKey(Guid key, int skip, int take, Ordering? ordering = null) + public async Task> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias) + { + using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true)) + { + return await _relationRepository.GetPagedByChildKeyAsync(childKey, skip, take, relationTypeAlias); + } + } + + public async Task, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null) { using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true)) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs index 9431633993..a38bf4547f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs @@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -38,6 +39,27 @@ internal class RelationRepository : EntityRepositoryBase, IRelat public IEnumerable GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes) => GetPagedChildEntitiesByParentId(parentId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes); + public Task> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias) + { + Sql sql = GetBaseQuery(false); + + if (string.IsNullOrEmpty(relationTypeAlias) is false) + { + + sql = sql.InnerJoin().On(umbracoRelation => umbracoRelation.RelationType, rt => rt.Id) + .Where(rt => rt.Alias == relationTypeAlias); + } + sql = sql.Where(n => n.UniqueId == childKey, "uchild"); // "uchild" comes from the base query + + + RelationDto[] pagedResult = + Database.SkipTake(skip, take, sql).ToArray(); + var totalRecords = Database.Count(sql); + + return Task.FromResult(new PagedModel(totalRecords, DtosToEntities(pagedResult))); + + } + public void Save(IEnumerable relations) { foreach (IGrouping hasIdentityGroup in relations.GroupBy(r => r.HasIdentity))