From fcda25af5083999a3d7edd7163786ebe4c216326 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 10 Apr 2024 12:30:30 +0200 Subject: [PATCH] Premigrations + Updated NuGet Dependencies (#15987) * Updated nuget packages + added migrations for OpenIddict - Currently can only be executed using unatttended installs * Added new Premigration concept - Migrations that always runs unattended before other migrations * Apply suggestions from code review Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --------- Co-authored-by: Zeegaan Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- Directory.Packages.props | 38 +-- .../Security/MemberApplicationManager.cs | 2 +- .../Security/BackOfficeApplicationManager.cs | 6 +- ...403140654_UpdateOpenIddictToV5.Designer.cs | 277 ++++++++++++++++++ .../20240403140654_UpdateOpenIddictToV5.cs | 59 ++++ .../UmbracoDbContextModelSnapshot.cs | 18 +- .../SqlServerMigrationProvider.cs | 1 + ...403141051_UpdateOpenIddictToV5.Designer.cs | 269 +++++++++++++++++ .../20240403141051_UpdateOpenIddictToV5.cs | 59 ++++ .../UmbracoDbContextModelSnapshot.cs | 16 +- .../SqliteMigrationProvider.cs | 1 + .../UmbracoDbContext.cs | 4 +- src/Umbraco.Core/Constants-Conventions.cs | 2 + ...RuntimePremigrationsUpgradeNotification.cs | 16 + .../UmbracoBuilder.CoreServices.cs | 1 + .../Install/PremigrationUpgrader.cs | 83 ++++++ .../Migrations/EFCoreMigration.cs | 3 +- .../Migrations/Install/DatabaseBuilder.cs | 4 +- .../Migrations/Install/DatabaseDataCreator.cs | 8 + .../Upgrade/UmbracoPremigrationPlan.cs | 58 ++++ .../Upgrade/V_14_0_0/UpdateToOpenIddictV5.cs | 19 ++ .../Runtime/CoreRuntime.cs | 20 ++ tests/Directory.Packages.props | 8 +- .../Umbraco.JsonSchema.csproj | 3 +- .../UmbracoJsonSchemaGenerator.cs | 7 +- 25 files changed, 941 insertions(+), 41 deletions(-) create mode 100644 src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.Designer.cs create mode 100644 src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.cs create mode 100644 src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.Designer.cs create mode 100644 src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.cs create mode 100644 src/Umbraco.Core/Notifications/RuntimePremigrationsUpgradeNotification.cs create mode 100644 src/Umbraco.Infrastructure/Install/PremigrationUpgrader.cs create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPremigrationPlan.cs create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/UpdateToOpenIddictV5.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 5bcf6f8e8a..0940d636c3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,25 +12,25 @@ - + - - - - + + + + - + - - + + - + @@ -41,15 +41,15 @@ - - + + - + - - + + @@ -57,9 +57,9 @@ - - - + + + @@ -73,7 +73,7 @@ - + @@ -87,4 +87,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs b/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs index 1f365a08aa..92ca409194 100644 --- a/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs +++ b/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs @@ -36,7 +36,7 @@ public class MemberApplicationManager : OpenIdDictApplicationManagerBase, IMembe { DisplayName = "Umbraco member access", ClientId = Constants.OAuthClientIds.Member, - Type = OpenIddictConstants.ClientTypes.Public, + ClientType = OpenIddictConstants.ClientTypes.Public, Permissions = { OpenIddictConstants.Permissions.Endpoints.Authorization, diff --git a/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs index 4162d695f8..ab47e3e8fe 100644 --- a/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs +++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs @@ -64,7 +64,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB { CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html") }, - Type = OpenIddictConstants.ClientTypes.Public, + ClientType = OpenIddictConstants.ClientTypes.Public, Permissions = { OpenIddictConstants.Permissions.Endpoints.Authorization, @@ -84,7 +84,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB { new Uri("https://oauth.pstmn.io/v1/callback"), new Uri("https://oauth.pstmn.io/v1/browser-callback") }, - Type = OpenIddictConstants.ClientTypes.Public, + ClientType = OpenIddictConstants.ClientTypes.Public, Permissions = { OpenIddictConstants.Permissions.Endpoints.Authorization, @@ -106,7 +106,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB { CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, _authorizeCallbackPathName), }, - Type = OpenIddictConstants.ClientTypes.Public, + ClientType = OpenIddictConstants.ClientTypes.Public, PostLogoutRedirectUris = { CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, _authorizeCallbackPathName), diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.Designer.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.Designer.cs new file mode 100644 index 0000000000..010cb718f0 --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.Designer.cs @@ -0,0 +1,277 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Umbraco.Cms.Persistence.EFCore; + +#nullable disable + +namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations +{ + [DbContext(typeof(UmbracoDbContext))] + [Migration("20240403140654_UpdateOpenIddictToV5")] + partial class UpdateOpenIddictToV5 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + + b.Property("Permissions") + .HasColumnType("nvarchar(max)"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Requirements") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique() + .HasFilter("[ClientId] IS NOT NULL"); + + b.ToTable("umbracoOpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("umbracoOpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Descriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Resources") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("umbracoOpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("umbracoOpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.cs new file mode 100644 index 0000000000..02c4754d8e --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/20240403140654_UpdateOpenIddictToV5.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations +{ + /// + public partial class UpdateOpenIddictToV5 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Type", + table: "umbracoOpenIddictApplications", + newName: "ClientType"); + + migrationBuilder.AddColumn( + name: "ApplicationType", + table: "umbracoOpenIddictApplications", + type: "nvarchar(50)", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "JsonWebKeySet", + table: "umbracoOpenIddictApplications", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Settings", + table: "umbracoOpenIddictApplications", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApplicationType", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "JsonWebKeySet", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "Settings", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.RenameColumn( + name: "ClientType", + table: "umbracoOpenIddictApplications", + newName: "Type"); + } + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs index 727f602b80..77b5f4f406 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/UmbracoDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.7") + .HasAnnotation("ProductVersion", "8.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -28,6 +28,10 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations .ValueGeneratedOnAdd() .HasColumnType("nvarchar(450)"); + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + b.Property("ClientId") .HasMaxLength(100) .HasColumnType("nvarchar(100)"); @@ -35,6 +39,10 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations b.Property("ClientSecret") .HasColumnType("nvarchar(max)"); + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + b.Property("ConcurrencyToken") .IsConcurrencyToken() .HasMaxLength(50) @@ -50,6 +58,9 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations b.Property("DisplayNames") .HasColumnType("nvarchar(max)"); + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + b.Property("Permissions") .HasColumnType("nvarchar(max)"); @@ -65,9 +76,8 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations b.Property("Requirements") .HasColumnType("nvarchar(max)"); - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + b.Property("Settings") + .HasColumnType("nvarchar(max)"); b.HasKey("Id"); diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs index 823a9e737f..384f23f422 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs @@ -30,6 +30,7 @@ public class SqlServerMigrationProvider : IMigrationProvider { EFCoreMigration.InitialCreate => typeof(Migrations.InitialCreate), EFCoreMigration.AddOpenIddict => typeof(Migrations.AddOpenIddict), + EFCoreMigration.UpdateOpenIddictToV5 => typeof(Migrations.UpdateOpenIddictToV5), _ => throw new ArgumentOutOfRangeException(nameof(migration), $@"Not expected migration value: {migration}") }; } diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.Designer.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.Designer.cs new file mode 100644 index 0000000000..fec4ad4dee --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.Designer.cs @@ -0,0 +1,269 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Umbraco.Cms.Persistence.EFCore; + +#nullable disable + +namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations +{ + [DbContext(typeof(UmbracoDbContext))] + [Migration("20240403141051_UpdateOpenIddictToV5")] + partial class UpdateOpenIddictToV5 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("ClientSecret") + .HasColumnType("TEXT"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .HasColumnType("TEXT"); + + b.Property("DisplayNames") + .HasColumnType("TEXT"); + + b.Property("JsonWebKeySet") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("TEXT"); + + b.Property("Properties") + .HasColumnType("TEXT"); + + b.Property("RedirectUris") + .HasColumnType("TEXT"); + + b.Property("Requirements") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("umbracoOpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicationId") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Properties") + .HasColumnType("TEXT"); + + b.Property("Scopes") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("umbracoOpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Descriptions") + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .HasColumnType("TEXT"); + + b.Property("DisplayNames") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Properties") + .HasColumnType("TEXT"); + + b.Property("Resources") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("umbracoOpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicationId") + .HasColumnType("TEXT"); + + b.Property("AuthorizationId") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Payload") + .HasColumnType("TEXT"); + + b.Property("Properties") + .HasColumnType("TEXT"); + + b.Property("RedemptionDate") + .HasColumnType("TEXT"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("umbracoOpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.cs new file mode 100644 index 0000000000..f91cdcfa8e --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/20240403141051_UpdateOpenIddictToV5.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations +{ + /// + public partial class UpdateOpenIddictToV5 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Type", + table: "umbracoOpenIddictApplications", + newName: "ClientType"); + + migrationBuilder.AddColumn( + name: "ApplicationType", + table: "umbracoOpenIddictApplications", + type: "TEXT", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "JsonWebKeySet", + table: "umbracoOpenIddictApplications", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "Settings", + table: "umbracoOpenIddictApplications", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApplicationType", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "JsonWebKeySet", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "Settings", + table: "umbracoOpenIddictApplications"); + + migrationBuilder.RenameColumn( + name: "ClientType", + table: "umbracoOpenIddictApplications", + newName: "Type"); + } + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs index 138647e7f3..4ea82c3ff5 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Migrations/UmbracoDbContextModelSnapshot.cs @@ -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", "7.0.7"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => { @@ -23,6 +23,10 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations .ValueGeneratedOnAdd() .HasColumnType("TEXT"); + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + b.Property("ClientId") .HasMaxLength(100) .HasColumnType("TEXT"); @@ -30,6 +34,10 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations b.Property("ClientSecret") .HasColumnType("TEXT"); + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + b.Property("ConcurrencyToken") .IsConcurrencyToken() .HasMaxLength(50) @@ -45,6 +53,9 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations b.Property("DisplayNames") .HasColumnType("TEXT"); + b.Property("JsonWebKeySet") + .HasColumnType("TEXT"); + b.Property("Permissions") .HasColumnType("TEXT"); @@ -60,8 +71,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations b.Property("Requirements") .HasColumnType("TEXT"); - b.Property("Type") - .HasMaxLength(50) + b.Property("Settings") .HasColumnType("TEXT"); b.HasKey("Id"); diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs index 834a68e2e2..e25d2955c7 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs @@ -38,6 +38,7 @@ public class SqliteMigrationProvider : IMigrationProvider { EFCoreMigration.InitialCreate => typeof(Migrations.InitialCreate), EFCoreMigration.AddOpenIddict => typeof(Migrations.AddOpenIddict), + EFCoreMigration.UpdateOpenIddictToV5 => typeof(Migrations.UpdateOpenIddictToV5), _ => throw new ArgumentOutOfRangeException(nameof(migration), $@"Not expected migration value: {migration}") }; } diff --git a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs index 980a5f4a6d..9fcaccfe37 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs @@ -17,9 +17,9 @@ namespace Umbraco.Cms.Persistence.EFCore; /// and insure the 'src/Umbraco.Web.UI/appsettings.json' have a connection string set with the right provider. /// /// Create a migration for each provider. -/// dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -- --provider SqlServer +/// dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -c UmbracoDbContext -- --provider SqlServer /// -/// dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite -- --provider Sqlite +/// dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite -c UmbracoDbContext -- --provider Sqlite /// /// Remove the last migration for each provider. /// dotnet ef migrations remove -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -- --provider SqlServer diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 7047e16437..647226b6d7 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -10,8 +10,10 @@ public static partial class Constants public static class Migrations { public const string UmbracoUpgradePlanName = "Umbraco.Core"; + public const string UmbracoUpgradePlanPremigrationsName = "Umbraco.Core.Premigrations"; public const string KeyValuePrefix = "Umbraco.Core.Upgrader.State+"; public const string UmbracoUpgradePlanKey = KeyValuePrefix + UmbracoUpgradePlanName; + public const string UmbracoUpgradePlanPremigrationsKey = KeyValuePrefix + UmbracoUpgradePlanPremigrationsName; } public static class PermissionCategories diff --git a/src/Umbraco.Core/Notifications/RuntimePremigrationsUpgradeNotification.cs b/src/Umbraco.Core/Notifications/RuntimePremigrationsUpgradeNotification.cs new file mode 100644 index 0000000000..e9c9fd85ec --- /dev/null +++ b/src/Umbraco.Core/Notifications/RuntimePremigrationsUpgradeNotification.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Cms.Core.Notifications; + +public class RuntimePremigrationsUpgradeNotification : INotification +{ + public enum PremigrationUpgradeResult + { + NotRequired = 0, + HasErrors = 1, + CoreUpgradeComplete = 100, + } + + /// + /// Gets/sets the result of the upgrade + /// + public PremigrationUpgradeResult UpgradeResult { get; set; } = PremigrationUpgradeResult.NotRequired; +} diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index af45471080..c95576395e 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -85,6 +85,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddSingleton(); builder.AddNotificationAsyncHandler(); builder.AddNotificationAsyncHandler(); + builder.AddNotificationAsyncHandler(); // Add runtime mode validation builder.Services.AddSingleton(); diff --git a/src/Umbraco.Infrastructure/Install/PremigrationUpgrader.cs b/src/Umbraco.Infrastructure/Install/PremigrationUpgrader.cs new file mode 100644 index 0000000000..a3307a347b --- /dev/null +++ b/src/Umbraco.Infrastructure/Install/PremigrationUpgrader.cs @@ -0,0 +1,83 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Migrations.Install; +using Umbraco.Cms.Infrastructure.Migrations.Upgrade; +using Umbraco.Cms.Infrastructure.Persistence; + +namespace Umbraco.Cms.Infrastructure.Install; + +/// +/// Handles to execute the unattended Umbraco upgrader +/// or the unattended Package migrations runner. +/// +public class PremigrationUpgrader : INotificationAsyncHandler +{ + private readonly DatabaseBuilder _databaseBuilder; + private readonly IProfilingLogger _profilingLogger; + private readonly IRuntimeState _runtimeState; + private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; + private readonly IKeyValueService _keyValueService; + + public PremigrationUpgrader( + IProfilingLogger profilingLogger, + DatabaseBuilder databaseBuilder, + IRuntimeState runtimeState, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IKeyValueService keyValueService) + { + _profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); + _databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder)); + _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); + _umbracoDatabaseFactory = umbracoDatabaseFactory; + _keyValueService = keyValueService; + } + + public Task HandleAsync(RuntimePremigrationsUpgradeNotification notification, CancellationToken cancellationToken) + { + // no connection string set + if (_umbracoDatabaseFactory.Configured is false) + { + return Task.CompletedTask; + } + + if (_databaseBuilder.IsUmbracoInstalled() is false) + { + return Task.CompletedTask; + } + + var plan = new UmbracoPremigrationPlan(); + if (HasMissingPremigrations(plan) is false) + { + return Task.CompletedTask; + } + + using (_profilingLogger.IsEnabled(LogLevel.Verbose) is false ? null : _profilingLogger.TraceDuration( + "Starting premigration upgrade.", + "Unattended premigration completed.")) + { + DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan); + if (result?.Success is false) + { + var innerException = new IOException( + "An error occurred while running the premigration upgrade.\n" + result.Message); + _runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException); + } + + notification.UpgradeResult = + RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.CoreUpgradeComplete; + } + + return Task.CompletedTask; + } + + private bool HasMissingPremigrations(UmbracoPremigrationPlan umbracoPremigrationPlan) + { + var premigrationState = _keyValueService.GetValue(Constants.Conventions.Migrations.UmbracoUpgradePlanPremigrationsKey); + + return umbracoPremigrationPlan.FinalState != premigrationState; + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/EFCoreMigration.cs b/src/Umbraco.Infrastructure/Migrations/EFCoreMigration.cs index b6e80f8cdb..d29fa0225e 100644 --- a/src/Umbraco.Infrastructure/Migrations/EFCoreMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/EFCoreMigration.cs @@ -3,5 +3,6 @@ namespace Umbraco.Cms.Persistence.EFCore.Migrations; public enum EFCoreMigration { InitialCreate = 0, - AddOpenIddict = 1 + AddOpenIddict = 1, + UpdateOpenIddictToV5 = 2, } diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs index ef8ec1eea6..e2930e49bd 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs @@ -347,6 +347,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install } } + public Result? UpgradeSchemaAndData(UmbracoPlan plan) => UpgradeSchemaAndData((MigrationPlan)plan); + /// /// Upgrades the database schema and data by running migrations. /// @@ -355,7 +357,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install /// configured and it is possible to connect to the database. /// Runs whichever migrations need to run. /// - public Result? UpgradeSchemaAndData(UmbracoPlan plan) + public Result? UpgradeSchemaAndData(MigrationPlan plan) { try { diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index 6341da03d3..a7a0b38b49 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -2318,6 +2318,14 @@ internal class DatabaseDataCreator _database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now }); + + + upgrader = new Upgrader(new UmbracoPremigrationPlan()); + stateValueKey = upgrader.StateValueKey; + finalState = upgrader.Plan.FinalState; + + _database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, + new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now }); } private void CreateLogViewerQueryData() diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPremigrationPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPremigrationPlan.cs new file mode 100644 index 0000000000..23f4aa108c --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPremigrationPlan.cs @@ -0,0 +1,58 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade; + +/// +/// Represents the Umbraco CMS pre-migration plan. - Migrations that always runs unattended before the main migration plan. +/// +/// +public class UmbracoPremigrationPlan : MigrationPlan +{ + /// + /// Initializes a new instance of the class. + /// + /// The Umbraco version. + public UmbracoPremigrationPlan() + : base(Constants.Conventions.Migrations.UmbracoUpgradePlanPremigrationsName) + => DefinePlan(); + + /// + /// + /// This is set to the final migration state of 13.0, making that the lowest supported version to upgrade from. + /// + public override string InitialState => ""; + + /// + /// Defines the plan. + /// + /// + /// This is virtual for testing purposes. + /// + protected virtual void DefinePlan() + { + // Please take great care when modifying the plan! + // + // Creating a migration: append the migration to the main chain, using a new GUID. + // + // If the new migration causes a merge conflict, because someone else also added another + // new migration, you NEED to fix the conflict by providing one default path, and paths + // out of the conflict states, eg: + // + // From("state-1") + // To("state-a") + // To("state-b") // Some might already be in this state, without having applied ChangeA + // + // From("state-1") + // .Merge() + // .To("state-a") + // .With() + // .To("state-b") + // .As("state-2"); + + From(InitialState); + + // To 14.0.0 + To("{76FBF80E-37E6-462E-ADC1-25668F56151D}"); + } +} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/UpdateToOpenIddictV5.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/UpdateToOpenIddictV5.cs new file mode 100644 index 0000000000..dbafc2346c --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/UpdateToOpenIddictV5.cs @@ -0,0 +1,19 @@ +using Umbraco.Cms.Persistence.EFCore.Migrations; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_14_0_0; + +public class UpdateToOpenIddictV5 : MigrationBase +{ + private readonly IEFCoreMigrationExecutor _efCoreMigrationExecutor; + + public UpdateToOpenIddictV5(IMigrationContext context, IEFCoreMigrationExecutor efCoreMigrationExecutor) + : base(context) + { + _efCoreMigrationExecutor = efCoreMigrationExecutor; + } + + protected override void Migrate() + { + _efCoreMigrationExecutor.ExecuteSingleMigrationAsync(EFCoreMigration.UpdateOpenIddictToV5); + } +} diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 81929290cd..42f7f6de3f 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -179,6 +179,26 @@ public class CoreRuntime : IRuntime throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}"); } + var premigrationUpgradeNotification = new RuntimePremigrationsUpgradeNotification(); + await _eventAggregator.PublishAsync(premigrationUpgradeNotification, cancellationToken); + switch (premigrationUpgradeNotification.UpgradeResult) + { + case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.HasErrors: + if (State.BootFailedException is null) + { + throw new InvalidOperationException($"Premigration upgrade result was {RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.HasErrors} but no {nameof(BootFailedException)} was registered"); + } + + // We cannot continue here, the exception will be rethrown by BootFailedMiddelware + return; + case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.CoreUpgradeComplete: + // Upgrade is done, set reason to Run + DetermineRuntimeLevel(); + break; + case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.NotRequired: + break; + } + // If level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade var unattendedUpgradeNotification = new RuntimeUnattendedUpgradeNotification(); await _eventAggregator.PublishAsync(unattendedUpgradeNotification, cancellationToken); diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 0fd40d25b4..ca3285ed2b 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -5,9 +5,9 @@ - + - + @@ -17,10 +17,10 @@ - + - + \ No newline at end of file diff --git a/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj b/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj index 3e3b2792a8..fbdc44a834 100644 --- a/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj +++ b/tools/Umbraco.JsonSchema/Umbraco.JsonSchema.csproj @@ -7,7 +7,8 @@ - + + diff --git a/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs b/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs index f1a55479e3..cc5e0f812c 100644 --- a/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs +++ b/tools/Umbraco.JsonSchema/UmbracoJsonSchemaGenerator.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using NJsonSchema.Generation; +using NJsonSchema.NewtonsoftJson.Generation; /// public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator @@ -10,7 +11,7 @@ public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator /// Initializes a new instance of the class. /// public UmbracoJsonSchemaGenerator() - : base(new JsonSchemaGeneratorSettings() + : base(new NewtonsoftJsonSchemaGeneratorSettings() { AlwaysAllowAdditionalObjectProperties = true, DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull, @@ -22,7 +23,9 @@ public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator } }) { - Settings.SerializerSettings.Converters.Add(new StringEnumConverter()); + + + ((NewtonsoftJsonSchemaGeneratorSettings)Settings).SerializerSettings.Converters.Add(new StringEnumConverter()); } ///