Merge remote-tracking branch 'origin/release/17.0'

This commit is contained in:
Jacob Overgaard
2025-11-24 16:32:38 +01:00
29 changed files with 436 additions and 451 deletions

View File

@@ -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>

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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),
},
},
});
}
}

View File

@@ -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" } } },
};
}

View File

@@ -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]]]") }
}
}
});
}
}

View File

@@ -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" } } },
};
}

View File

@@ -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

View File

@@ -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"
}
]
}

View File

@@ -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",
});
}
}

View File

@@ -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),
},
}
}
});
}
}

View File

@@ -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 },
};
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 =>
{

View File

@@ -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 =>
{

View File

@@ -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": {

View File

@@ -6,6 +6,6 @@
"build": "vite build"
},
"dependencies": {
"marked": "^16.3.0"
"marked": "^17.0.1"
}
}

View File

@@ -6,6 +6,6 @@
"build": "vite build"
},
"dependencies": {
"@microsoft/signalr": "9.0.6"
"@microsoft/signalr": "^10.0.0"
}
}

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;

View File

@@ -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>

View File

@@ -1596,7 +1596,15 @@ internal sealed class OpenApiContractTest : UmbracoTestServerTestBase
"additionalProperties": { }
}
}
},
"tags": [
{
"name": "Content"
},
{
"name": "Media"
}
]
}
""";
}