diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Content/ContentControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Content/ContentControllerBase.cs index 24ba42b2d5..aeb99e2442 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Content/ContentControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Content/ContentControllerBase.cs @@ -1,7 +1,5 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Api.Management.ViewModels.Content; -using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.ContentEditing.Validation; using Umbraco.Cms.Core.PropertyEditors.Validation; @@ -12,7 +10,6 @@ namespace Umbraco.Cms.Api.Management.Controllers.Content; public abstract class ContentControllerBase : ManagementApiControllerBase { - protected IActionResult ContentEditingOperationStatusResult(ContentEditingOperationStatus status) => OperationStatusResult(status, problemDetailsBuilder => status switch { @@ -98,6 +95,17 @@ public abstract class ContentControllerBase : ManagementApiControllerBase .Build()), }); + protected IActionResult GetReferencesOperationStatusResult(GetReferencesOperationStatus status) + => OperationStatusResult(status, problemDetailsBuilder => status switch + { + GetReferencesOperationStatus.ContentNotFound => NotFound(problemDetailsBuilder + .WithTitle("The requested content could not be found") + .Build()), + _ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder + .WithTitle("Unknown get references operation status.") + .Build()), + }); + protected IActionResult ContentEditingOperationStatusResult( ContentEditingOperationStatus status, TContentModelBase requestModel, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs index 6a1d9b2824..a695895d1d 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedByDocumentController.cs @@ -4,8 +4,10 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +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.Document.References; @@ -15,22 +17,16 @@ public class ReferencedByDocumentController : DocumentControllerBase private readonly ITrackedReferencesService _trackedReferencesService; private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; - public ReferencedByDocumentController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory) + public ReferencedByDocumentController( + ITrackedReferencesService trackedReferencesService, + IRelationTypePresentationFactory relationTypePresentationFactory) { _trackedReferencesService = trackedReferencesService; _relationTypePresentationFactory = relationTypePresentationFactory; } - /// - /// Gets a paged list of tracked references for the current item, so you can see where an item is being used. - /// - /// - /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. - /// This is basically finding parents of relations. - /// - [HttpGet("{id:guid}/referenced-by")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")] + [NonAction] public async Task>> ReferencedBy( CancellationToken cancellationToken, Guid id, @@ -47,4 +43,37 @@ public class ReferencedByDocumentController : DocumentControllerBase return pagedViewModel; } + + /// + /// Gets a paged list of tracked references for the current item, so you can see where an item is being used. + /// + /// + /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. + /// This is basically finding parents of relations. + /// + [HttpGet("{id:guid}/referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedBy2( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Document, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + + var pagedViewModel = new PagedViewModel + { + Total = relationItemsAttempt.Result.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items), + }; + + return Ok(pagedViewModel); + } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs index 138b919628..3940153308 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/References/ReferencedDescendantsDocumentController.cs @@ -3,9 +3,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Api.Management.Controllers.Document.References; @@ -15,12 +17,32 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public ReferencedDescendantsDocumentController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ReferencedDescendantsDocumentController( + ITrackedReferencesService trackedReferencesSkipTakeService, + IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; } + [Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")] + [NonAction] + public async Task>> ReferencedDescendants( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + PagedModel relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true); + var pagedViewModel = new PagedViewModel + { + Total = relationItems.Total, + Items = _umbracoMapper.MapEnumerable(relationItems.Items), + }; + + return pagedViewModel; + } + /// /// Gets a paged list of the descendant nodes of the current item used in any kind of relation. /// @@ -32,19 +54,26 @@ public class ReferencedDescendantsDocumentController : DocumentControllerBase [HttpGet("{id:guid}/referenced-descendants")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> ReferencedDescendants( + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedDescendants2( CancellationToken cancellationToken, Guid id, int skip = 0, int take = 20) { - PagedModel relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true); + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Document, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + var pagedViewModel = new PagedViewModel { - Total = relationItems.Total, - Items = _umbracoMapper.MapEnumerable(relationItems.Items), + Total = relationItemsAttempt.Result.Total, + Items = _umbracoMapper.MapEnumerable(relationItemsAttempt.Result.Items), }; - return pagedViewModel; + return Ok(pagedViewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedByMediaController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedByMediaController.cs index e9e62504ed..eaee4269cd 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedByMediaController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedByMediaController.cs @@ -4,8 +4,10 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +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.Media.References; @@ -15,22 +17,16 @@ public class ReferencedByMediaController : MediaControllerBase private readonly ITrackedReferencesService _trackedReferencesService; private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; - public ReferencedByMediaController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory) + public ReferencedByMediaController( + ITrackedReferencesService trackedReferencesService, + IRelationTypePresentationFactory relationTypePresentationFactory) { _trackedReferencesService = trackedReferencesService; _relationTypePresentationFactory = relationTypePresentationFactory; } - /// - /// Gets a page list of tracked references for the current item, so you can see where an item is being used. - /// - /// - /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. - /// This is basically finding parents of relations. - /// - [HttpGet("{id:guid}/referenced-by")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")] + [NonAction] public async Task>> ReferencedBy( CancellationToken cancellationToken, Guid id, @@ -47,4 +43,37 @@ public class ReferencedByMediaController : MediaControllerBase return pagedViewModel; } + + /// + /// Gets a page list of tracked references for the current item, so you can see where an item is being used. + /// + /// + /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. + /// This is basically finding parents of relations. + /// + [HttpGet("{id:guid}/referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedBy2( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Media, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + + var pagedViewModel = new PagedViewModel + { + Total = relationItemsAttempt.Result.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items), + }; + + return Ok(pagedViewModel); + } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedDescendantsMediaController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedDescendantsMediaController.cs index c6c16cb222..c78b34a7ef 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedDescendantsMediaController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/References/ReferencedDescendantsMediaController.cs @@ -3,9 +3,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Api.Management.Controllers.Media.References; @@ -14,13 +16,32 @@ public class ReferencedDescendantsMediaController : MediaControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - - public ReferencedDescendantsMediaController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ReferencedDescendantsMediaController( + ITrackedReferencesService trackedReferencesSkipTakeService, + IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; } + [Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")] + [NonAction] + public async Task>> ReferencedDescendants( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + PagedModel relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true); + var pagedViewModel = new PagedViewModel + { + Total = relationItems.Total, + Items = _umbracoMapper.MapEnumerable(relationItems.Items), + }; + + return pagedViewModel; + } + /// /// Gets a page list of the child nodes of the current item used in any kind of relation. /// @@ -32,19 +53,26 @@ public class ReferencedDescendantsMediaController : MediaControllerBase [HttpGet("{id:guid}/referenced-descendants")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> ReferencedDescendants( + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedDescendants2( CancellationToken cancellationToken, Guid id, int skip = 0, int take = 20) { - PagedModel relationItems = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, skip, take, true); + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Media, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + var pagedViewModel = new PagedViewModel { - Total = relationItems.Total, - Items = _umbracoMapper.MapEnumerable(relationItems.Items), + Total = relationItemsAttempt.Result.Total, + Items = _umbracoMapper.MapEnumerable(relationItemsAttempt.Result.Items), }; - return pagedViewModel; + return Ok(pagedViewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedByMemberController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedByMemberController.cs index 69237278a5..4e03fbfbf2 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedByMemberController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedByMemberController.cs @@ -4,8 +4,10 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +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.Member.References; @@ -15,22 +17,16 @@ public class ReferencedByMemberController : MemberControllerBase private readonly ITrackedReferencesService _trackedReferencesService; private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; - public ReferencedByMemberController(ITrackedReferencesService trackedReferencesService, IRelationTypePresentationFactory relationTypePresentationFactory) + public ReferencedByMemberController( + ITrackedReferencesService trackedReferencesService, + IRelationTypePresentationFactory relationTypePresentationFactory) { _trackedReferencesService = trackedReferencesService; _relationTypePresentationFactory = relationTypePresentationFactory; } - /// - /// Gets a page list of tracked references for the current item, so you can see where an item is being used. - /// - /// - /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. - /// This is basically finding parents of relations. - /// - [HttpGet("{id:guid}/referenced-by")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [Obsolete("Use the ReferencedBy2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedBy2 will be renamed back to ReferencedBy.")] + [NonAction] public async Task>> ReferencedBy( CancellationToken cancellationToken, Guid id, @@ -47,4 +43,37 @@ public class ReferencedByMemberController : MemberControllerBase return pagedViewModel; } + + /// + /// Gets a page list of tracked references for the current item, so you can see where an item is being used. + /// + /// + /// Used by info tabs on content, media etc. and for the delete and unpublish of single items. + /// This is basically finding parents of relations. + /// + [HttpGet("{id:guid}/referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedBy2( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesService.GetPagedRelationsForItemAsync(id, UmbracoObjectTypes.Member, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + + var pagedViewModel = new PagedViewModel + { + Total = relationItemsAttempt.Result.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItemsAttempt.Result.Items), + }; + + return Ok(pagedViewModel); + } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedDescendantsMemberController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedDescendantsMemberController.cs index aa86950e6e..8521ea3805 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedDescendantsMemberController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Member/References/ReferencedDescendantsMemberController.cs @@ -3,9 +3,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Api.Management.Controllers.Member.References; @@ -15,23 +17,16 @@ public class ReferencedDescendantsMemberController : MemberControllerBase private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public ReferencedDescendantsMemberController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ReferencedDescendantsMemberController( + ITrackedReferencesService trackedReferencesSkipTakeService, + IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; } - /// - /// Gets a page list of the child nodes of the current item used in any kind of relation. - /// - /// - /// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any - /// kind of relation. - /// This is basically finding the descending items which are children in relations. - /// - [HttpGet("{id:guid}/referenced-descendants")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [Obsolete("Use the ReferencedDescendants2 action method instead. Scheduled for removal in Umbraco 19, when ReferencedDescendants2 will be renamed back to ReferencedDescendants.")] + [NonAction] public async Task>> ReferencedDescendants( CancellationToken cancellationToken, Guid id, @@ -47,4 +42,38 @@ public class ReferencedDescendantsMemberController : MemberControllerBase return pagedViewModel; } + + /// + /// Gets a page list of the child nodes of the current item used in any kind of relation. + /// + /// + /// Used when deleting and unpublishing a single item to check if this item has any descending items that are in any + /// kind of relation. + /// This is basically finding the descending items which are children in relations. + /// + [HttpGet("{id:guid}/referenced-descendants")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ReferencedDescendants2( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + Attempt, GetReferencesOperationStatus> relationItemsAttempt = await _trackedReferencesSkipTakeService.GetPagedDescendantsInReferencesAsync(id, UmbracoObjectTypes.Member, skip, take, true); + + if (relationItemsAttempt.Success is false) + { + return GetReferencesOperationStatusResult(relationItemsAttempt.Status); + } + + var pagedViewModel = new PagedViewModel + { + Total = relationItemsAttempt.Result.Total, + Items = _umbracoMapper.MapEnumerable(relationItemsAttempt.Result.Items), + }; + + return Ok(pagedViewModel); + } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 86d522dd3a..6c6801fd24 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -9628,6 +9628,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -9692,6 +9706,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -17348,6 +17376,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -17412,6 +17454,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -21653,6 +21709,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -21717,6 +21787,20 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, "401": { "description": "The resource is protected and requires an authentication token" }, @@ -49575,4 +49659,4 @@ "name": "Webhook" } ] -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Services/ITrackedReferencesService.cs b/src/Umbraco.Core/Services/ITrackedReferencesService.cs index cc97954846..ad84c79fcc 100644 --- a/src/Umbraco.Core/Services/ITrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/ITrackedReferencesService.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Core.Services; @@ -16,8 +17,27 @@ public interface ITrackedReferencesService /// dependencies (isDependency field is set to true). /// /// A paged result of objects. + [Obsolete("Use GetPagedRelationsForItemAsync which returns an Attempt with operation status. Scheduled for removal in Umbraco 19.")] Task> GetPagedRelationsForItemAsync(Guid key, long skip, long take, bool filterMustBeIsDependency); + /// + /// Gets a paged result of items which are in relation with the current item. + /// Basically, shows the items which depend on the current item. + /// + /// The identifier of the entity to retrieve relations for. + /// The Umbraco object type of the parent. + /// The amount of items to skip + /// The amount of items to take. + /// + /// A boolean indicating whether to filter only the RelationTypes which are + /// dependencies (isDependency field is set to true). + /// + /// A paged result of objects. + async Task, GetReferencesOperationStatus>> GetPagedRelationsForItemAsync(Guid key, UmbracoObjectTypes objectType, long skip, long take, bool filterMustBeIsDependency) +#pragma warning disable CS0618 // Type or member is obsolete + => Attempt.SucceedWithStatus(GetReferencesOperationStatus.Success, await GetPagedRelationsForItemAsync(key, skip, take, filterMustBeIsDependency)); +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets a paged result of items which are in relation with an item in the recycle bin. /// @@ -42,8 +62,26 @@ public interface ITrackedReferencesService /// dependencies (isDependency field is set to true). /// /// A paged result of objects. + [Obsolete("Use GetPagedDescendantsInReferencesAsync which returns an Attempt with operation status. Scheduled for removal in Umbraco 19.")] Task> GetPagedDescendantsInReferencesAsync(Guid parentKey, long skip, long take, bool filterMustBeIsDependency); + /// + /// Gets a paged result of the descending items that have any references, given a parent id. + /// + /// The unique identifier of the parent to retrieve descendants for. + /// The Umbraco object type of the parent. + /// The amount of items to skip + /// The amount of items to take. + /// + /// A boolean indicating whether to filter only the RelationTypes which are + /// dependencies (isDependency field is set to true). + /// + /// An wrapping a paged result of objects. + async Task, GetReferencesOperationStatus>> GetPagedDescendantsInReferencesAsync(Guid parentKey, UmbracoObjectTypes objectType, long skip, long take, bool filterMustBeIsDependency) +#pragma warning disable CS0618 // Type or member is obsolete + => Attempt.SucceedWithStatus(GetReferencesOperationStatus.Success, await GetPagedDescendantsInReferencesAsync(parentKey, skip, take, filterMustBeIsDependency)); +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets a paged result of items used in any kind of relation from selected integer ids. /// diff --git a/src/Umbraco.Core/Services/OperationStatus/GetReferencesOperationStatus.cs b/src/Umbraco.Core/Services/OperationStatus/GetReferencesOperationStatus.cs new file mode 100644 index 0000000000..2ba0e80dc8 --- /dev/null +++ b/src/Umbraco.Core/Services/OperationStatus/GetReferencesOperationStatus.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Cms.Core.Services.OperationStatus; + +public enum GetReferencesOperationStatus +{ + Success, + ContentNotFound +} diff --git a/src/Umbraco.Core/Services/TrackedReferencesService.cs b/src/Umbraco.Core/Services/TrackedReferencesService.cs index 6babae6e05..24332dcfa5 100644 --- a/src/Umbraco.Core/Services/TrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/TrackedReferencesService.cs @@ -1,6 +1,8 @@ using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Core.Services; @@ -20,6 +22,7 @@ public class TrackedReferencesService : ITrackedReferencesService _entityService = entityService; } + [Obsolete("Use the GetPagedRelationsForItemAsync overload which returns an Attempt with operation status. Scheduled for removal in Umbraco 19.")] public Task> GetPagedRelationsForItemAsync(Guid key, long skip, long take, bool filterMustBeIsDependency) { using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); @@ -29,6 +32,21 @@ public class TrackedReferencesService : ITrackedReferencesService return Task.FromResult(pagedModel); } + public async Task, GetReferencesOperationStatus>> GetPagedRelationsForItemAsync(Guid key, UmbracoObjectTypes objectType, long skip, long take, bool filterMustBeIsDependency) + { + IEntitySlim? entity = _entityService.Get(key, objectType); + if (entity is null) + { + return Attempt.FailWithStatus(GetReferencesOperationStatus.ContentNotFound, new PagedModel()); + } + +#pragma warning disable CS0618 // Type or member is obsolete (but using whilst it exists to avoid code repetition) + PagedModel pagedModel = await GetPagedRelationsForItemAsync(key, skip, take, filterMustBeIsDependency); +#pragma warning restore CS0618 // Type or member is obsolete + + return Attempt.SucceedWithStatus(GetReferencesOperationStatus.Success, pagedModel); + } + public Task> GetPagedRelationsForRecycleBinAsync(UmbracoObjectTypes objectType, long skip, long take, bool filterMustBeIsDependency) { Guid objectTypeKey = objectType switch @@ -44,6 +62,7 @@ public class TrackedReferencesService : ITrackedReferencesService return Task.FromResult(pagedModel); } + [Obsolete("Use GetPagedDescendantsInReferencesAsync which returns an Attempt with operation status. Scheduled for removal in Umbraco 19.")] public Task> GetPagedDescendantsInReferencesAsync(Guid parentKey, long skip, long take, bool filterMustBeIsDependency) { using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); @@ -59,6 +78,21 @@ public class TrackedReferencesService : ITrackedReferencesService return Task.FromResult(pagedModel); } + public async Task, GetReferencesOperationStatus>> GetPagedDescendantsInReferencesAsync(Guid parentKey, UmbracoObjectTypes objectType, long skip, long take, bool filterMustBeIsDependency) + { + IEntitySlim? entity = _entityService.Get(parentKey, objectType); + if (entity is null) + { + return Attempt.FailWithStatus(GetReferencesOperationStatus.ContentNotFound, new PagedModel()); + } + +#pragma warning disable CS0618 // Type or member is obsolete (but using whilst it exists to avoid code repetition) + PagedModel pagedModel = await GetPagedDescendantsInReferencesAsync(parentKey, skip, take, filterMustBeIsDependency); +#pragma warning restore CS0618 // Type or member is obsolete + + return Attempt.SucceedWithStatus(GetReferencesOperationStatus.Success, pagedModel); + } + public Task> GetPagedItemsWithRelationsAsync(ISet keys, long skip, long take, bool filterMustBeIsDependency) { using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/TrackedReferencesServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/TrackedReferencesServiceTests.cs index ae868d00fc..71e9020c8d 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/TrackedReferencesServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/TrackedReferencesServiceTests.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Infrastructure.Persistence.Relations; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Builders.Extensions; @@ -77,25 +78,55 @@ internal class TrackedReferencesServiceTests : UmbracoIntegrationTest { var sut = GetRequiredService(); - var actual = await sut.GetPagedRelationsForItemAsync(Root1.Key, 0, 10, true); + var actual = await sut.GetPagedRelationsForItemAsync(Root1.Key, UmbracoObjectTypes.Document, 0, 10, true); Assert.Multiple(() => { - Assert.AreEqual(1, actual.Total); - var item = actual.Items.FirstOrDefault(); + Assert.IsTrue(actual.Success); + Assert.AreEqual(1, actual.Result.Total); + var item = actual.Result.Items.FirstOrDefault(); Assert.AreEqual(Root2.ContentType.Alias, item?.ContentTypeAlias); Assert.AreEqual(Root2.Key, item?.NodeKey); }); } + [Test] + public async Task Get_Relations_For_Non_Existing_Page_Returns_Not_Found() + { + var sut = GetRequiredService(); + + var actual = await sut.GetPagedRelationsForItemAsync(Guid.NewGuid(), UmbracoObjectTypes.Document, 0, 10, true); + + Assert.Multiple(() => + { + Assert.IsFalse(actual.Success); + Assert.AreEqual(GetReferencesOperationStatus.ContentNotFound, actual.Status); + }); + } + + [Test] + public async Task Get_Descendants_In_References_For_Non_Existing_Page_Returns_Not_Found() + { + var sut = GetRequiredService(); + + var actual = await sut.GetPagedDescendantsInReferencesAsync(Guid.NewGuid(), UmbracoObjectTypes.Document, 0, 10, true); + + Assert.Multiple(() => + { + Assert.IsFalse(actual.Success); + Assert.AreEqual(GetReferencesOperationStatus.ContentNotFound, actual.Status); + }); + } + [Test] public async Task Does_Not_Return_References_If_Item_Is_Not_Referenced() { var sut = GetRequiredService(); - var actual = await sut.GetPagedRelationsForItemAsync(Root2.Key, 0, 10, true); + var actual = await sut.GetPagedRelationsForItemAsync(Root2.Key, UmbracoObjectTypes.Document, 0, 10, true); - Assert.AreEqual(0, actual.Total); + Assert.IsTrue(actual.Success); + Assert.AreEqual(0, actual.Result.Total); } [Test]