V14/feature/all segment endpoint (#16054)

* Added All Segments service with backing noopService

* Add openapi spec

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* Fix ctor

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Sven Geusens
2024-04-18 12:20:26 +02:00
committed by GitHub
parent 337d547698
commit d04769a84b
12 changed files with 264 additions and 2 deletions

View File

@@ -0,0 +1,50 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.ViewModels.Segment;
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;
using Umbraco.Cms.Web.Common.Authorization;
namespace Umbraco.Cms.Api.Management.Controllers.Segment;
[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class AllSegmentController : SegmentControllerBase
{
private readonly ISegmentService _segmentService;
private readonly IUmbracoMapper _umbracoMapper;
public AllSegmentController(ISegmentService segmentService, IUmbracoMapper umbracoMapper)
{
_segmentService = segmentService;
_umbracoMapper = umbracoMapper;
}
[HttpGet]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedViewModel<SegmentResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetAll(CancellationToken cancellationToken, int skip = 0, int take = 100)
{
Attempt<PagedModel<Core.Models.Segment>?, SegmentOperationStatus> pagedAttempt = await _segmentService.GetPagedSegmentsAsync(skip, take);
if (pagedAttempt.Success is false)
{
return MapFailure(pagedAttempt.Status);
}
var viewModel = new PagedViewModel<SegmentResponseModel>
{
Items = _umbracoMapper.MapEnumerable<Core.Models.Segment, SegmentResponseModel>(pagedAttempt.Result!.Items),
Total = pagedAttempt.Result!.Total,
};
return Ok(viewModel);
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Api.Management.Controllers.Segment;
[VersionedApiBackOfficeRoute("segment")]
[ApiExplorerSettings(GroupName = "Segment")]
public abstract class SegmentControllerBase : ManagementApiControllerBase
{
protected IActionResult MapFailure(SegmentOperationStatus status)
=> OperationStatusResult(status, problemDetailsBuilder => status switch
{
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
.WithTitle("Unknown segment operation status.")
.Build()),
});
}

View File

@@ -0,0 +1,15 @@
using Umbraco.Cms.Api.Management.Mapping.Segment;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
namespace Umbraco.Cms.Api.Management.DependencyInjection;
internal static class SegmentBuilderExtensions
{
internal static IUmbracoBuilder AddSegment(this IUmbracoBuilder builder)
{
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<SegmentMapDefinition>();
return builder;
}
}

View File

@@ -67,7 +67,8 @@ public static partial class UmbracoBuilderExtensions
.AddPreview()
.AddPasswordConfiguration()
.AddSupplemenataryLocalizedTextFileSources()
.AddUserData();
.AddUserData()
.AddSegment();
services
.ConfigureOptions<ConfigureApiBehaviorOptions>()

View File

@@ -0,0 +1,16 @@
using Umbraco.Cms.Api.Management.ViewModels.Segment;
using Umbraco.Cms.Core.Mapping;
namespace Umbraco.Cms.Api.Management.Mapping.Segment;
public class SegmentMapDefinition : IMapDefinition
{
public void DefineMaps(IUmbracoMapper mapper) => mapper.Define<Core.Models.Segment, SegmentResponseModel>((_, _) => new SegmentResponseModel { Name = string.Empty, Alias = string.Empty }, Map);
// Umbraco.Code.MapAll
private static void Map(Core.Models.Segment source, SegmentResponseModel target, MapperContext context)
{
target.Name = source.Name;
target.Alias = source.Alias;
}
}

View File

@@ -23974,6 +23974,75 @@
}
}
},
"/umbraco/management/api/v1/segment": {
"get": {
"tags": [
"Segment"
],
"operationId": "GetSegment",
"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": {
"oneOf": [
{
"$ref": "#/components/schemas/PagedSegmentResponseModel"
}
]
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ProblemDetails"
}
]
}
}
}
},
"401": {
"description": "The resource is protected and requires an authentication token"
},
"403": {
"description": "The authenticated user do not have access to this resource"
}
},
"security": [
{
"Backoffice User": [ ]
}
]
}
},
"/umbraco/management/api/v1/server/configuration": {
"get": {
"tags": [
@@ -39861,6 +39930,30 @@
},
"additionalProperties": false
},
"PagedSegmentResponseModel": {
"required": [
"items",
"total"
],
"type": "object",
"properties": {
"total": {
"type": "integer",
"format": "int64"
},
"items": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/components/schemas/SegmentResponseModel"
}
]
}
}
},
"additionalProperties": false
},
"PagedTagResponseModel": {
"required": [
"items",
@@ -40836,6 +40929,24 @@
},
"additionalProperties": false
},
"SegmentResponseModel": {
"required": [
"alias",
"name"
],
"type": "object",
"properties": {
"name": {
"minLength": 1,
"type": "string"
},
"alias": {
"minLength": 1,
"type": "string"
}
},
"additionalProperties": false
},
"ServerConfigurationItemResponseModel": {
"required": [
"data",
@@ -43568,4 +43679,4 @@
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Api.Management.ViewModels.Segment;
public class SegmentResponseModel
{
public required string Name { get; set; } = string.Empty;
public required string Alias { get; set; } = string.Empty;
}

View File

@@ -390,6 +390,9 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddSingleton<IMediaPermissionAuthorizer, MediaPermissionAuthorizer>();
Services.AddSingleton<IUserGroupPermissionAuthorizer, UserGroupPermissionAuthorizer>();
Services.AddSingleton<IUserPermissionAuthorizer, UserPermissionAuthorizer>();
// Segments
Services.AddUnique<ISegmentService, NoopSegmentService>();
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Cms.Core.Models;
public class Segment
{
public required string Name { get; set; }
public required string Alias { get; set; }
}

View File

@@ -0,0 +1,9 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
public interface ISegmentService
{
Task<Attempt<PagedModel<Segment>?, SegmentOperationStatus>> GetPagedSegmentsAsync(int skip = 0, int take = 100);
}

View File

@@ -0,0 +1,14 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
public class NoopSegmentService : ISegmentService
{
public async Task<Attempt<PagedModel<Segment>?, SegmentOperationStatus>> GetPagedSegmentsAsync(int skip = 0, int take = 100)
{
return await Task.FromResult(Attempt.SucceedWithStatus<PagedModel<Segment>?, SegmentOperationStatus>(
SegmentOperationStatus.Success,
new PagedModel<Segment> { Total = 0, Items = Enumerable.Empty<Segment>() }));
}
}

View File

@@ -0,0 +1,6 @@
namespace Umbraco.Cms.Core.Services.OperationStatus;
public enum SegmentOperationStatus
{
Success,
}