diff --git a/Directory.Packages.props b/Directory.Packages.props index 416a91cec3..2c9fa306e4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + @@ -74,7 +74,7 @@ - + diff --git a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs index a4cabf51bb..4b65bb12bf 100644 --- a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs @@ -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; diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/EnumSchemaFilter.cs b/src/Umbraco.Cms.Api.Common/OpenApi/EnumSchemaFilter.cs index e2ac5ab870..845adb4990 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/EnumSchemaFilter.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/EnumSchemaFilter.cs @@ -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(); - foreach (var name in Enum.GetNames(context.Type)) - { - var actualName = context.Type.GetField(name)?.GetCustomAttribute()?.Value ?? name; - model.Enum.Add(new OpenApiString(actualName)); - } + return; + } + + schema.Type = JsonSchemaType.String; + schema.Format = null; + schema.Enum = new List(); + foreach (var name in Enum.GetNames(context.Type)) + { + var actualName = context.Type.GetField(name)?.GetCustomAttribute()?.Value ?? name; + schema.Enum.Add(actualName); } } } diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/MimeTypeDocumentFilter.cs b/src/Umbraco.Cms.Api.Common/OpenApi/MimeTypeDocumentFilter.cs index a756c30f1f..2dfc6a88a4 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/MimeTypeDocumentFilter.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/MimeTypeDocumentFilter.cs @@ -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()) .ToArray(); - void RemoveUnwantedMimeTypes(IDictionary content) + void RemoveUnwantedMimeTypes(IDictionary? content) { - if (content.ContainsKey("application/json")) + if (content is null || content.ContainsKey("application/json") is false) { - content.RemoveAll(r => r.Key != "application/json"); + 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() + .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()) + .OfType() + .ToArray(); foreach (OpenApiResponse response in responses) { RemoveUnwantedMimeTypes(response.Content); diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/RemoveSecuritySchemesDocumentFilter.cs b/src/Umbraco.Cms.Api.Common/OpenApi/RemoveSecuritySchemesDocumentFilter.cs index 8ab2041f7d..c90c9f9c73 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/RemoveSecuritySchemesDocumentFilter.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/RemoveSecuritySchemesDocumentFilter.cs @@ -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(); } } diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs b/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs index dba0e6f56d..2c45a36754 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs @@ -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; diff --git a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoDeliveryApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoDeliveryApiSwaggerGenOptions.cs index 9b87166300..8c7437e4bf 100644 --- a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoDeliveryApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoDeliveryApiSwaggerGenOptions.cs @@ -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; diff --git a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs index 3161105dca..5922768462 100644 --- a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs @@ -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 - { - new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = AuthSchemeName, - } - }, - [] - } - } - }; + var schemaRef = new OpenApiSecuritySchemeReference(AuthSchemeName, context.Document); + operation.Security ??= new List(); + 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(); 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), + }, + }, }); } } diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs index 014aed28c8..290f6b66dc 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs @@ -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(); + operation.Parameters ??= new List(); 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 + Schema = new OpenApiSchema { Type = JsonSchemaType.String }, + Examples = new Dictionary { - { "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 + Schema = new OpenApiSchema { Type = JsonSchemaType.String }, + Examples = new Dictionary { - { "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 FetchQueryParameterExamples() => + private Dictionary 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 FilterQueryParameterExamples() => + private Dictionary 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 SortQueryParameterExamples() => + private Dictionary 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" } } }, }; } diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs index 8a450a2f9b..2776f6a39e 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerDocumentationFilterBase.cs @@ -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 : SwaggerFilterBase, 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 } } - 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 examples, string description) + protected void AddQueryParameterDocumentation(OpenApiParameter parameter, Dictionary examples, string description) { parameter.Description = QueryParameterDescription(description); parameter.Examples = examples; @@ -60,15 +60,19 @@ internal abstract class SwaggerDocumentationFilterBase AddFields(operation); } - protected void AddApiKey(OpenApiOperation operation) => - operation.Parameters.Add(new OpenApiParameter - { - Name = Core.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" } - }); + protected void AddApiKey(OpenApiOperation operation) + { + operation.Parameters ??= new List(); + 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 = 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 // FIXME: remove this when Delivery API V1 has been removed (expectedly in V15) private void AddExpandV1(OpenApiOperation operation) - => 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 + { + operation.Parameters ??= new List(); + operation.Parameters.Add( + new OpenApiParameter { - { "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } }, - { "Expand all", new OpenApiExample { Value = new OpenApiString("all") } }, + Name = "expand", + In = ParameterLocation.Query, + Required = false, + Description = + QueryParameterDescription("Defines the properties that should be expanded in the response"), + Schema = new OpenApiSchema { Type = JsonSchemaType.String }, + Examples = new Dictionary { - "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 - { - 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 + { + operation.Parameters ??= new List(); + operation.Parameters.Add( + new OpenApiParameter { - { "Expand none", new OpenApiExample { Value = new OpenApiString(string.Empty) } }, - { "Expand all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } }, + Name = "expand", + In = ParameterLocation.Query, + Required = false, + Description = + QueryParameterDescription("Defines the properties that should be expanded in the response"), + Schema = new OpenApiSchema { Type = JsonSchemaType.String }, + Examples = new Dictionary { - "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 - { - 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 + { + operation.Parameters ??= new List(); + operation.Parameters.Add( + new OpenApiParameter { - { "Include all properties", new OpenApiExample { Value = new OpenApiString("properties[$all]") } }, + 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 = JsonSchemaType.String }, + Examples = new Dictionary { - "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]]]") } - } - } - }); + }); + } } diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs index 85ba66e648..a8da861325 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs @@ -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(); + operation.Parameters ??= new List(); AddExpand(operation, context); @@ -46,77 +45,29 @@ internal sealed class SwaggerMediaDocumentationFilter : SwaggerDocumentationFilt } } - private Dictionary FetchQueryParameterExamples() => + private Dictionary 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 FilterQueryParameterExamples() => + private Dictionary 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 SortQueryParameterExamples() => + private Dictionary 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" } } }, }; } diff --git a/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs index 74862e3bab..98ed51f42d 100644 --- a/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs @@ -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 { - 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 diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 86ed089da5..4c495413c6 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs index e2ff1e609a..bcc069f90b 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs @@ -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 - { - Description = "The resource is protected and requires an authentication token" - }); - - operation.Security = new List - { - new OpenApiSecurityRequirement + operation.Responses ??= new OpenApiResponses(); + operation.Responses.Add( + StatusCodes.Status401Unauthorized.ToString(), + new OpenApiResponse { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = ManagementApiConfiguration.ApiSecurityName - } - }, [] - } - } - }; + Description = "The resource is protected and requires an authentication token", + }); + + var schemaRef = new OpenApiSecuritySchemeReference(ManagementApiConfiguration.ApiSecurityName, context.Document); + operation.Security ??= new List(); + 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,10 +47,13 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera if (numberOfAuthorizeAttributes > 2 || hasConstructorInjectingIAuthorizationService) { - operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse() - { - Description = "The authenticated user does not have access to this resource" - }); + operation.Responses ??= new OpenApiResponses(); + operation.Responses.Add( + StatusCodes.Status403Forbidden.ToString(), + new OpenApiResponse + { + Description = "The authenticated user does not have access to this resource", + }); } } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/NotificationHeaderFilter.cs b/src/Umbraco.Cms.Api.Management/OpenApi/NotificationHeaderFilter.cs index f11c350aae..5872c36b45 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/NotificationHeaderFilter.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/NotificationHeaderFilter.cs @@ -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,28 +24,25 @@ internal sealed class NotificationHeaderFilter : IOperationFilter // filter out irrelevant responses (401 will never produce notifications) IEnumerable relevantResponses = operation - .Responses + .Responses? .Where(pair => pair.Key != StatusCodes.Status401Unauthorized.ToString()) - .Select(pair => pair.Value); + .Select(pair => pair.Value) + .OfType() + ?? Enumerable.Empty(); foreach (OpenApiResponse response in relevantResponses) { - response.Headers.TryAdd(Constants.Headers.Notifications, new OpenApiHeader - { - Description = "The list of notifications produced during the request.", - Schema = new OpenApiSchema + response.Headers ??= new Dictionary(); + response.Headers.TryAdd( + Constants.Headers.Notifications, + new OpenApiHeader { - Type = "array", - Nullable = true, - Items = new OpenApiSchema() + Description = "The list of notifications produced during the request.", + Schema = new OpenApiSchema { - Reference = new OpenApiReference() - { - Type = ReferenceType.Schema, - Id = notificationModelType.Name - }, - } - } - }); + Type = JsonSchemaType.Array | JsonSchemaType.Null, + Items = new OpenApiSchemaReference(notificationModelType.Name), + }, + }); } } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/ReponseHeaderOperationFilter.cs b/src/Umbraco.Cms.Api.Management/OpenApi/ReponseHeaderOperationFilter.cs index e639a64091..4bbc032398 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/ReponseHeaderOperationFilter.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/ReponseHeaderOperationFilter.cs @@ -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(); - } - - value.Headers[headerName] = new OpenApiHeader() + value.Headers ??= new Dictionary(); + 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 }, }; } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/RequireNonNullablePropertiesSchemaFilter.cs b/src/Umbraco.Cms.Api.Management/OpenApi/RequireNonNullablePropertiesSchemaFilter.cs index 0d06da8bf6..5c957725d3 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/RequireNonNullablePropertiesSchemaFilter.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/RequireNonNullablePropertiesSchemaFilter.cs @@ -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 /// /// Add to model.Required all properties where Nullable is false. /// - 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 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(); foreach (var propKey in additionalRequiredProps) { - model.Required.Add(propKey); + schema.Required.Add(propKey); } } } diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20251006140751_UpdateOpenIddictToV7.Designer.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20251006140751_UpdateOpenIddictToV7.Designer.cs index 4883257986..430ff75841 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20251006140751_UpdateOpenIddictToV7.Designer.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20251006140751_UpdateOpenIddictToV7.Designer.cs @@ -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); diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs index 66c495fe60..3ab5eabcb6 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// 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); diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20251006140958_UpdateOpenIddictToV7.Designer.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20251006140958_UpdateOpenIddictToV7.Designer.cs index 5f44ea2dae..d77d3a0fb6 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20251006140958_UpdateOpenIddictToV7.Designer.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20251006140958_UpdateOpenIddictToV7.Designer.cs @@ -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 => { diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs index 7c3cf00576..392310d26a 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// 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 => { diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 5044853ffa..852218182e 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -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": { diff --git a/src/Umbraco.Web.UI.Client/src/external/marked/package.json b/src/Umbraco.Web.UI.Client/src/external/marked/package.json index aba2839f75..30e912cca4 100644 --- a/src/Umbraco.Web.UI.Client/src/external/marked/package.json +++ b/src/Umbraco.Web.UI.Client/src/external/marked/package.json @@ -6,6 +6,6 @@ "build": "vite build" }, "dependencies": { - "marked": "^16.3.0" + "marked": "^17.0.1" } } diff --git a/src/Umbraco.Web.UI.Client/src/external/signalr/package.json b/src/Umbraco.Web.UI.Client/src/external/signalr/package.json index 08e7abbc27..6bbc565225 100644 --- a/src/Umbraco.Web.UI.Client/src/external/signalr/package.json +++ b/src/Umbraco.Web.UI.Client/src/external/signalr/package.json @@ -6,6 +6,6 @@ "build": "vite build" }, "dependencies": { - "@microsoft/signalr": "9.0.6" + "@microsoft/signalr": "^10.0.0" } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts index 9032ace017..07582d290f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts @@ -417,7 +417,7 @@ export type CreateUserGroupRequestModel = { mediaStartNode?: ReferenceByIdModel | null; mediaRootAccess: boolean; fallbackPermissions: Array; - permissions: Array; + permissions: Array; id?: string | null; }; @@ -476,7 +476,7 @@ export type CurrentUserResponseModel = { hasAccessToAllLanguages: boolean; hasAccessToSensitiveData: boolean; fallbackPermissions: Array; - permissions: Array; + permissions: Array; allowedSections: Array; isAdmin: boolean; }; @@ -778,12 +778,6 @@ export type DocumentTypeItemResponseModel = { description?: string | null; }; -export type DocumentTypePermissionPresentationModel = { - $type: string; - verbs: Array; - documentTypeAlias: string; -}; - export type DocumentTypePropertyTypeContainerResponseModel = { id: string; parent?: ReferenceByIdModel | null; @@ -2826,7 +2820,7 @@ export type UpdateUserGroupRequestModel = { mediaStartNode?: ReferenceByIdModel | null; mediaRootAccess: boolean; fallbackPermissions: Array; - permissions: Array; + permissions: Array; }; export type UpdateUserGroupsOnUserRequestModel = { @@ -2927,7 +2921,7 @@ export type UserGroupResponseModel = { mediaStartNode?: ReferenceByIdModel | null; mediaRootAccess: boolean; fallbackPermissions: Array; - permissions: Array; + permissions: Array; id: string; isDeletable: boolean; aliasCanBeChanged: boolean; diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a9c363c8a0..287d607548 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -25,7 +25,7 @@ - + - - + + - - + + @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/OpenApiContractTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/OpenApiContractTest.cs index 3514f32d2b..6d74f5ff5e 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/OpenApiContractTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/OpenApiContractTest.cs @@ -1596,7 +1596,15 @@ internal sealed class OpenApiContractTest : UmbracoTestServerTestBase "additionalProperties": { } } } - } + }, + "tags": [ + { + "name": "Content" + }, + { + "name": "Media" + } + ] } """; }