Added functionality to split det swagger doc into multiple definitions (#14126)
* Added functionality to split det swagger doc into multiple definitions. Most (maybe too much) is still shared between the apis. * Fixed issue with duplicate operation ids when we have multiple versions of an endpoint * use strong types instead of var * Updated OpenApi.json * Formatting * formatting * Delete old files that is not implemented in api.common * Updated openAPi after merge --------- Co-authored-by: Zeegaan <nge@umbraco.dk>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Cms.Api.Common.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class MapToApiAttribute : Attribute
|
||||
{
|
||||
public MapToApiAttribute(string apiName) => ApiName = apiName;
|
||||
|
||||
public string ApiName { get; }
|
||||
}
|
||||
@@ -20,6 +20,6 @@ public class ConfigureApiExplorerOptions : IConfigureOptions<ApiExplorerOptions>
|
||||
options.GroupNameFormat = "'v'VVV";
|
||||
options.SubstituteApiVersionInUrl = true;
|
||||
options.AddApiVersionParametersWhenVersionNeutral = true;
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.AssumeDefaultVersionWhenUnspecified = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ public class ConfigureApiVersioningOptions : IConfigureOptions<ApiVersioningOpti
|
||||
options.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
options.ReportApiVersions = true;
|
||||
options.ApiVersionReader = new UrlSegmentApiVersionReader();
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);
|
||||
options.AssumeDefaultVersionWhenUnspecified = true; // This is required for the old backoffice to work
|
||||
options.UseApiBehavior = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Security;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
using Umbraco.Cms.Api.Common.Attributes;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Extensions;
|
||||
using OperationIdRegexes = Umbraco.Cms.Api.Common.OpenApi.OperationIdRegexes;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
namespace Umbraco.Cms.Api.Common.Configuration;
|
||||
|
||||
internal sealed class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
|
||||
public ConfigureUmbracoSwaggerGenOptions(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
|
||||
{
|
||||
_umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
|
||||
}
|
||||
|
||||
public void Configure(SwaggerGenOptions swaggerGenOptions)
|
||||
{
|
||||
swaggerGenOptions.SwaggerDoc(
|
||||
ManagementApiConfiguration.DefaultApiDocumentName,
|
||||
DefaultApiConfiguration.ApiName,
|
||||
new OpenApiInfo
|
||||
{
|
||||
Title = ManagementApiConfiguration.ApiTitle,
|
||||
Version = ManagementApiConfiguration.DefaultApiVersion.ToString(),
|
||||
Description =
|
||||
"This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility"
|
||||
Title = "Default API",
|
||||
Version = "Latest",
|
||||
Description = "All endpoints not defined under specific APIs"
|
||||
});
|
||||
|
||||
swaggerGenOptions.AddSecurityDefinition(
|
||||
"OAuth",
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
In = ParameterLocation.Header,
|
||||
Name = "Umbraco",
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Description = "Umbraco Authentication",
|
||||
Flows = new OpenApiOAuthFlows
|
||||
{
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl =
|
||||
new Uri(Paths.BackOfficeApiAuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.BackOfficeApiTokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
// this weird looking construct works because OpenApiSecurityRequirement
|
||||
// is a specialization of Dictionary<,>
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference { Id = "OAuth", Type = ReferenceType.SecurityScheme }
|
||||
},
|
||||
new List<string>()
|
||||
}
|
||||
});
|
||||
|
||||
swaggerGenOptions.CustomOperationIds(CustomOperationId);
|
||||
swaggerGenOptions.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName));
|
||||
|
||||
swaggerGenOptions.DocInclusionPredicate((name, api) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(api.GroupName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (api.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
{
|
||||
return controllerActionDescriptor.MethodInfo.HasMapToApiAttribute(name);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
swaggerGenOptions.TagActionsBy(api => new[] { api.GroupName });
|
||||
swaggerGenOptions.OrderActionsBy(ActionOrderBy);
|
||||
swaggerGenOptions.DocumentFilter<MimeTypeDocumentFilter>();
|
||||
@@ -72,10 +48,11 @@ internal sealed class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<Swag
|
||||
swaggerGenOptions.CustomSchemaIds(SchemaIdGenerator.Generate);
|
||||
swaggerGenOptions.SupportNonNullableReferenceTypes();
|
||||
|
||||
swaggerGenOptions.OperationFilter<ReponseHeaderOperationFilter>();
|
||||
|
||||
swaggerGenOptions.UseOneOfForPolymorphism();
|
||||
swaggerGenOptions.UseAllOfForInheritance();
|
||||
swaggerGenOptions.SelectSubTypesUsing(_umbracoJsonTypeInfoResolver.FindSubTypes);
|
||||
|
||||
|
||||
|
||||
swaggerGenOptions.SelectDiscriminatorNameUsing(type =>
|
||||
{
|
||||
@@ -89,7 +66,7 @@ internal sealed class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<Swag
|
||||
swaggerGenOptions.SelectDiscriminatorValueUsing(x => x.Name);
|
||||
}
|
||||
|
||||
private static string CustomOperationId(ApiDescription api)
|
||||
private static string CustomOperationId(ApiDescription api)
|
||||
{
|
||||
var httpMethod = api.HttpMethod?.ToLower().ToFirstUpper() ?? "Get";
|
||||
|
||||
@@ -127,12 +104,21 @@ internal sealed class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions<Swag
|
||||
.ToCamelCaseRegex()
|
||||
.Replace(formattedOperationId, m => m.Groups[1].Value.ToUpper());
|
||||
|
||||
//Get map to version attribute
|
||||
string? version = null;
|
||||
|
||||
if (api.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
{
|
||||
version = controllerActionDescriptor.MethodInfo.GetMapToApiVersionAttributeValue();
|
||||
}
|
||||
|
||||
// Return the operation ID with the formatted http method verb in front, e.g. GetTrackedReferenceById
|
||||
return $"{httpMethod}{formattedOperationId.ToFirstUpper()}";
|
||||
return $"{httpMethod}{formattedOperationId.ToFirstUpper()}{version}";
|
||||
}
|
||||
|
||||
// see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting
|
||||
private static string ActionOrderBy(ApiDescription apiDesc)
|
||||
=>
|
||||
$"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.ActionDescriptor.RouteValues["action"]}_{apiDesc.HttpMethod}";
|
||||
// see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting
|
||||
private static string ActionOrderBy(ApiDescription apiDesc)
|
||||
=>
|
||||
$"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.ActionDescriptor.RouteValues["action"]}_{apiDesc.HttpMethod}";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Common.Configuration;
|
||||
|
||||
internal static class DefaultApiConfiguration
|
||||
{
|
||||
public const string ApiName = "default";
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.Configuration;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
using Umbraco.Extensions;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
|
||||
public static class UmbracoBuilderApiExtensions
|
||||
{
|
||||
public static IUmbracoBuilder AddUmbracoApiOpenApiUI(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.ConfigureOptions<ConfigureApiVersioningOptions>();
|
||||
builder.Services.AddApiVersioning();
|
||||
builder.Services.ConfigureOptions<ConfigureApiExplorerOptions>();
|
||||
builder.Services.AddVersionedApiExplorer();
|
||||
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.ConfigureOptions<ConfigureUmbracoSwaggerGenOptions>();
|
||||
builder.Services.AddSingleton<IUmbracoJsonTypeInfoResolver, UmbracoJsonTypeInfoResolver>();
|
||||
|
||||
builder.Services.Configure<UmbracoPipelineOptions>(options =>
|
||||
{
|
||||
options.AddFilter(new UmbracoPipelineFilter(
|
||||
"UmbracoApiCommon",
|
||||
applicationBuilder =>
|
||||
{
|
||||
|
||||
},
|
||||
applicationBuilder =>
|
||||
{
|
||||
IServiceProvider provider = applicationBuilder.ApplicationServices;
|
||||
IWebHostEnvironment webHostEnvironment = provider.GetRequiredService<IWebHostEnvironment>();
|
||||
IOptions<SwaggerGenOptions> swaggerGenOptions = provider.GetRequiredService<IOptions<SwaggerGenOptions>>();
|
||||
|
||||
|
||||
if (!webHostEnvironment.IsProduction())
|
||||
{
|
||||
GlobalSettings? settings = provider.GetRequiredService<IOptions<GlobalSettings>>().Value;
|
||||
IHostingEnvironment hostingEnvironment = provider.GetRequiredService<IHostingEnvironment>();
|
||||
var umbracoPath = settings.GetBackOfficePath(hostingEnvironment);
|
||||
|
||||
applicationBuilder.UseSwagger(swaggerOptions =>
|
||||
{
|
||||
swaggerOptions.RouteTemplate =
|
||||
$"{umbracoPath.TrimStart(Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json";
|
||||
});
|
||||
applicationBuilder.UseSwaggerUI(
|
||||
swaggerUiOptions =>
|
||||
{
|
||||
swaggerUiOptions.RoutePrefix = $"{umbracoPath.TrimStart(Constants.CharArrays.ForwardSlash)}/swagger";
|
||||
|
||||
foreach ((var name, OpenApiInfo? apiInfo) in swaggerGenOptions.Value.SwaggerGeneratorOptions.SwaggerDocs.OrderBy(x=>x.Value.Title))
|
||||
{
|
||||
swaggerUiOptions.SwaggerEndpoint($"{name}/swagger.json", $"{apiInfo.Title}");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
applicationBuilder =>
|
||||
{
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Attributes;
|
||||
using Umbraco.Cms.Api.Common.Configuration;
|
||||
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class MethodInfoApiCommonExtensions
|
||||
{
|
||||
|
||||
public static string? GetMapToApiVersionAttributeValue(this MethodInfo methodInfo)
|
||||
{
|
||||
MapToApiVersionAttribute[] mapToApis = methodInfo.GetCustomAttributes(typeof(MapToApiVersionAttribute), inherit: true).Cast<MapToApiVersionAttribute>().ToArray();
|
||||
|
||||
return string.Join("|", mapToApis.SelectMany(x=>x.Versions));
|
||||
}
|
||||
|
||||
public static string? GetMapToApiAttributeValue(this MethodInfo methodInfo)
|
||||
{
|
||||
MapToApiAttribute[] mapToApis = (methodInfo.DeclaringType?.GetCustomAttributes(typeof(MapToApiAttribute), inherit: true) ?? Array.Empty<object>()).Cast<MapToApiAttribute>().ToArray();
|
||||
|
||||
return mapToApis.SingleOrDefault()?.ApiName;
|
||||
}
|
||||
|
||||
public static bool HasMapToApiAttribute(this MethodInfo methodInfo, string apiName)
|
||||
{
|
||||
var value = methodInfo.GetMapToApiAttributeValue();
|
||||
|
||||
return value == apiName
|
||||
|| (value is null && apiName == DefaultApiConfiguration.ApiName);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
public class EnumSchemaFilter : ISchemaFilter
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
/// <summary>
|
||||
/// This filter explicitly removes all other mime types than application/json from the produced OpenAPI document when application/json is accepted.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
/// <summary>
|
||||
/// This is the regexes used to generate the operation IDs, the benefit of this being partial with GeneratedRegex
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
internal static class SchemaIdGenerator
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
namespace Umbraco.Cms.Api.Common.Serialization;
|
||||
|
||||
public interface IUmbracoJsonTypeInfoResolver : IJsonTypeInfoResolver
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
namespace Umbraco.Cms.Api.Common.Serialization;
|
||||
|
||||
public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, IUmbracoJsonTypeInfoResolver
|
||||
{
|
||||
@@ -11,8 +11,11 @@
|
||||
<!-- <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />-->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
|
||||
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Configuration;
|
||||
|
||||
public class ConfigureUmbracoDeliveryApiSwaggerGenOptions: IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
public void Configure(SwaggerGenOptions swaggerGenOptions)
|
||||
{
|
||||
swaggerGenOptions.SwaggerDoc(
|
||||
DeliveryApiConfiguration.ApiName,
|
||||
new OpenApiInfo
|
||||
{
|
||||
Title = DeliveryApiConfiguration.ApiTitle,
|
||||
Version = "Latest",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Delivery.Configuration;
|
||||
|
||||
internal static class DeliveryApiConfiguration
|
||||
{
|
||||
internal const string ApiTitle = "Umbraco Delivery API";
|
||||
|
||||
internal const string ApiName = "delivery";
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Attributes;
|
||||
using Umbraco.Cms.Api.Common.Filters;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
@@ -10,6 +12,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers;
|
||||
[DeliveryApiAccess]
|
||||
[JsonOptionsName(Constants.JsonOptionsNames.DeliveryApi)]
|
||||
[LocalizeFromAcceptLanguageHeader]
|
||||
[MapToApi(DeliveryApiConfiguration.ApiName)]
|
||||
public abstract class DeliveryApiControllerBase : Controller
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.Configuration;
|
||||
using Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Delivery.Accessors;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Json;
|
||||
using Umbraco.Cms.Api.Delivery.Rendering;
|
||||
using Umbraco.Cms.Api.Delivery.Services;
|
||||
@@ -28,10 +29,8 @@ public static class UmbracoBuilderExtensions
|
||||
builder.Services.AddSingleton<IApiAccessService, ApiAccessService>();
|
||||
builder.Services.AddSingleton<IApiContentQueryService, ApiContentQueryService>();
|
||||
|
||||
builder.Services.ConfigureOptions<ConfigureApiVersioningOptions>();
|
||||
builder.Services.AddApiVersioning();
|
||||
builder.Services.ConfigureOptions<ConfigureApiExplorerOptions>();
|
||||
builder.Services.AddVersionedApiExplorer();
|
||||
builder.Services.ConfigureOptions<ConfigureUmbracoDeliveryApiSwaggerGenOptions>();
|
||||
builder.AddUmbracoApiOpenApiUI();
|
||||
|
||||
builder
|
||||
.Services
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Security;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.OpenApi;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Configuration;
|
||||
|
||||
public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
private IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
|
||||
public ConfigureUmbracoManagementApiSwaggerGenOptions(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
|
||||
{
|
||||
_umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver;
|
||||
}
|
||||
|
||||
public void Configure(SwaggerGenOptions swaggerGenOptions)
|
||||
{
|
||||
swaggerGenOptions.SwaggerDoc(
|
||||
ManagementApiConfiguration.ApiName,
|
||||
new OpenApiInfo
|
||||
{
|
||||
Title = ManagementApiConfiguration.ApiTitle,
|
||||
Version = "Latest",
|
||||
Description = "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility",
|
||||
});
|
||||
|
||||
swaggerGenOptions.OperationFilter<ResponseHeaderOperationFilter>();
|
||||
swaggerGenOptions.SelectSubTypesUsing(_umbracoJsonTypeInfoResolver.FindSubTypes);
|
||||
|
||||
swaggerGenOptions.AddSecurityDefinition(
|
||||
"OAuth",
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
In = ParameterLocation.Header,
|
||||
Name = "Umbraco",
|
||||
Type = SecuritySchemeType.OAuth2,
|
||||
Description = "Umbraco Authentication",
|
||||
Flows = new OpenApiOAuthFlows
|
||||
{
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl =
|
||||
new Uri(Paths.BackOfficeApiAuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.BackOfficeApiTokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
// this weird looking construct works because OpenApiSecurityRequirement
|
||||
// is a specialization of Dictionary<,>
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference { Id = "OAuth", Type = ReferenceType.SecurityScheme }
|
||||
},
|
||||
new List<string>()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Attributes;
|
||||
using Umbraco.Cms.Api.Common.Filters;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.New.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers;
|
||||
|
||||
[MapToApi(ManagementApiConfiguration.ApiName)]
|
||||
[JsonOptionsName(Constants.JsonOptionsNames.BackOffice)]
|
||||
public class ManagementApiControllerBase : Controller
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using Umbraco.Cms.Api.Management.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.RecycleBin;
|
||||
|
||||
public abstract class RecycleBinControllerBase<TItem> : Controller
|
||||
public abstract class RecycleBinControllerBase<TItem> : ManagementApiControllerBase
|
||||
where TItem : RecycleBinItemResponseModel, new()
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class ApiVersioningBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddApiVersioning(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddApiVersioning(options =>
|
||||
{
|
||||
options.DefaultApiVersion = ManagementApiConfiguration.DefaultApiVersion;
|
||||
options.ReportApiVersions = true;
|
||||
options.ApiVersionReader = new UrlSegmentApiVersionReader();
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.UseApiBehavior = false;
|
||||
});
|
||||
|
||||
builder.Services.AddVersionedApiExplorer(options =>
|
||||
{
|
||||
options.DefaultApiVersion = ManagementApiConfiguration.DefaultApiVersion;
|
||||
options.GroupNameFormat = "'v'VVV";
|
||||
options.SubstituteApiVersionInUrl = true;
|
||||
options.AddApiVersionParametersWhenVersionNeutral = true;
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -52,37 +52,6 @@ internal static class ApplicationBuilderExtensions
|
||||
}));
|
||||
});
|
||||
|
||||
internal static IApplicationBuilder UseSwagger(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
IServiceProvider provider = applicationBuilder.ApplicationServices;
|
||||
IWebHostEnvironment webHostEnvironment = provider.GetRequiredService<IWebHostEnvironment>();
|
||||
|
||||
if (webHostEnvironment.IsProduction())
|
||||
{
|
||||
return applicationBuilder;
|
||||
}
|
||||
|
||||
GlobalSettings settings = provider.GetRequiredService<IOptions<GlobalSettings>>().Value;
|
||||
IHostingEnvironment hostingEnvironment = provider.GetRequiredService<IHostingEnvironment>();
|
||||
var backOfficePath = settings.GetBackOfficePath(hostingEnvironment);
|
||||
|
||||
applicationBuilder.UseSwagger(swaggerOptions =>
|
||||
{
|
||||
swaggerOptions.RouteTemplate = $"{backOfficePath.TrimStart(Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json";
|
||||
});
|
||||
applicationBuilder.UseSwaggerUI(
|
||||
swaggerUiOptions =>
|
||||
{
|
||||
swaggerUiOptions.SwaggerEndpoint($"{backOfficePath}/swagger/v1/swagger.json", $"{ManagementApiConfiguration.ApiTitle} {ManagementApiConfiguration.DefaultApiVersion}");
|
||||
swaggerUiOptions.RoutePrefix = $"{backOfficePath.TrimStart(Constants.CharArrays.ForwardSlash)}/swagger";
|
||||
|
||||
swaggerUiOptions.OAuthClientId(New.Cms.Core.Constants.OauthClientIds.Swagger);
|
||||
swaggerUiOptions.OAuthUsePkce();
|
||||
});
|
||||
|
||||
return applicationBuilder;
|
||||
}
|
||||
|
||||
internal static IApplicationBuilder UseEndpoints(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
IServiceProvider provider = applicationBuilder.ApplicationServices;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Dictionary;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Security;
|
||||
using Umbraco.Cms.Api.Management.OpenApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class ManagementApiBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddSwaggerGen(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.ConfigureOptions<ConfigureUmbracoSwaggerGenOptions>();
|
||||
builder.Services.AddSingleton<IUmbracoJsonTypeInfoResolver, UmbracoJsonTypeInfoResolver>();
|
||||
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@ namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class ManagementApiConfiguration
|
||||
{
|
||||
internal const string ApiTitle = "Umbraco Backoffice API";
|
||||
internal const string ApiTitle = "Umbraco Management API";
|
||||
|
||||
internal const string DefaultApiDocumentName = "v1";
|
||||
|
||||
internal static ApiVersion DefaultApiVersion => new(1, 0);
|
||||
internal const string ApiName = "management";
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.Configuration;
|
||||
using Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Configuration;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Serialization;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
@@ -46,21 +47,11 @@ public class ManagementApiComposer : IComposer
|
||||
.AddUserGroups()
|
||||
.AddPackages()
|
||||
.AddEntitys()
|
||||
.AddBackOfficeAuthentication()
|
||||
.AddApiVersioning()
|
||||
.AddSwaggerGen();
|
||||
.AddBackOfficeAuthentication();
|
||||
|
||||
services
|
||||
.ConfigureOptions<ConfigureMvcOptions>()
|
||||
.ConfigureOptions<ConfigureApiBehaviorOptions>()
|
||||
.Configure<UmbracoPipelineOptions>(options =>
|
||||
{
|
||||
options.AddFilter(new UmbracoPipelineFilter(
|
||||
"BackOfficeManagementApiFilter",
|
||||
applicationBuilder => applicationBuilder.UseProblemDetailsExceptionHandling(),
|
||||
applicationBuilder => applicationBuilder.UseSwagger(),
|
||||
applicationBuilder => applicationBuilder.UseEndpoints()));
|
||||
})
|
||||
.AddControllers()
|
||||
.AddJsonOptions(_ =>
|
||||
{
|
||||
@@ -69,6 +60,16 @@ public class ManagementApiComposer : IComposer
|
||||
.AddJsonOptions(New.Cms.Core.Constants.JsonOptionsNames.BackOffice, _ => { });
|
||||
|
||||
services.ConfigureOptions<ConfigureUmbracoBackofficeJsonOptions>( );
|
||||
services.ConfigureOptions<ConfigureUmbracoManagementApiSwaggerGenOptions>( );
|
||||
|
||||
services.Configure<UmbracoPipelineOptions>(options =>
|
||||
{
|
||||
options.AddFilter(new UmbracoPipelineFilter(
|
||||
"BackOfficeManagementApiFilter",
|
||||
applicationBuilder => applicationBuilder.UseProblemDetailsExceptionHandling(),
|
||||
applicationBuilder => { },
|
||||
applicationBuilder => applicationBuilder.UseEndpoints()));
|
||||
});
|
||||
|
||||
// FIXME: when this is moved to core, make the AddUmbracoOptions extension private again and remove core InternalsVisibleTo for Umbraco.Cms.Api.Management
|
||||
builder.AddUmbracoOptions<NewBackOfficeSettings>();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
|
||||
internal class ReponseHeaderOperationFilter : IOperationFilter
|
||||
internal class ResponseHeaderOperationFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (context.MethodInfo.HasMapToApiAttribute(ManagementApiConfiguration.ApiName) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ((var key, OpenApiResponse? value) in operation.Responses)
|
||||
{
|
||||
switch (int.Parse(key))
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Serialization;
|
||||
|
||||
@@ -35,7 +35,7 @@ internal sealed class OpenAPIContractTest : UmbracoTestServerTestBase
|
||||
var officePath = GlobalSettings.GetBackOfficePath(HostingEnvironment);
|
||||
|
||||
var urlToContract = $"{officePath}/management/api/openapi.json";
|
||||
var swaggerPath = $"{officePath}/swagger/v1/swagger.json";
|
||||
var swaggerPath = $"{officePath}/swagger/management/swagger.json";
|
||||
var apiContract = JObject.Parse(await Client.GetStringAsync(urlToContract));
|
||||
|
||||
var generatedJsonString = await Client.GetStringAsync(swaggerPath);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
services.AddSingleton<IWebProfilerRepository, TestWebProfilerRepository>();
|
||||
|
||||
|
||||
// Add a test auth scheme with a test auth handler to authn and assign the user
|
||||
services.AddAuthentication(TestAuthHandler.TestAuthenticationScheme)
|
||||
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
|
||||
@@ -245,6 +245,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest
|
||||
.AddWebsite()
|
||||
.AddUmbracoSqlServerSupport()
|
||||
.AddUmbracoSqliteSupport()
|
||||
.AddDeliveryApi()
|
||||
.AddTestServices(TestHelper); // This is the important one!
|
||||
|
||||
CustomTestSetup(builder);
|
||||
@@ -256,7 +257,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest
|
||||
/// </summary>
|
||||
protected virtual void ConfigureTestServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void Configure(IApplicationBuilder app)
|
||||
|
||||
Reference in New Issue
Block a user