Merge remote-tracking branch 'origin/release/17.0'
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<!-- Umbraco packages -->
|
||||
<ItemGroup>
|
||||
@@ -74,7 +74,7 @@
|
||||
<PackageVersion Include="Serilog.Sinks.Map" Version="2.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="9.0.6" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
public class EnumSchemaFilter : ISchemaFilter
|
||||
{
|
||||
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||
public void Apply(IOpenApiSchema model, SchemaFilterContext context)
|
||||
{
|
||||
if (context.Type.IsEnum)
|
||||
if (model is not OpenApiSchema schema || context.Type.IsEnum is false)
|
||||
{
|
||||
model.Type = "string";
|
||||
model.Format = null;
|
||||
model.Enum.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
schema.Type = JsonSchemaType.String;
|
||||
schema.Format = null;
|
||||
schema.Enum = new List<JsonNode>();
|
||||
foreach (var name in Enum.GetNames(context.Type))
|
||||
{
|
||||
var actualName = context.Type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
|
||||
model.Enum.Add(new OpenApiString(actualName));
|
||||
}
|
||||
schema.Enum.Add(actualName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -21,25 +21,32 @@ public class MimeTypeDocumentFilter : IDocumentFilter
|
||||
}
|
||||
|
||||
OpenApiOperation[] operations = swaggerDoc.Paths
|
||||
.SelectMany(path => path.Value.Operations.Values)
|
||||
.SelectMany(path => path.Value.Operations?.Values ?? Enumerable.Empty<OpenApiOperation>())
|
||||
.ToArray();
|
||||
|
||||
void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType> content)
|
||||
void RemoveUnwantedMimeTypes(IDictionary<string, OpenApiMediaType>? content)
|
||||
{
|
||||
if (content.ContainsKey("application/json"))
|
||||
if (content is null || content.ContainsKey("application/json") is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
content.RemoveAll(r => r.Key != "application/json");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray();
|
||||
OpenApiRequestBody[] requestBodies = operations
|
||||
.Select(operation => operation.RequestBody)
|
||||
.OfType<OpenApiRequestBody>()
|
||||
.ToArray();
|
||||
foreach (OpenApiRequestBody requestBody in requestBodies)
|
||||
{
|
||||
RemoveUnwantedMimeTypes(requestBody.Content);
|
||||
}
|
||||
|
||||
OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray();
|
||||
OpenApiResponse[] responses = operations
|
||||
.SelectMany(operation => operation.Responses?.Values ?? Enumerable.Empty<IOpenApiResponse>())
|
||||
.OfType<OpenApiResponse>()
|
||||
.ToArray();
|
||||
foreach (OpenApiResponse response in responses)
|
||||
{
|
||||
RemoveUnwantedMimeTypes(response.Content);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
@@ -20,6 +20,6 @@ public class RemoveSecuritySchemesDocumentFilter : IDocumentFilter
|
||||
return;
|
||||
}
|
||||
|
||||
swaggerDoc.Components.SecuritySchemes.Clear();
|
||||
swaggerDoc.Components?.SecuritySchemes?.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
using Umbraco.Extensions;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Api.Common.OpenApi;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
@@ -35,23 +35,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
return;
|
||||
}
|
||||
|
||||
operation.Security = new List<OpenApiSecurityRequirement>
|
||||
{
|
||||
new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = AuthSchemeName,
|
||||
}
|
||||
},
|
||||
[]
|
||||
}
|
||||
}
|
||||
};
|
||||
var schemaRef = new OpenApiSecuritySchemeReference(AuthSchemeName, context.Document);
|
||||
operation.Security ??= new List<OpenApiSecurityRequirement>();
|
||||
operation.Security.Add(new OpenApiSecurityRequirement { [schemaRef] = [] });
|
||||
}
|
||||
|
||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||
@@ -61,6 +47,8 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
return;
|
||||
}
|
||||
|
||||
swaggerDoc.Components ??= new OpenApiComponents();
|
||||
swaggerDoc.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
|
||||
swaggerDoc.Components.SecuritySchemes.Add(
|
||||
AuthSchemeName,
|
||||
new OpenApiSecurityScheme
|
||||
@@ -74,9 +62,9 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions :
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl = new Uri(Paths.MemberApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
TokenUrl = new Uri(Paths.MemberApi.TokenEndpoint, UriKind.Relative),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
|
||||
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
operation.Parameters ??= new List<OpenApiParameter>();
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
|
||||
AddExpand(operation, context);
|
||||
|
||||
@@ -21,50 +21,50 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptLanguage,
|
||||
Name = Constants.DeliveryApi.HeaderNames.AcceptLanguage,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Defines the language to return. Use this when querying language variant content items.",
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "English culture", new OpenApiExample { Value = new OpenApiString("en-us") } }
|
||||
}
|
||||
{ "Default", new OpenApiExample { Value = string.Empty } },
|
||||
{ "English culture", new OpenApiExample { Value = "en-us" } },
|
||||
},
|
||||
});
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.AcceptSegment,
|
||||
Name = Constants.DeliveryApi.HeaderNames.AcceptSegment,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Defines the segment to return. Use this when querying segment variant content items.",
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Default", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Segment One", new OpenApiExample { Value = new OpenApiString("segment-one") } }
|
||||
}
|
||||
{ "Default", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Segment One", new OpenApiExample { Value = "segment-one" } },
|
||||
},
|
||||
});
|
||||
|
||||
AddApiKey(operation);
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.Preview,
|
||||
Name = Constants.DeliveryApi.HeaderNames.Preview,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "Whether to request draft content.",
|
||||
Schema = new OpenApiSchema { Type = "boolean" }
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.Boolean },
|
||||
});
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.StartItem,
|
||||
Name = Constants.DeliveryApi.HeaderNames.StartItem,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "URL segment or GUID of a root content item.",
|
||||
Schema = new OpenApiSchema { Type = "string" }
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,105 +92,36 @@ internal sealed class SwaggerContentDocumentationFilter : SwaggerDocumentationFi
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Select all", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Select all ancestors of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("ancestors:id") }
|
||||
},
|
||||
{
|
||||
"Select all ancestors of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("ancestors:path") }
|
||||
},
|
||||
{
|
||||
"Select all children of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("children:id") }
|
||||
},
|
||||
{
|
||||
"Select all children of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("children:path") }
|
||||
},
|
||||
{
|
||||
"Select all descendants of a node by id",
|
||||
new OpenApiExample { Value = new OpenApiString("descendants:id") }
|
||||
},
|
||||
{
|
||||
"Select all descendants of a node by path",
|
||||
new OpenApiExample { Value = new OpenApiString("descendants:path") }
|
||||
}
|
||||
{ "Select all", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Select all ancestors of a node by id", new OpenApiExample { Value = "ancestors:id" } },
|
||||
{ "Select all ancestors of a node by path", new OpenApiExample { Value = "ancestors:path" } },
|
||||
{ "Select all children of a node by id", new OpenApiExample { Value = "children:id" } },
|
||||
{ "Select all children of a node by path", new OpenApiExample { Value = "children:path" } },
|
||||
{ "Select all descendants of a node by id", new OpenApiExample { Value = "descendants:id" } },
|
||||
{ "Select all descendants of a node by path", new OpenApiExample { Value = "descendants:path" } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Filter by content type (equals)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("contentType:alias1") } }
|
||||
},
|
||||
{
|
||||
"Filter by name (contains)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
|
||||
},
|
||||
{
|
||||
"Filter by creation date (less than)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("createDate<2024-01-01") } }
|
||||
},
|
||||
{
|
||||
"Filter by update date (greater than or equal)",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("updateDate>:2023-01-01") } }
|
||||
}
|
||||
{ "Default filter", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Filter by content type (equals)", new OpenApiExample { Value = new JsonArray { "contentType:alias1" } } },
|
||||
{ "Filter by name (contains)", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
|
||||
{ "Filter by creation date (less than)", new OpenApiExample { Value = new JsonArray { "createDate<2024-01-01" } } },
|
||||
{ "Filter by update date (greater than or equal)", new OpenApiExample { Value = new JsonArray { "updateDate>:2023-01-01" } } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Sort by create date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by level",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("level:asc"), new OpenApiString("level:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by name",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by sort order",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by update date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "Default sort", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
|
||||
{ "Sort by level", new OpenApiExample { Value = new JsonArray { "level:asc", "level:desc" } } },
|
||||
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
|
||||
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
|
||||
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
@@ -9,6 +9,8 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
: SwaggerFilterBase<TBaseController>, IOperationFilter, IParameterFilter
|
||||
where TBaseController : Controller
|
||||
{
|
||||
protected abstract string DocumentationLink { get; }
|
||||
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (CanApply(context))
|
||||
@@ -17,21 +19,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
|
||||
public void Apply(IOpenApiParameter parameter, ParameterFilterContext context)
|
||||
{
|
||||
if (CanApply(context))
|
||||
if (CanApply(context) && parameter is OpenApiParameter openApiParameter)
|
||||
{
|
||||
ApplyParameter(parameter, context);
|
||||
ApplyParameter(openApiParameter, context);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract string DocumentationLink { get; }
|
||||
|
||||
protected abstract void ApplyOperation(OpenApiOperation operation, OperationFilterContext context);
|
||||
|
||||
protected abstract void ApplyParameter(OpenApiParameter parameter, ParameterFilterContext context);
|
||||
|
||||
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, OpenApiExample> examples, string description)
|
||||
protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary<string, IOpenApiExample> examples, string description)
|
||||
{
|
||||
parameter.Description = QueryParameterDescription(description);
|
||||
parameter.Examples = examples;
|
||||
@@ -60,15 +60,19 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
AddFields(operation);
|
||||
}
|
||||
|
||||
protected void AddApiKey(OpenApiOperation operation) =>
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
protected void AddApiKey(OpenApiOperation operation)
|
||||
{
|
||||
Name = Core.Constants.DeliveryApi.HeaderNames.ApiKey,
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
Name = Constants.DeliveryApi.HeaderNames.ApiKey,
|
||||
In = ParameterLocation.Header,
|
||||
Required = false,
|
||||
Description = "API key specified through configuration to authorize access to the API.",
|
||||
Schema = new OpenApiSchema { Type = "string" }
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
});
|
||||
}
|
||||
|
||||
protected string PaginationDescription(bool skip, string itemType)
|
||||
=> $"Specifies the number of found {itemType} items to {(skip ? "skip" : "take")}. Use this to control pagination of the response.";
|
||||
@@ -82,78 +86,70 @@ internal abstract class SwaggerDocumentationFilterBase<TBaseController>
|
||||
|
||||
// FIXME: remove this when Delivery API V1 has been removed (expectedly in V15)
|
||||
private void AddExpandV1(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Description =
|
||||
QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Expand all", new OpenApiExample { Value = new OpenApiString("all") } },
|
||||
{
|
||||
"Expand specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("property:alias1") }
|
||||
{ "Expand none", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Expand all", new OpenApiExample { Value = "all" } },
|
||||
{ "Expand specific property", new OpenApiExample { Value = "property:alias1" } },
|
||||
{ "Expand specific properties", new OpenApiExample { Value = "property:alias1,alias2" } },
|
||||
},
|
||||
{
|
||||
"Expand specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("property:alias1,alias2") }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void AddExpand(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
Name = "expand",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Description =
|
||||
QueryParameterDescription("Defines the properties that should be expanded in the response"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{ "Expand all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
|
||||
{
|
||||
"Expand specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
|
||||
{ "Expand none", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Expand all properties", new OpenApiExample { Value = "properties[$all]" } },
|
||||
{ "Expand specific property", new OpenApiExample { Value = "properties[alias1]" } },
|
||||
{ "Expand specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
|
||||
{ "Expand nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
|
||||
},
|
||||
{
|
||||
"Expand specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
|
||||
},
|
||||
{
|
||||
"Expand nested properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void AddFields(OpenApiOperation operation)
|
||||
=> operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
operation.Parameters.Add(
|
||||
new OpenApiParameter
|
||||
{
|
||||
Name = "fields",
|
||||
In = ParameterLocation.Query,
|
||||
Required = false,
|
||||
Description = QueryParameterDescription("Explicitly defines which properties should be included in the response (by default all properties are included)"),
|
||||
Schema = new OpenApiSchema { Type = "string" },
|
||||
Examples = new Dictionary<string, OpenApiExample>
|
||||
Description =
|
||||
QueryParameterDescription(
|
||||
"Explicitly defines which properties should be included in the response (by default all properties are included)"),
|
||||
Schema = new OpenApiSchema { Type = JsonSchemaType.String },
|
||||
Examples = new Dictionary<string, IOpenApiExample>
|
||||
{
|
||||
{ "Include all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } },
|
||||
{
|
||||
"Include only specific property",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1]") }
|
||||
{ "Include all properties", new OpenApiExample { Value = "properties[$all]" } },
|
||||
{ "Include only specific property", new OpenApiExample { Value = "properties[alias1]" } },
|
||||
{ "Include only specific properties", new OpenApiExample { Value = "properties[alias1,alias2]" } },
|
||||
{ "Include only specific nested properties", new OpenApiExample { Value = "properties[alias1[properties[nestedAlias1,nestedAlias2]]]" } },
|
||||
},
|
||||
{
|
||||
"Include only specific properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1,alias2]") }
|
||||
},
|
||||
{
|
||||
"Include only specific nested properties",
|
||||
new OpenApiExample { Value = new OpenApiString("properties[alias1[properties[nestedAlias1,nestedAlias2]]]") }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers;
|
||||
using Umbraco.Cms.Api.Delivery.Controllers.Media;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
@@ -13,7 +12,7 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
|
||||
|
||||
protected override void ApplyOperation(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
operation.Parameters ??= new List<OpenApiParameter>();
|
||||
operation.Parameters ??= new List<IOpenApiParameter>();
|
||||
|
||||
AddExpand(operation, context);
|
||||
|
||||
@@ -46,77 +45,29 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, OpenApiExample> FetchQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FetchQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{
|
||||
"Select all children at root level",
|
||||
new OpenApiExample { Value = new OpenApiString("children:/") }
|
||||
},
|
||||
{
|
||||
"Select all children of a media item by id",
|
||||
new OpenApiExample { Value = new OpenApiString("children:id") }
|
||||
},
|
||||
{
|
||||
"Select all children of a media item by path",
|
||||
new OpenApiExample { Value = new OpenApiString("children:path") }
|
||||
}
|
||||
{ "Select all children at root level", new OpenApiExample { Value = "children:/" } },
|
||||
{ "Select all children of a media item by id", new OpenApiExample { Value = "children:id" } },
|
||||
{ "Select all children of a media item by path", new OpenApiExample { Value = "children:path" } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> FilterQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> FilterQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default filter", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Filter by media type",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("mediaType:alias1") } }
|
||||
},
|
||||
{
|
||||
"Filter by name",
|
||||
new OpenApiExample { Value = new OpenApiArray { new OpenApiString("name:nodeName") } }
|
||||
}
|
||||
{ "Default filter", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Filter by media type", new OpenApiExample { Value = new JsonArray { "mediaType:alias1" } } },
|
||||
{ "Filter by name", new OpenApiExample { Value = new JsonArray { "name:nodeName" } } },
|
||||
};
|
||||
|
||||
private Dictionary<string, OpenApiExample> SortQueryParameterExamples() =>
|
||||
private Dictionary<string, IOpenApiExample> SortQueryParameterExamples() =>
|
||||
new()
|
||||
{
|
||||
{ "Default sort", new OpenApiExample { Value = new OpenApiString(string.Empty) } },
|
||||
{
|
||||
"Sort by create date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("createDate:asc"), new OpenApiString("createDate:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by name",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray { new OpenApiString("name:asc"), new OpenApiString("name:desc") }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by sort order",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("sortOrder:asc"), new OpenApiString("sortOrder:desc")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sort by update date",
|
||||
new OpenApiExample
|
||||
{
|
||||
Value = new OpenApiArray
|
||||
{
|
||||
new OpenApiString("updateDate:asc"), new OpenApiString("updateDate:desc")
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "Default sort", new OpenApiExample { Value = string.Empty } },
|
||||
{ "Sort by create date", new OpenApiExample { Value = new JsonArray { "createDate:asc", "createDate:desc" } } },
|
||||
{ "Sort by name", new OpenApiExample { Value = new JsonArray { "name:asc", "name:desc" } } },
|
||||
{ "Sort by sort order", new OpenApiExample { Value = new JsonArray { "sortOrder:asc", "sortOrder:desc" } } },
|
||||
{ "Sort by update date", new OpenApiExample { Value = new JsonArray { "updateDate:asc", "updateDate:desc" } } },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Common.OpenApi;
|
||||
using Umbraco.Cms.Api.Common.Security;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.OpenApi;
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Cms.Api.Management.Configuration;
|
||||
|
||||
public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
private IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver;
|
||||
|
||||
public ConfigureUmbracoManagementApiSwaggerGenOptions(IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver)
|
||||
{
|
||||
@@ -20,7 +20,6 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
|
||||
|
||||
public void Configure(SwaggerGenOptions swaggerGenOptions)
|
||||
{
|
||||
|
||||
swaggerGenOptions.SwaggerDoc(
|
||||
ManagementApiConfiguration.ApiName,
|
||||
new OpenApiInfo
|
||||
@@ -51,10 +50,10 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
|
||||
AuthorizationCode = new OpenApiOAuthFlow
|
||||
{
|
||||
AuthorizationUrl =
|
||||
new Uri(Common.Security.Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Common.Security.Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative)
|
||||
}
|
||||
}
|
||||
new Uri(Paths.BackOfficeApi.AuthorizationEndpoint, UriKind.Relative),
|
||||
TokenUrl = new Uri(Paths.BackOfficeApi.TokenEndpoint, UriKind.Relative),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Sets Security requirement on backoffice apis
|
||||
|
||||
@@ -38521,9 +38521,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/UnknownTypePermissionPresentationModel"
|
||||
}
|
||||
@@ -38810,9 +38807,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/UnknownTypePermissionPresentationModel"
|
||||
}
|
||||
@@ -40211,36 +40205,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTypePermissionPresentationModel": {
|
||||
"required": [
|
||||
"$type",
|
||||
"documentTypeAlias",
|
||||
"verbs"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$type": {
|
||||
"type": "string"
|
||||
},
|
||||
"verbs": {
|
||||
"uniqueItems": true,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"documentTypeAlias": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"discriminator": {
|
||||
"propertyName": "$type",
|
||||
"mapping": {
|
||||
"DocumentTypePermissionPresentationModel": "#/components/schemas/DocumentTypePermissionPresentationModel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DocumentTypePropertyTypeContainerResponseModel": {
|
||||
"required": [
|
||||
"id",
|
||||
@@ -49034,9 +48998,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/UnknownTypePermissionPresentationModel"
|
||||
}
|
||||
@@ -49474,9 +49435,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePermissionPresentationModel"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/UnknownTypePermissionPresentationModel"
|
||||
}
|
||||
@@ -50157,5 +50115,160 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Culture"
|
||||
},
|
||||
{
|
||||
"name": "Data Type"
|
||||
},
|
||||
{
|
||||
"name": "Dictionary"
|
||||
},
|
||||
{
|
||||
"name": "Document Blueprint"
|
||||
},
|
||||
{
|
||||
"name": "Document Type"
|
||||
},
|
||||
{
|
||||
"name": "Document Version"
|
||||
},
|
||||
{
|
||||
"name": "Document"
|
||||
},
|
||||
{
|
||||
"name": "Dynamic Root"
|
||||
},
|
||||
{
|
||||
"name": "Health Check"
|
||||
},
|
||||
{
|
||||
"name": "Help"
|
||||
},
|
||||
{
|
||||
"name": "Imaging"
|
||||
},
|
||||
{
|
||||
"name": "Import"
|
||||
},
|
||||
{
|
||||
"name": "Indexer"
|
||||
},
|
||||
{
|
||||
"name": "Install"
|
||||
},
|
||||
{
|
||||
"name": "Language"
|
||||
},
|
||||
{
|
||||
"name": "Log Viewer"
|
||||
},
|
||||
{
|
||||
"name": "Manifest"
|
||||
},
|
||||
{
|
||||
"name": "Media Type"
|
||||
},
|
||||
{
|
||||
"name": "Media"
|
||||
},
|
||||
{
|
||||
"name": "Member Group"
|
||||
},
|
||||
{
|
||||
"name": "Member Type"
|
||||
},
|
||||
{
|
||||
"name": "Member"
|
||||
},
|
||||
{
|
||||
"name": "Models Builder"
|
||||
},
|
||||
{
|
||||
"name": "News Dashboard"
|
||||
},
|
||||
{
|
||||
"name": "Object Types"
|
||||
},
|
||||
{
|
||||
"name": "oEmbed"
|
||||
},
|
||||
{
|
||||
"name": "Package"
|
||||
},
|
||||
{
|
||||
"name": "Partial View"
|
||||
},
|
||||
{
|
||||
"name": "Preview"
|
||||
},
|
||||
{
|
||||
"name": "Profiling"
|
||||
},
|
||||
{
|
||||
"name": "Property Type"
|
||||
},
|
||||
{
|
||||
"name": "Published Cache"
|
||||
},
|
||||
{
|
||||
"name": "Redirect Management"
|
||||
},
|
||||
{
|
||||
"name": "Relation Type"
|
||||
},
|
||||
{
|
||||
"name": "Relation"
|
||||
},
|
||||
{
|
||||
"name": "Script"
|
||||
},
|
||||
{
|
||||
"name": "Searcher"
|
||||
},
|
||||
{
|
||||
"name": "Security"
|
||||
},
|
||||
{
|
||||
"name": "Segment"
|
||||
},
|
||||
{
|
||||
"name": "Server"
|
||||
},
|
||||
{
|
||||
"name": "Static File"
|
||||
},
|
||||
{
|
||||
"name": "Stylesheet"
|
||||
},
|
||||
{
|
||||
"name": "Tag"
|
||||
},
|
||||
{
|
||||
"name": "Telemetry"
|
||||
},
|
||||
{
|
||||
"name": "Template"
|
||||
},
|
||||
{
|
||||
"name": "Temporary File"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade"
|
||||
},
|
||||
{
|
||||
"name": "User Data"
|
||||
},
|
||||
{
|
||||
"name": "User Group"
|
||||
},
|
||||
{
|
||||
"name": "User"
|
||||
},
|
||||
{
|
||||
"name": "Webhook"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
@@ -21,27 +21,17 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera
|
||||
if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) &&
|
||||
!(context.MethodInfo.DeclaringType?.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) ?? false))
|
||||
{
|
||||
operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new OpenApiResponse
|
||||
operation.Responses ??= new OpenApiResponses();
|
||||
operation.Responses.Add(
|
||||
StatusCodes.Status401Unauthorized.ToString(),
|
||||
new OpenApiResponse
|
||||
{
|
||||
Description = "The resource is protected and requires an authentication token"
|
||||
Description = "The resource is protected and requires an authentication token",
|
||||
});
|
||||
|
||||
operation.Security = new List<OpenApiSecurityRequirement>
|
||||
{
|
||||
new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = ManagementApiConfiguration.ApiSecurityName
|
||||
}
|
||||
}, []
|
||||
}
|
||||
}
|
||||
};
|
||||
var schemaRef = new OpenApiSecuritySchemeReference(ManagementApiConfiguration.ApiSecurityName, context.Document);
|
||||
operation.Security ??= new List<OpenApiSecurityRequirement>();
|
||||
operation.Security.Add(new OpenApiSecurityRequirement { [schemaRef] = [] });
|
||||
}
|
||||
|
||||
// Assuming if and endpoint have more then one AuthorizeAttribute, there is a risk the user do not have access while still being authorized.
|
||||
@@ -57,9 +47,12 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera
|
||||
|
||||
if (numberOfAuthorizeAttributes > 2 || hasConstructorInjectingIAuthorizationService)
|
||||
{
|
||||
operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse()
|
||||
operation.Responses ??= new OpenApiResponses();
|
||||
operation.Responses.Add(
|
||||
StatusCodes.Status403Forbidden.ToString(),
|
||||
new OpenApiResponse
|
||||
{
|
||||
Description = "The authenticated user does not have access to this resource"
|
||||
Description = "The authenticated user does not have access to this resource",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -24,27 +24,24 @@ internal sealed class NotificationHeaderFilter : IOperationFilter
|
||||
|
||||
// filter out irrelevant responses (401 will never produce notifications)
|
||||
IEnumerable<OpenApiResponse> relevantResponses = operation
|
||||
.Responses
|
||||
.Responses?
|
||||
.Where(pair => pair.Key != StatusCodes.Status401Unauthorized.ToString())
|
||||
.Select(pair => pair.Value);
|
||||
.Select(pair => pair.Value)
|
||||
.OfType<OpenApiResponse>()
|
||||
?? Enumerable.Empty<OpenApiResponse>();
|
||||
foreach (OpenApiResponse response in relevantResponses)
|
||||
{
|
||||
response.Headers.TryAdd(Constants.Headers.Notifications, new OpenApiHeader
|
||||
response.Headers ??= new Dictionary<string, IOpenApiHeader>();
|
||||
response.Headers.TryAdd(
|
||||
Constants.Headers.Notifications,
|
||||
new OpenApiHeader
|
||||
{
|
||||
Description = "The list of notifications produced during the request.",
|
||||
Schema = new OpenApiSchema
|
||||
{
|
||||
Type = "array",
|
||||
Nullable = true,
|
||||
Items = new OpenApiSchema()
|
||||
{
|
||||
Reference = new OpenApiReference()
|
||||
{
|
||||
Type = ReferenceType.Schema,
|
||||
Id = notificationModelType.Name
|
||||
Type = JsonSchemaType.Array | JsonSchemaType.Null,
|
||||
Items = new OpenApiSchemaReference(notificationModelType.Name),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -11,36 +11,36 @@ internal sealed class ResponseHeaderOperationFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (context.MethodInfo.HasMapToApiAttribute(ManagementApiConfiguration.ApiName) == false)
|
||||
if (context.MethodInfo.HasMapToApiAttribute(ManagementApiConfiguration.ApiName) is false || operation.Responses is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ((var key, OpenApiResponse? value) in operation.Responses)
|
||||
foreach ((var key, IOpenApiResponse value) in operation.Responses)
|
||||
{
|
||||
if (value is not OpenApiResponse openApiResponse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (int.Parse(key))
|
||||
{
|
||||
case StatusCodes.Status201Created:
|
||||
// NOTE: The header order matters to the back-office client. Do not change.
|
||||
SetHeader(value, Constants.Headers.GeneratedResource, "Identifier of the newly created resource", "string");
|
||||
SetHeader(value, Constants.Headers.Location, "Location of the newly created resource", "string", "uri");
|
||||
SetHeader(openApiResponse, Constants.Headers.GeneratedResource, "Identifier of the newly created resource", JsonSchemaType.String);
|
||||
SetHeader(openApiResponse, Constants.Headers.Location, "Location of the newly created resource", JsonSchemaType.String, "uri");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetHeader(OpenApiResponse value, string headerName, string description, string type, string? format = null)
|
||||
private static void SetHeader(OpenApiResponse value, string headerName, string description, JsonSchemaType type, string? format = null)
|
||||
{
|
||||
|
||||
if (value.Headers is null)
|
||||
{
|
||||
value.Headers = new Dictionary<string, OpenApiHeader>();
|
||||
}
|
||||
|
||||
value.Headers[headerName] = new OpenApiHeader()
|
||||
value.Headers ??= new Dictionary<string, IOpenApiHeader>();
|
||||
value.Headers[headerName] = new OpenApiHeader
|
||||
{
|
||||
Description = description,
|
||||
Schema = new OpenApiSchema { Description = description, Type = type, Format = format }
|
||||
Schema = new OpenApiSchema { Description = description, Type = type, Format = format },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.OpenApi;
|
||||
@@ -8,14 +8,21 @@ public class RequireNonNullablePropertiesSchemaFilter : ISchemaFilter
|
||||
/// <summary>
|
||||
/// Add to model.Required all properties where Nullable is false.
|
||||
/// </summary>
|
||||
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||
public void Apply(IOpenApiSchema model, SchemaFilterContext context)
|
||||
{
|
||||
var additionalRequiredProps = model.Properties
|
||||
.Where(x => !x.Value.Nullable && !model.Required.Contains(x.Key))
|
||||
.Select(x => x.Key);
|
||||
if (model is not OpenApiSchema schema)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<string> additionalRequiredProps = schema.Properties
|
||||
?.Where(x => x.Value.Type?.HasFlag(JsonSchemaType.Null) is not true && model.Required?.Contains(x.Key) is not true)
|
||||
.Select(x => x.Key)
|
||||
?? [];
|
||||
schema.Required ??= new SortedSet<string>();
|
||||
foreach (var propKey in additionalRequiredProps)
|
||||
{
|
||||
model.Required.Add(propKey);
|
||||
schema.Required.Add(propKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107")
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107")
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
|
||||
{
|
||||
|
||||
16
src/Umbraco.Web.UI.Client/package-lock.json
generated
16
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -1334,9 +1334,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/signalr": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-9.0.6.tgz",
|
||||
"integrity": "sha512-DrhgzFWI9JE4RPTsHYRxh4yr+OhnwKz8bnJe7eIi7mLLjqhJpEb62CiUy/YbFvLqLzcGzlzz1QWgVAW0zyipMQ==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-10.0.0.tgz",
|
||||
"integrity": "sha512-0BRqz/uCx3JdrOqiqgFhih/+hfTERaUfCZXFB52uMaZJrKaPRzHzMuqVsJC/V3pt7NozcNXGspjKiQEK+X7P2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
@@ -10617,9 +10617,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz",
|
||||
"integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==",
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz",
|
||||
"integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
@@ -16965,7 +16965,7 @@
|
||||
"src/external/marked": {
|
||||
"name": "@umbraco-backoffice/marked",
|
||||
"dependencies": {
|
||||
"marked": "^16.3.0"
|
||||
"marked": "^17.0.1"
|
||||
}
|
||||
},
|
||||
"src/external/monaco-editor": {
|
||||
@@ -16986,7 +16986,7 @@
|
||||
"src/external/signalr": {
|
||||
"name": "@umbraco-backoffice/signalr",
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "9.0.6"
|
||||
"@microsoft/signalr": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"src/external/uui": {
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^16.3.0"
|
||||
"marked": "^17.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "9.0.6"
|
||||
"@microsoft/signalr": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ export type CreateUserGroupRequestModel = {
|
||||
mediaStartNode?: ReferenceByIdModel | null;
|
||||
mediaRootAccess: boolean;
|
||||
fallbackPermissions: Array<string>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | DocumentTypePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
id?: string | null;
|
||||
};
|
||||
|
||||
@@ -476,7 +476,7 @@ export type CurrentUserResponseModel = {
|
||||
hasAccessToAllLanguages: boolean;
|
||||
hasAccessToSensitiveData: boolean;
|
||||
fallbackPermissions: Array<string>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | DocumentTypePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
allowedSections: Array<string>;
|
||||
isAdmin: boolean;
|
||||
};
|
||||
@@ -778,12 +778,6 @@ export type DocumentTypeItemResponseModel = {
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type DocumentTypePermissionPresentationModel = {
|
||||
$type: string;
|
||||
verbs: Array<string>;
|
||||
documentTypeAlias: string;
|
||||
};
|
||||
|
||||
export type DocumentTypePropertyTypeContainerResponseModel = {
|
||||
id: string;
|
||||
parent?: ReferenceByIdModel | null;
|
||||
@@ -2826,7 +2820,7 @@ export type UpdateUserGroupRequestModel = {
|
||||
mediaStartNode?: ReferenceByIdModel | null;
|
||||
mediaRootAccess: boolean;
|
||||
fallbackPermissions: Array<string>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | DocumentTypePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
};
|
||||
|
||||
export type UpdateUserGroupsOnUserRequestModel = {
|
||||
@@ -2927,7 +2921,7 @@ export type UserGroupResponseModel = {
|
||||
mediaStartNode?: ReferenceByIdModel | null;
|
||||
mediaRootAccess: boolean;
|
||||
fallbackPermissions: Array<string>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | DocumentTypePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
permissions: Array<DocumentPermissionPresentationModel | DocumentPropertyValuePermissionPresentationModel | UnknownTypePermissionPresentationModel>;
|
||||
id: string;
|
||||
isDeletable: boolean;
|
||||
aliasCanBeChanged: boolean;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Add design/build time support for EF Core migrations -->
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" Version="10.0.0" />
|
||||
<!--
|
||||
Added the following direct dependency due to Microsoft.EntityFrameworkCore.Design having a dependency on an insecure version.
|
||||
Review for removal when Microsoft.EntityFrameworkCore.Design is updated to a newer version.
|
||||
|
||||
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<ItemGroup>
|
||||
<!-- Microsoft packages -->
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.15.4" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||
<PackageVersion Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
<PackageVersion Include="System.Data.Odbc" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageVersion Include="System.Data.Odbc" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1596,7 +1596,15 @@ internal sealed class OpenApiContractTest : UmbracoTestServerTestBase
|
||||
"additionalProperties": { }
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Content"
|
||||
},
|
||||
{
|
||||
"name": "Media"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user