diff --git a/src/Umbraco.Core/Services/IContentQueryOperationService.cs b/src/Umbraco.Core/Services/IContentQueryOperationService.cs new file mode 100644 index 0000000000..06bbaf06af --- /dev/null +++ b/src/Umbraco.Core/Services/IContentQueryOperationService.cs @@ -0,0 +1,123 @@ +// src/Umbraco.Core/Services/IContentQueryOperationService.cs +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Querying; + +namespace Umbraco.Cms.Core.Services; + +/// +/// Service for content query operations (counting, filtering by type/level). +/// +/// +/// +/// Implementation Note: Do not implement this interface directly. +/// Instead, inherit from which provides required +/// infrastructure (scoping, repository access, auditing). Direct implementation +/// without this base class will result in missing functionality. +/// +/// +/// This interface is part of the ContentService refactoring initiative (Phase 2). +/// It extracts query operations into a focused, testable service. +/// +/// +/// Versioning Policy: This interface follows additive-only changes. +/// New methods may be added with default implementations. Existing methods will not +/// be removed or have signatures changed without a 2 major version deprecation period. +/// +/// +/// Version History: +/// +/// v1.0 (Phase 2): Initial interface with Count, GetByLevel, GetPagedOfType operations +/// +/// +/// +/// 1.0 +public interface IContentQueryOperationService : IService +{ + #region Count Operations + + /// + /// Counts content items, optionally filtered by content type. + /// + /// Optional content type alias to filter by. If the alias doesn't exist, returns 0. + /// The count of matching content items (includes trashed items). + int Count(string? contentTypeAlias = null); + + /// + /// Counts published content items, optionally filtered by content type. + /// + /// Optional content type alias to filter by. If the alias doesn't exist, returns 0. + /// The count of matching published content items. + int CountPublished(string? contentTypeAlias = null); + + /// + /// Counts children of a parent, optionally filtered by content type. + /// + /// The parent content id. If the parent doesn't exist, returns 0. + /// Optional content type alias to filter by. If the alias doesn't exist, returns 0. + /// The count of matching child content items. + int CountChildren(int parentId, string? contentTypeAlias = null); + + /// + /// Counts descendants of an ancestor, optionally filtered by content type. + /// + /// The ancestor content id. If the ancestor doesn't exist, returns 0. + /// Optional content type alias to filter by. If the alias doesn't exist, returns 0. + /// The count of matching descendant content items. + int CountDescendants(int parentId, string? contentTypeAlias = null); + + #endregion + + #region Hierarchy Queries + + /// + /// Gets content items at a specific tree level. + /// + /// The tree level (1 = root children, 2 = grandchildren, etc.). + /// Content items at the specified level, excluding trashed items. + IEnumerable GetByLevel(int level); + + #endregion + + #region Paged Type Queries + + /// + /// Gets paged content items of a specific content type. + /// + /// The content type id. If the content type doesn't exist, returns empty results with totalRecords = 0. + /// Zero-based page index. + /// Page size. + /// Output: total number of matching records. + /// Optional filter query. + /// Optional ordering (defaults to sortOrder). + /// Paged content items. + /// Thrown when pageIndex is negative or pageSize is less than or equal to zero. + IEnumerable GetPagedOfType( + int contentTypeId, + long pageIndex, + int pageSize, + out long totalRecords, + IQuery? filter = null, + Ordering? ordering = null); + + /// + /// Gets paged content items of multiple content types. + /// + /// The content type ids. If empty or containing non-existent IDs, returns empty results with totalRecords = 0. + /// Zero-based page index. + /// Page size. + /// Output: total number of matching records. + /// Optional filter query. + /// Optional ordering (defaults to sortOrder). + /// Paged content items. + /// Thrown when contentTypeIds is null. + /// Thrown when pageIndex is negative or pageSize is less than or equal to zero. + IEnumerable GetPagedOfTypes( + int[] contentTypeIds, + long pageIndex, + int pageSize, + out long totalRecords, + IQuery? filter = null, + Ordering? ordering = null); + + #endregion +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentQueryOperationServiceInterfaceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentQueryOperationServiceInterfaceTests.cs new file mode 100644 index 0000000000..9f6bafed14 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentQueryOperationServiceInterfaceTests.cs @@ -0,0 +1,30 @@ +// tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentQueryOperationServiceInterfaceTests.cs +using NUnit.Framework; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Services; + +[TestFixture] +public class ContentQueryOperationServiceInterfaceTests +{ + [Test] + public void IContentQueryOperationService_Interface_Exists() + { + // Arrange & Act + var interfaceType = typeof(IContentQueryOperationService); + + // Assert + Assert.That(interfaceType, Is.Not.Null); + Assert.That(interfaceType.IsInterface, Is.True); + } + + [Test] + public void IContentQueryOperationService_Extends_IService() + { + // Arrange + var interfaceType = typeof(IContentQueryOperationService); + + // Act & Assert + Assert.That(typeof(IService).IsAssignableFrom(interfaceType), Is.True); + } +}