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
This commit is contained in:
Bjarke Berg
2023-08-28 11:16:56 +02:00
committed by GitHub
parent f2d11e3095
commit b17a7689ab
12 changed files with 81 additions and 175 deletions

View File

@@ -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}")]
/// <summary>
/// Gets a paged list of relations by the unique relation child keys.
/// </summary>
/// <remarks>
/// Use case: When you wanna restore a deleted item, this is used to find the old location
/// </remarks>
[HttpGet("child-relation/{childId:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<RelationResponseModel>), StatusCodes.Status200OK)]
public async Task<PagedViewModel<RelationResponseModel>> ByChild(int childId, int skip = 0, int take = 100, string? relationTypeAlias = "")
public async Task<IActionResult> ByChild(Guid childId, int skip = 0, int take = 100, string? relationTypeAlias = "")
{
IRelation[] relations = _relationService.GetByChildId(childId).ToArray();
RelationResponseModel[] result = Array.Empty<RelationResponseModel>();
PagedModel<IRelation> 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<RelationResponseModel> mappedRelations = relationsAttempt.Items.Select(_relationPresentationFactory.Create);
return await Task.FromResult(new PagedViewModel<RelationResponseModel>
return await Task.FromResult(Ok(new PagedViewModel<RelationResponseModel>
{
Total = result.Length,
Items = result.Skip(skip).Take(take),
});
Total = relationsAttempt.Total,
Items = mappedRelations,
}));
}
}

View File

@@ -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<IActionResult> ById(int id)
{
IRelation? relation = _relationService.GetById(id);
if (relation is null)
{
return RelationNotFound();
}
return await Task.FromResult(Ok(_relationPresentationFactory.Create(relation)));
}
}

View File

@@ -23,13 +23,19 @@ public class ByRelationTypeKeyRelationController : RelationControllerBase
_relationPresentationFactory = relationPresentationFactory;
}
/// <summary>
/// Gets a paged list of relations by the unique relation key.
/// </summary>
/// <remarks>
/// Use case: On a relation type page you can see all created relations of this type.
/// </remarks>
[HttpGet("type/{id:guid}")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<RelationResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PagedViewModel<ProblemDetails>), StatusCodes.Status404NotFound)]
public async Task<IActionResult> ByRelationTypeKey(Guid id, int skip = 0, int take = 100)
{
Attempt<PagedModel<IRelation>, RelationOperationStatus> relationsAttempt = await _relationService.GetPagedByRelationTypeKey(id, skip, take);
Attempt<PagedModel<IRelation>, RelationOperationStatus> relationsAttempt = await _relationService.GetPagedByRelationTypeKeyAsync(id, skip, take);
if (relationsAttempt.Success is false)
{

View File

@@ -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<MapDefinitionCollectionBuilder>()
.Add<TrackedReferenceViewModelsMapDefinition>()
.Add<RelationModelMapDefinition>()
.Add<RelationViewModelsMapDefinition>();
.Add<RelationModelMapDefinition>();
return builder;
}

View File

@@ -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<RelationResponseModel>(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<IUmbracoEntity, IUmbracoEntity>? entities = _relationService.GetEntitiesFromRelation(relation);
if (entities is not null)

View File

@@ -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<IRelation, RelationResponseModel>((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;
}
}

View File

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

View File

@@ -9,7 +9,7 @@ public class RelationResponseModel
/// Gets or sets the Parent Id of the Relation (Source).
/// </summary>
[ReadOnly(true)]
public int ParentId { get; set; }
public Guid ParentId { get; set; }
/// <summary>
/// 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).
/// </summary>
[ReadOnly(true)]
public int ChildId { get; set; }
public Guid ChildId { get; set; }
/// <summary>
/// Gets or sets the Child Name of the relation (Destination).

View File

@@ -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<int, IRelation>
IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes);
IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes);
Task<PagedModel<IRelation>> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias);
}

View File

@@ -181,7 +181,7 @@ public interface IRelationService : IService
/// <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);
Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null);
/// <summary>
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity" />
@@ -392,4 +392,6 @@ public interface IRelationService : IService
/// </summary>
/// <returns>All of the allowed <see cref="UmbracoObjectTypes"/>.</returns>
IEnumerable<UmbracoObjectTypes> GetAllowedObjectTypes();
Task<PagedModel<IRelation>> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias);
}

View File

@@ -273,7 +273,15 @@ public class RelationService : RepositoryService, IRelationService
}
}
public async Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKey(Guid key, int skip, int take, Ordering? ordering = null)
public async Task<PagedModel<IRelation>> 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<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null)
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
{

View File

@@ -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<int, IRelation>, IRelat
public IEnumerable<IUmbracoEntity> 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<PagedModel<IRelation>> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias)
{
Sql<ISqlContext> sql = GetBaseQuery(false);
if (string.IsNullOrEmpty(relationTypeAlias) is false)
{
sql = sql.InnerJoin<RelationTypeDto>().On<RelationDto, RelationTypeDto>(umbracoRelation => umbracoRelation.RelationType, rt => rt.Id)
.Where<RelationTypeDto>(rt => rt.Alias == relationTypeAlias);
}
sql = sql.Where<NodeDto>(n => n.UniqueId == childKey, "uchild"); // "uchild" comes from the base query
RelationDto[] pagedResult =
Database.SkipTake<RelationDto>(skip, take, sql).ToArray();
var totalRecords = Database.Count(sql);
return Task.FromResult(new PagedModel<IRelation>(totalRecords, DtosToEntities(pagedResult)));
}
public void Save(IEnumerable<IRelation> relations)
{
foreach (IGrouping<bool, IRelation> hasIdentityGroup in relations.GroupBy(r => r.HasIdentity))