Storage - Replace LiteDB with SQLite + EF Core (#843)

Storage - Added LiteDB to SQLite migration
UI - Try to die a bit more gracefully
Core - Delay start watching plugins for hot reload after initializing
UI - Simplify category management logic
UI - Avoid crash during profile icon load
Storage - Fix entry metadata retrieval
This commit is contained in:
RobertBeekman authored and GitHub committed 2024-03-13 20:19:21 +01:00
1 parent 9132301dbf
commit 551921db9f
130 files changed
+2965 -1135

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LiteDB" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Serilog" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
+59
View File
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;
using Artemis.Storage.Entities.General;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Entities.Workshop;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage;
public class ArtemisDbContext : DbContext
{
public DbSet<DeviceEntity> Devices => Set<DeviceEntity>();
public DbSet<EntryEntity> Entries => Set<EntryEntity>();
public DbSet<PluginEntity> Plugins => Set<PluginEntity>();
public DbSet<PluginFeatureEntity> PluginFeatures => Set<PluginFeatureEntity>();
public DbSet<PluginSettingEntity> PluginSettings => Set<PluginSettingEntity>();
public DbSet<ProfileCategoryEntity> ProfileCategories => Set<ProfileCategoryEntity>();
public DbSet<ProfileContainerEntity> ProfileContainers => Set<ProfileContainerEntity>();
public DbSet<ReleaseEntity> Releases => Set<ReleaseEntity>();
public string DataFolder { get; set; } = string.Empty;
public JsonSerializerOptions JsonSerializerOptions { get; set; } = JsonSerializerOptions.Default;
/// <inheritdoc />
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Data Source={Path.Combine(DataFolder, "artemis.db")}");
}
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DeviceEntity>()
.OwnsOne(d => d.InputIdentifiers, builder => builder.ToJson())
.OwnsOne(d => d.InputMappings, builder => builder.ToJson());
modelBuilder.Entity<EntryEntity>()
.Property(e => e.Metadata)
.HasConversion(
v => JsonSerializer.Serialize(v, JsonSerializerOptions),
v => JsonSerializer.Deserialize<Dictionary<string, JsonNode>>(v, JsonSerializerOptions) ?? new Dictionary<string, JsonNode>());
modelBuilder.Entity<ProfileContainerEntity>()
.Property(e => e.ProfileConfiguration)
.HasConversion(
v => JsonSerializer.Serialize(v, JsonSerializerOptions),
v => JsonSerializer.Deserialize<ProfileConfigurationEntity>(v, JsonSerializerOptions) ?? new ProfileConfigurationEntity());
modelBuilder.Entity<ProfileContainerEntity>()
.Property(e => e.Profile)
.HasConversion(
v => JsonSerializer.Serialize(v, JsonSerializerOptions),
v => JsonSerializer.Deserialize<ProfileEntity>(v, JsonSerializerOptions) ?? new ProfileEntity());
}
}
@@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
namespace Artemis.Storage.Entities.General;
public class QueuedActionEntity
{
public QueuedActionEntity()
{
Parameters = new Dictionary<string, object>();
}
public Guid Id { get; set; }
public string Type { get; set; } = string.Empty;
public DateTimeOffset CreatedAt { get; set; }
public Dictionary<string, object> Parameters { get; set; }
}
@@ -1,11 +1,17 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.General;
[Index(nameof(Version), IsUnique = true)]
[Index(nameof(InstalledAt))]
public class ReleaseEntity
{
public Guid Id { get; set; }
[MaxLength(64)]
public string Version { get; set; } = string.Empty;
public DateTimeOffset? InstalledAt { get; set; }
}
@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Plugins;
/// <summary>
/// Represents the configuration of a plugin, each plugin has one configuration
/// </summary>
[Index(nameof(PluginGuid), IsUnique = true)]
public class PluginEntity
{
public PluginEntity()
@@ -14,6 +17,7 @@ public class PluginEntity
}
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public bool IsEnabled { get; set; }
public List<PluginFeatureEntity> Features { get; set; }
@@ -24,6 +28,8 @@ public class PluginEntity
/// </summary>
public class PluginFeatureEntity
{
public Guid Id { get; set; }
public string Type { get; set; } = string.Empty;
public bool IsEnabled { get; set; }
}
@@ -1,15 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Plugins;
/// <summary>
/// Represents the setting of a plugin, a plugin can have multiple settings
/// </summary>
[Index(nameof(Name), nameof(PluginGuid), IsUnique = true)]
[Index(nameof(PluginGuid))]
public class PluginSettingEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
[MaxLength(128)]
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
}
@@ -1,6 +1,5 @@
using System;
using Artemis.Storage.Entities.Profile.Abstract;
using LiteDB;
namespace Artemis.Storage.Entities.Profile;
@@ -11,8 +10,5 @@ public class FolderEntity : RenderElementEntity
public bool IsExpanded { get; set; }
public bool Suspended { get; set; }
[BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } = null!;
public Guid ProfileId { get; set; }
}
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.AdaptionHints;
using LiteDB;
namespace Artemis.Storage.Entities.Profile;
@@ -25,8 +24,5 @@ public class LayerEntity : RenderElementEntity
public PropertyGroupEntity? TransformPropertyGroup { get; set; }
public LayerBrushEntity? LayerBrush { get; set; }
[BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } = null!;
public Guid ProfileId { get; set; }
}
@@ -1,16 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Profile;
[Index(nameof(Name), IsUnique = true)]
public class ProfileCategoryEntity
{
public Guid Id { get; set; }
[MaxLength(64)]
public string Name { get; set; } = string.Empty;
public bool IsCollapsed { get; set; }
public bool IsSuspended { get; set; }
public int Order { get; set; }
public List<ProfileConfigurationEntity> ProfileConfigurations { get; set; } = new();
public List<ProfileContainerEntity> ProfileConfigurations { get; set; } = new();
}
@@ -1,6 +1,5 @@
using System;
using Artemis.Storage.Entities.Profile.Nodes;
using Serilog.Core;
namespace Artemis.Storage.Entities.Profile;
@@ -8,7 +7,6 @@ public class ProfileConfigurationEntity
{
public string Name { get; set; } = string.Empty;
public string? MaterialIcon { get; set; }
public Guid FileIconId { get; set; }
public int IconType { get; set; }
public bool IconFill { get; set; }
public int Order { get; set; }
@@ -27,5 +25,5 @@ public class ProfileConfigurationEntity
public Guid ProfileId { get; set; }
public bool FadeInAndOut { get; set; }
public int Version { get; set; } = StorageMigrationService.PROFILE_VERSION;
public int Version { get; set; }
}
@@ -0,0 +1,14 @@
using System;
namespace Artemis.Storage.Entities.Profile;
public class ProfileContainerEntity
{
public Guid Id { get; set; }
public byte[] Icon { get; set; } = Array.Empty<byte>();
public ProfileCategoryEntity ProfileCategory { get; set; } = null!;
public ProfileConfigurationEntity ProfileConfiguration { get; set; } = new();
public ProfileEntity Profile { get; set; } = new();
}
@@ -0,0 +1,10 @@
using System;
namespace Artemis.Storage.Entities;
internal class RawProfileContainer
{
public Guid Id { get; set; }
public string ProfileConfiguration { get; set; }
public string Profile { get; set; }
}
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Artemis.Storage.Entities.Surface;
@@ -11,8 +12,12 @@ public class DeviceEntity
Categories = new List<int>();
}
[MaxLength(512)]
public string Id { get; set; } = string.Empty;
[MaxLength(512)]
public string DeviceProvider { get; set; } = string.Empty;
public float X { get; set; }
public float Y { get; set; }
public float Rotation { get; set; }
@@ -22,10 +27,16 @@ public class DeviceEntity
public float GreenScale { get; set; }
public float BlueScale { get; set; }
public bool IsEnabled { get; set; }
public int PhysicalLayout { get; set; }
[MaxLength(32)]
public string? LogicalLayout { get; set; }
[MaxLength(64)]
public string? LayoutType { get; set; }
[MaxLength(512)]
public string? LayoutParameter { get; set; }
public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; }
@@ -42,5 +53,5 @@ public class InputMappingEntity
public class DeviceInputIdentifierEntity
{
public string InputProvider { get; set; } = string.Empty;
public object Identifier { get; set; } = string.Empty;
public string Identifier { get; set; } = string.Empty;
}
@@ -1,21 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Workshop;
[Index(nameof(EntryId), IsUnique = true)]
public class EntryEntity
{
public Guid Id { get; set; }
public long EntryId { get; set; }
public int EntryType { get; set; }
public string Author { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public long ReleaseId { get; set; }
public string ReleaseVersion { get; set; } = string.Empty;
public DateTimeOffset InstalledAt { get; set; }
public Dictionary<string,object>? Metadata { get; set; }
public Dictionary<string, JsonNode>? Metadata { get; set; }
}
@@ -0,0 +1,14 @@
using System;
namespace Artemis.Storage.Exceptions;
public class ArtemisStorageException : Exception
{
public ArtemisStorageException(string message) : base(message)
{
}
public ArtemisStorageException(string message, Exception innerException) : base(message, innerException)
{
}
}
@@ -0,0 +1,352 @@
// <auto-generated />
using System;
using Artemis.Storage;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Artemis.Storage.Migrations
{
[DbContext(typeof(ArtemisDbContext))]
[Migration("20240310201706_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
modelBuilder.Entity("Artemis.Storage.Entities.General.ReleaseEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("InstalledAt")
.HasColumnType("TEXT");
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstalledAt");
b.HasIndex("Version")
.IsUnique();
b.ToTable("Releases");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid")
.IsUnique();
b.ToTable("Plugins");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginFeatureEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid?>("PluginEntityId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginEntityId");
b.ToTable("PluginFeatures");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginSettingEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid");
b.HasIndex("Name", "PluginGuid")
.IsUnique();
b.ToTable("PluginSettings");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsCollapsed")
.HasColumnType("INTEGER");
b.Property<bool>("IsSuspended")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<int>("Order")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProfileCategories");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileContainerEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Icon")
.IsRequired()
.HasColumnType("BLOB");
b.Property<string>("Profile")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid>("ProfileCategoryId")
.HasColumnType("TEXT");
b.Property<string>("ProfileConfiguration")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProfileCategoryId");
b.ToTable("ProfileContainers");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.Property<string>("Id")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("BlueScale")
.HasColumnType("REAL");
b.Property<string>("Categories")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("DeviceProvider")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("GreenScale")
.HasColumnType("REAL");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<string>("LayoutParameter")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<string>("LayoutType")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LogicalLayout")
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int>("PhysicalLayout")
.HasColumnType("INTEGER");
b.Property<float>("RedScale")
.HasColumnType("REAL");
b.Property<float>("Rotation")
.HasColumnType("REAL");
b.Property<float>("Scale")
.HasColumnType("REAL");
b.Property<float>("X")
.HasColumnType("REAL");
b.Property<float>("Y")
.HasColumnType("REAL");
b.Property<int>("ZIndex")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Devices");
});
modelBuilder.Entity("Artemis.Storage.Entities.Workshop.EntryEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("EntryId")
.HasColumnType("INTEGER");
b.Property<int>("EntryType")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("InstalledAt")
.HasColumnType("TEXT");
b.Property<string>("Metadata")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("ReleaseId")
.HasColumnType("INTEGER");
b.Property<string>("ReleaseVersion")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("EntryId")
.IsUnique();
b.ToTable("Entries");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginFeatureEntity", b =>
{
b.HasOne("Artemis.Storage.Entities.Plugins.PluginEntity", null)
.WithMany("Features")
.HasForeignKey("PluginEntityId");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileContainerEntity", b =>
{
b.HasOne("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", "ProfileCategory")
.WithMany("ProfileConfigurations")
.HasForeignKey("ProfileCategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ProfileCategory");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.OwnsOne("System.Collections.Generic.List<Artemis.Storage.Entities.Surface.DeviceInputIdentifierEntity>", "InputIdentifiers", b1 =>
{
b1.Property<string>("DeviceEntityId")
.HasColumnType("TEXT");
b1.Property<int>("Capacity")
.HasColumnType("INTEGER");
b1.HasKey("DeviceEntityId");
b1.ToTable("Devices");
b1.ToJson("InputIdentifiers");
b1.WithOwner()
.HasForeignKey("DeviceEntityId");
});
b.OwnsOne("System.Collections.Generic.List<Artemis.Storage.Entities.Surface.InputMappingEntity>", "InputMappings", b1 =>
{
b1.Property<string>("DeviceEntityId")
.HasColumnType("TEXT");
b1.Property<int>("Capacity")
.HasColumnType("INTEGER");
b1.HasKey("DeviceEntityId");
b1.ToTable("Devices");
b1.ToJson("InputMappings");
b1.WithOwner()
.HasForeignKey("DeviceEntityId");
});
b.Navigation("InputIdentifiers")
.IsRequired();
b.Navigation("InputMappings")
.IsRequired();
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginEntity", b =>
{
b.Navigation("Features");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", b =>
{
b.Navigation("ProfileConfigurations");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,235 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Artemis.Storage.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Devices",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", maxLength: 512, nullable: false),
DeviceProvider = table.Column<string>(type: "TEXT", maxLength: 512, nullable: false),
X = table.Column<float>(type: "REAL", nullable: false),
Y = table.Column<float>(type: "REAL", nullable: false),
Rotation = table.Column<float>(type: "REAL", nullable: false),
Scale = table.Column<float>(type: "REAL", nullable: false),
ZIndex = table.Column<int>(type: "INTEGER", nullable: false),
RedScale = table.Column<float>(type: "REAL", nullable: false),
GreenScale = table.Column<float>(type: "REAL", nullable: false),
BlueScale = table.Column<float>(type: "REAL", nullable: false),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
PhysicalLayout = table.Column<int>(type: "INTEGER", nullable: false),
LogicalLayout = table.Column<string>(type: "TEXT", maxLength: 32, nullable: true),
LayoutType = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
LayoutParameter = table.Column<string>(type: "TEXT", maxLength: 512, nullable: true),
Categories = table.Column<string>(type: "TEXT", nullable: false),
InputIdentifiers = table.Column<string>(type: "TEXT", nullable: false),
InputMappings = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Devices", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Entries",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
EntryId = table.Column<long>(type: "INTEGER", nullable: false),
EntryType = table.Column<int>(type: "INTEGER", nullable: false),
Author = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
ReleaseId = table.Column<long>(type: "INTEGER", nullable: false),
ReleaseVersion = table.Column<string>(type: "TEXT", nullable: false),
InstalledAt = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
Metadata = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Entries", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Plugins",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
PluginGuid = table.Column<Guid>(type: "TEXT", nullable: false),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Plugins", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PluginSettings",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
PluginGuid = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 128, nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PluginSettings", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ProfileCategories",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
IsCollapsed = table.Column<bool>(type: "INTEGER", nullable: false),
IsSuspended = table.Column<bool>(type: "INTEGER", nullable: false),
Order = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProfileCategories", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Releases",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Version = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
InstalledAt = table.Column<DateTimeOffset>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Releases", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PluginFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Type = table.Column<string>(type: "TEXT", nullable: false),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
PluginEntityId = table.Column<Guid>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PluginFeatures", x => x.Id);
table.ForeignKey(
name: "FK_PluginFeatures_Plugins_PluginEntityId",
column: x => x.PluginEntityId,
principalTable: "Plugins",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ProfileContainers",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Icon = table.Column<byte[]>(type: "BLOB", nullable: false),
ProfileCategoryId = table.Column<Guid>(type: "TEXT", nullable: false),
ProfileConfiguration = table.Column<string>(type: "TEXT", nullable: false),
Profile = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProfileContainers", x => x.Id);
table.ForeignKey(
name: "FK_ProfileContainers_ProfileCategories_ProfileCategoryId",
column: x => x.ProfileCategoryId,
principalTable: "ProfileCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Entries_EntryId",
table: "Entries",
column: "EntryId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginFeatures_PluginEntityId",
table: "PluginFeatures",
column: "PluginEntityId");
migrationBuilder.CreateIndex(
name: "IX_Plugins_PluginGuid",
table: "Plugins",
column: "PluginGuid",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginSettings_Name_PluginGuid",
table: "PluginSettings",
columns: new[] { "Name", "PluginGuid" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginSettings_PluginGuid",
table: "PluginSettings",
column: "PluginGuid");
migrationBuilder.CreateIndex(
name: "IX_ProfileCategories_Name",
table: "ProfileCategories",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ProfileContainers_ProfileCategoryId",
table: "ProfileContainers",
column: "ProfileCategoryId");
migrationBuilder.CreateIndex(
name: "IX_Releases_InstalledAt",
table: "Releases",
column: "InstalledAt");
migrationBuilder.CreateIndex(
name: "IX_Releases_Version",
table: "Releases",
column: "Version",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Devices");
migrationBuilder.DropTable(
name: "Entries");
migrationBuilder.DropTable(
name: "PluginFeatures");
migrationBuilder.DropTable(
name: "PluginSettings");
migrationBuilder.DropTable(
name: "ProfileContainers");
migrationBuilder.DropTable(
name: "Releases");
migrationBuilder.DropTable(
name: "Plugins");
migrationBuilder.DropTable(
name: "ProfileCategories");
}
}
}
@@ -0,0 +1,349 @@
// <auto-generated />
using System;
using Artemis.Storage;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Artemis.Storage.Migrations
{
[DbContext(typeof(ArtemisDbContext))]
partial class ArtemisDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
modelBuilder.Entity("Artemis.Storage.Entities.General.ReleaseEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("InstalledAt")
.HasColumnType("TEXT");
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstalledAt");
b.HasIndex("Version")
.IsUnique();
b.ToTable("Releases");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid")
.IsUnique();
b.ToTable("Plugins");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginFeatureEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid?>("PluginEntityId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginEntityId");
b.ToTable("PluginFeatures");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginSettingEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid");
b.HasIndex("Name", "PluginGuid")
.IsUnique();
b.ToTable("PluginSettings");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsCollapsed")
.HasColumnType("INTEGER");
b.Property<bool>("IsSuspended")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<int>("Order")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProfileCategories");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileContainerEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Icon")
.IsRequired()
.HasColumnType("BLOB");
b.Property<string>("Profile")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid>("ProfileCategoryId")
.HasColumnType("TEXT");
b.Property<string>("ProfileConfiguration")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProfileCategoryId");
b.ToTable("ProfileContainers");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.Property<string>("Id")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("BlueScale")
.HasColumnType("REAL");
b.Property<string>("Categories")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("DeviceProvider")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("GreenScale")
.HasColumnType("REAL");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<string>("LayoutParameter")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<string>("LayoutType")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LogicalLayout")
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int>("PhysicalLayout")
.HasColumnType("INTEGER");
b.Property<float>("RedScale")
.HasColumnType("REAL");
b.Property<float>("Rotation")
.HasColumnType("REAL");
b.Property<float>("Scale")
.HasColumnType("REAL");
b.Property<float>("X")
.HasColumnType("REAL");
b.Property<float>("Y")
.HasColumnType("REAL");
b.Property<int>("ZIndex")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Devices");
});
modelBuilder.Entity("Artemis.Storage.Entities.Workshop.EntryEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("EntryId")
.HasColumnType("INTEGER");
b.Property<int>("EntryType")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("InstalledAt")
.HasColumnType("TEXT");
b.Property<string>("Metadata")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("ReleaseId")
.HasColumnType("INTEGER");
b.Property<string>("ReleaseVersion")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("EntryId")
.IsUnique();
b.ToTable("Entries");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginFeatureEntity", b =>
{
b.HasOne("Artemis.Storage.Entities.Plugins.PluginEntity", null)
.WithMany("Features")
.HasForeignKey("PluginEntityId");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileContainerEntity", b =>
{
b.HasOne("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", "ProfileCategory")
.WithMany("ProfileConfigurations")
.HasForeignKey("ProfileCategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ProfileCategory");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.OwnsOne("System.Collections.Generic.List<Artemis.Storage.Entities.Surface.DeviceInputIdentifierEntity>", "InputIdentifiers", b1 =>
{
b1.Property<string>("DeviceEntityId")
.HasColumnType("TEXT");
b1.Property<int>("Capacity")
.HasColumnType("INTEGER");
b1.HasKey("DeviceEntityId");
b1.ToTable("Devices");
b1.ToJson("InputIdentifiers");
b1.WithOwner()
.HasForeignKey("DeviceEntityId");
});
b.OwnsOne("System.Collections.Generic.List<Artemis.Storage.Entities.Surface.InputMappingEntity>", "InputMappings", b1 =>
{
b1.Property<string>("DeviceEntityId")
.HasColumnType("TEXT");
b1.Property<int>("Capacity")
.HasColumnType("INTEGER");
b1.HasKey("DeviceEntityId");
b1.ToTable("Devices");
b1.ToJson("InputMappings");
b1.WithOwner()
.HasForeignKey("DeviceEntityId");
});
b.Navigation("InputIdentifiers")
.IsRequired();
b.Navigation("InputMappings")
.IsRequired();
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginEntity", b =>
{
b.Navigation("Features");
});
modelBuilder.Entity("Artemis.Storage.Entities.Profile.ProfileCategoryEntity", b =>
{
b.Navigation("ProfileConfigurations");
});
#pragma warning restore 612, 618
}
}
}
@@ -1,9 +0,0 @@
using LiteDB;
namespace Artemis.Storage.Migrations;
public interface IStorageMigration
{
int UserVersion { get; }
void Apply(LiteRepository repository);
}
@@ -1,19 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0020AvaloniaReset : IStorageMigration
{
public int UserVersion => 20;
public void Apply(LiteRepository repository)
{
repository.Database.Commit();
List<string> collectionNames = repository.Database.GetCollectionNames().ToList();
foreach (string collectionName in collectionNames)
repository.Database.DropCollection(collectionName);
}
}
@@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Nodes;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0021GradientNodes : IStorageMigration
{
private void MigrateDataBinding(PropertyEntity property)
{
NodeScriptEntity? script = property.DataBinding?.NodeScript;
NodeEntity? exitNode = script?.Nodes.FirstOrDefault(s => s.IsExitNode);
if (script == null || exitNode == null)
return;
// Create a new node at the same position of the exit node
NodeEntity gradientNode = new()
{
Id = Guid.NewGuid(),
Type = "ColorGradientNode",
ProviderId = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78",
Name = "Color Gradient",
Description = "Outputs a color gradient with the given colors",
X = exitNode.X,
Y = exitNode.Y,
Storage = property.Value // Copy the value of the property into the node storage
};
script.Nodes.Add(gradientNode);
// Move all connections of the exit node to the new node
foreach (NodeConnectionEntity connection in script.Connections)
{
if (connection.SourceNode == exitNode.Id)
{
connection.SourceNode = gradientNode.Id;
connection.SourcePinId++;
}
}
// Connect the data binding node to the source node
script.Connections.Add(new NodeConnectionEntity
{
SourceType = "ColorGradient",
SourceNode = exitNode.Id,
SourcePinCollectionId = -1,
SourcePinId = 0,
TargetType = "ColorGradient",
TargetNode = gradientNode.Id,
TargetPinCollectionId = -1,
TargetPinId = 0
});
// Move the exit node to the right
exitNode.X += 300;
exitNode.Y += 30;
}
private void MigrateDataBinding(PropertyGroupEntity? propertyGroup)
{
if (propertyGroup == null)
return;
foreach (PropertyGroupEntity propertyGroupPropertyGroup in propertyGroup.PropertyGroups)
MigrateDataBinding(propertyGroupPropertyGroup);
foreach (PropertyEntity property in propertyGroup.Properties)
{
if (property.Value.StartsWith("[{\"Color\":\"") && property.DataBinding?.NodeScript != null && property.DataBinding.IsEnabled)
MigrateDataBinding(property);
}
}
public int UserVersion => 21;
public void Apply(LiteRepository repository)
{
// Find all color gradient data bindings, there's no really good way to do this so infer it from the value
List<ProfileEntity> profiles = repository.Query<ProfileEntity>().ToList();
foreach (ProfileEntity profileEntity in profiles)
{
foreach (LayerEntity layer in profileEntity.Layers.Where(le => le.LayerBrush != null))
MigrateDataBinding(layer.LayerBrush?.PropertyGroup);
repository.Update(profileEntity);
}
}
}
@@ -1,86 +0,0 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using Artemis.Storage.Entities.Profile.Nodes;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0022TransitionNodes : IStorageMigration
{
private void MigrateNodeScript(NodeScriptEntity? nodeScript)
{
if (nodeScript == null)
return;
foreach (NodeEntity node in nodeScript.Nodes)
{
if (node.Type == "NumericEasingNode")
node.Type = "NumericTransitionNode";
else if (node.Type == "ColorGradientEasingNode")
node.Type = "ColorGradientTransitionNode";
else if (node.Type == "SKColorEasingNode")
node.Type = "SKColorTransitionNode";
else if (node.Type == "EasingTypeNode")
node.Type = "EasingFunctionNode";
}
}
private void MigratePropertyGroup(PropertyGroupEntity? propertyGroup)
{
if (propertyGroup == null)
return;
foreach (PropertyGroupEntity childPropertyGroup in propertyGroup.PropertyGroups)
MigratePropertyGroup(childPropertyGroup);
foreach (PropertyEntity property in propertyGroup.Properties)
MigrateNodeScript(property.DataBinding?.NodeScript);
}
private void MigrateDisplayCondition(IConditionEntity? conditionEntity)
{
if (conditionEntity is EventConditionEntity eventConditionEntity)
MigrateNodeScript(eventConditionEntity.Script);
else if (conditionEntity is StaticConditionEntity staticConditionEntity)
MigrateNodeScript(staticConditionEntity.Script);
}
public int UserVersion => 22;
public void Apply(LiteRepository repository)
{
// Migrate profile configuration display conditions
List<ProfileCategoryEntity> categories = repository.Query<ProfileCategoryEntity>().ToList();
foreach (ProfileCategoryEntity profileCategoryEntity in categories)
{
foreach (ProfileConfigurationEntity profileConfigurationEntity in profileCategoryEntity.ProfileConfigurations)
MigrateNodeScript(profileConfigurationEntity.ActivationCondition);
repository.Update(profileCategoryEntity);
}
// Migrate profile display conditions and data bindings
List<ProfileEntity> profiles = repository.Query<ProfileEntity>().ToList();
foreach (ProfileEntity profileEntity in profiles)
{
foreach (LayerEntity layer in profileEntity.Layers)
{
MigratePropertyGroup(layer.LayerBrush?.PropertyGroup);
MigratePropertyGroup(layer.GeneralPropertyGroup);
MigratePropertyGroup(layer.TransformPropertyGroup);
foreach (LayerEffectEntity layerEffectEntity in layer.LayerEffects)
MigratePropertyGroup(layerEffectEntity.PropertyGroup);
MigrateDisplayCondition(layer.DisplayCondition);
}
foreach (FolderEntity folder in profileEntity.Folders)
{
foreach (LayerEffectEntity folderLayerEffect in folder.LayerEffects)
MigratePropertyGroup(folderLayerEffect.PropertyGroup);
MigrateDisplayCondition(folder.DisplayCondition);
}
repository.Update(profileEntity);
}
}
}
@@ -1,34 +0,0 @@
using System.Collections.Generic;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0023LayoutProviders : IStorageMigration
{
public int UserVersion => 23;
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> deviceEntities = repository.Database.GetCollection("DeviceEntity");
List<BsonDocument> toUpdate = new();
foreach (BsonDocument bsonDocument in deviceEntities.FindAll())
{
if (bsonDocument.TryGetValue("CustomLayoutPath", out BsonValue customLayoutPath) && customLayoutPath.IsString && !string.IsNullOrEmpty(customLayoutPath.AsString))
{
bsonDocument.Add("LayoutType", new BsonValue("CustomPath"));
bsonDocument.Add("LayoutParameter", new BsonValue(customLayoutPath.AsString));
}
else if (bsonDocument.TryGetValue("DisableDefaultLayout", out BsonValue disableDefaultLayout) && disableDefaultLayout.AsBoolean)
bsonDocument.Add("LayoutType", new BsonValue("None"));
else
bsonDocument.Add("LayoutType", new BsonValue("Default"));
bsonDocument.Remove("CustomLayoutPath");
bsonDocument.Remove("DisableDefaultLayout");
toUpdate.Add(bsonDocument);
}
deviceEntities.Update(toUpdate);
}
}
@@ -1,107 +0,0 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0024NodeProviders : IStorageMigration
{
public int UserVersion => 24;
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity");
List<BsonDocument> categoriesToUpdate = new();
foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll())
{
BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray;
if (profiles != null)
{
foreach (BsonValue profile in profiles)
profile["Version"] = 1;
categoriesToUpdate.Add(profileCategoryBson);
}
}
categoryCollection.Update(categoriesToUpdate);
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("ProfileEntity");
List<BsonDocument> profilesToUpdate = new();
foreach (BsonDocument profileBson in collection.FindAll())
{
BsonArray? folders = profileBson["Folders"]?.AsArray;
BsonArray? layers = profileBson["Layers"]?.AsArray;
if (folders != null)
{
foreach (BsonValue folder in folders)
MigrateProfileElement(folder.AsDocument);
}
if (layers != null)
{
foreach (BsonValue layer in layers)
{
MigrateProfileElement(layer.AsDocument);
MigratePropertyGroup(layer.AsDocument["GeneralPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument);
}
}
profilesToUpdate.Add(profileBson);
}
collection.Update(profilesToUpdate);
}
private void MigrateProfileElement(BsonDocument profileElement)
{
BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray;
if (layerEffects != null)
{
foreach (BsonValue layerEffect in layerEffects)
MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument);
}
BsonValue? displayCondition = profileElement["DisplayCondition"];
if (displayCondition != null)
MigrateNodeScript(displayCondition.AsDocument["Script"].AsDocument);
}
private void MigratePropertyGroup(BsonDocument? propertyGroup)
{
if (propertyGroup == null || propertyGroup.Keys.Count == 0)
return;
BsonArray? properties = propertyGroup["Properties"]?.AsArray;
BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray;
if (properties != null)
{
foreach (BsonValue property in properties)
MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument);
}
if (propertyGroups != null)
{
foreach (BsonValue childPropertyGroup in propertyGroups)
MigratePropertyGroup(childPropertyGroup.AsDocument);
}
}
private void MigrateNodeScript(BsonDocument? nodeScript)
{
if (nodeScript == null || nodeScript.Keys.Count == 0)
return;
BsonArray? nodes = nodeScript["Nodes"]?.AsArray;
if (nodes == null)
return;
foreach (BsonValue node in nodes)
{
node.AsDocument["Type"] = node.AsDocument["Type"]?.AsString?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes");
node.AsDocument["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78";
}
}
}
@@ -1,46 +0,0 @@
using System.Collections.Generic;
using LiteDB;
namespace Artemis.Storage.Migrations.Storage;
public class M0025NodeProvidersProfileConfig : IStorageMigration
{
public int UserVersion => 25;
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity");
List<BsonDocument> toUpdate = new();
foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll())
{
BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray;
if (profiles != null)
{
foreach (BsonValue profile in profiles)
{
profile["Version"] = 2;
MigrateNodeScript(profile["ActivationCondition"]?.AsDocument);
}
toUpdate.Add(profileCategoryBson);
}
}
categoryCollection.Update(toUpdate);
}
private void MigrateNodeScript(BsonDocument? nodeScript)
{
if (nodeScript == null || nodeScript.Keys.Count == 0)
return;
BsonArray? nodes = nodeScript["Nodes"]?.AsArray;
if (nodes == null)
return;
foreach (BsonValue node in nodes)
{
node.AsDocument["Type"] = node.AsDocument["Type"]?.AsString?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes");
node.AsDocument["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78";
}
}
}
@@ -1,119 +0,0 @@
using System.Collections.Generic;
using Artemis.Storage.Migrations.Profile;
using LiteDB;
using Serilog;
namespace Artemis.Storage.Migrations.Storage;
public class M0026NodeStorage : IStorageMigration
{
private readonly ILogger _logger;
public M0026NodeStorage(ILogger logger)
{
_logger = logger;
}
public int UserVersion => 26;
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity");
List<BsonDocument> toUpdate = new();
foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll())
{
BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray;
if (profiles != null)
{
foreach (BsonValue profile in profiles)
{
profile["Version"] = 4;
MigrateNodeScript(profile["ActivationCondition"]?.AsDocument);
}
toUpdate.Add(profileCategoryBson);
}
}
categoryCollection.Update(toUpdate);
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("ProfileEntity");
List<BsonDocument> profilesToUpdate = new();
foreach (BsonDocument profileBson in collection.FindAll())
{
BsonArray? folders = profileBson["Folders"]?.AsArray;
BsonArray? layers = profileBson["Layers"]?.AsArray;
if (folders != null)
{
foreach (BsonValue folder in folders)
MigrateProfileElement(folder.AsDocument);
}
if (layers != null)
{
foreach (BsonValue layer in layers)
{
MigrateProfileElement(layer.AsDocument);
MigratePropertyGroup(layer.AsDocument["GeneralPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument);
}
}
profilesToUpdate.Add(profileBson);
}
collection.Update(profilesToUpdate);
}
private void MigrateProfileElement(BsonDocument profileElement)
{
BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray;
if (layerEffects != null)
{
foreach (BsonValue layerEffect in layerEffects)
MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument);
}
BsonValue? displayCondition = profileElement["DisplayCondition"];
if (displayCondition != null)
MigrateNodeScript(displayCondition.AsDocument["Script"].AsDocument);
}
private void MigratePropertyGroup(BsonDocument? propertyGroup)
{
if (propertyGroup == null || propertyGroup.Keys.Count == 0)
return;
BsonArray? properties = propertyGroup["Properties"]?.AsArray;
BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray;
if (properties != null)
{
foreach (BsonValue property in properties)
MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument);
}
if (propertyGroups != null)
{
foreach (BsonValue childPropertyGroup in propertyGroups)
MigratePropertyGroup(childPropertyGroup.AsDocument);
}
}
private void MigrateNodeScript(BsonDocument? nodeScript)
{
if (nodeScript == null || nodeScript.Keys.Count == 0)
return;
BsonArray? nodes = nodeScript["Nodes"]?.AsArray;
if (nodes == null)
return;
foreach (BsonValue node in nodes)
{
// Migrate the storage of the node
node["Storage"] = M0004NodeStorage.MigrateNodeStorageJson(node.AsDocument["Storage"]?.AsString, _logger);
}
}
}
@@ -1,47 +1,50 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories;
internal class DeviceRepository : IDeviceRepository
internal class DeviceRepository(Func<ArtemisDbContext> getContext) : IDeviceRepository
{
private readonly LiteRepository _repository;
public DeviceRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<DeviceEntity>().EnsureIndex(s => s.Id);
}
public void Add(DeviceEntity deviceEntity)
{
_repository.Insert(deviceEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.Devices.Add(deviceEntity);
dbContext.SaveChanges();
}
public void Remove(DeviceEntity deviceEntity)
{
_repository.Delete<DeviceEntity>(deviceEntity.Id);
using ArtemisDbContext dbContext = getContext();
dbContext.Devices.Remove(deviceEntity);
dbContext.SaveChanges();
}
public DeviceEntity? Get(string id)
{
return _repository.FirstOrDefault<DeviceEntity>(s => s.Id == id);
using ArtemisDbContext dbContext = getContext();
return dbContext.Devices.FirstOrDefault(d => d.Id == id);
}
public List<DeviceEntity> GetAll()
{
return _repository.Query<DeviceEntity>().Include(s => s.InputIdentifiers).ToList();
using ArtemisDbContext dbContext = getContext();
return dbContext.Devices.ToList();
}
public void Save(DeviceEntity deviceEntity)
{
_repository.Upsert(deviceEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.Update(deviceEntity);
dbContext.SaveChanges();
}
public void Save(IEnumerable<DeviceEntity> deviceEntities)
public void SaveRange(IEnumerable<DeviceEntity> deviceEntities)
{
_repository.Upsert(deviceEntities);
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(deviceEntities);
dbContext.SaveChanges();
}
}
@@ -1,54 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.Workshop;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories;
internal class EntryRepository : IEntryRepository
internal class EntryRepository(Func<ArtemisDbContext> getContext) : IEntryRepository
{
private readonly LiteRepository _repository;
public EntryRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<EntryEntity>().EnsureIndex(s => s.Id);
_repository.Database.GetCollection<EntryEntity>().EnsureIndex(s => s.EntryId);
}
public void Add(EntryEntity entryEntity)
{
_repository.Insert(entryEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.Entries.Add(entryEntity);
dbContext.SaveChanges();
}
public void Remove(EntryEntity entryEntity)
{
_repository.Delete<EntryEntity>(entryEntity.Id);
using ArtemisDbContext dbContext = getContext();
dbContext.Entries.Remove(entryEntity);
dbContext.SaveChanges();
}
public EntryEntity? Get(Guid id)
{
return _repository.FirstOrDefault<EntryEntity>(s => s.Id == id);
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries.FirstOrDefault(s => s.Id == id);
}
public EntryEntity? GetByEntryId(long entryId)
{
return _repository.FirstOrDefault<EntryEntity>(s => s.EntryId == entryId);
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries.FirstOrDefault(s => s.EntryId == entryId);
}
public List<EntryEntity> GetAll()
{
return _repository.Query<EntryEntity>().ToList();
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries.ToList();
}
public void Save(EntryEntity entryEntity)
{
_repository.Upsert(entryEntity);
}
public void Save(IEnumerable<EntryEntity> entryEntities)
{
_repository.Upsert(entryEntities);
using ArtemisDbContext dbContext = getContext();
dbContext.Update(entryEntity);
dbContext.SaveChanges();
}
}
@@ -10,5 +10,5 @@ public interface IDeviceRepository : IRepository
DeviceEntity? Get(string id);
List<DeviceEntity> GetAll();
void Save(DeviceEntity deviceEntity);
void Save(IEnumerable<DeviceEntity> deviceEntities);
void SaveRange(IEnumerable<DeviceEntity> deviceEntities);
}
@@ -12,5 +12,4 @@ public interface IEntryRepository : IRepository
EntryEntity? GetByEntryId(long entryId);
List<EntryEntity> GetAll();
void Save(EntryEntity entryEntity);
void Save(IEnumerable<EntryEntity> entryEntities);
}
@@ -5,13 +5,9 @@ namespace Artemis.Storage.Repositories.Interfaces;
public interface IPluginRepository : IRepository
{
void AddPlugin(PluginEntity pluginEntity);
PluginEntity? GetPluginByGuid(Guid pluginGuid);
void SavePlugin(PluginEntity pluginEntity);
void AddSetting(PluginSettingEntity pluginSettingEntity);
PluginSettingEntity? GetSettingByGuid(Guid pluginGuid);
PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid);
PluginEntity? GetPluginByPluginGuid(Guid pluginGuid);
void SaveSetting(PluginSettingEntity pluginSettingEntity);
void SavePlugin(PluginEntity pluginEntity);
PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid);
void RemoveSettings(Guid pluginGuid);
}
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Storage.Repositories.Interfaces;
@@ -11,8 +10,7 @@ public interface IProfileCategoryRepository : IRepository
void Remove(ProfileCategoryEntity profileCategoryEntity);
List<ProfileCategoryEntity> GetAll();
ProfileCategoryEntity? Get(Guid id);
Stream? GetProfileIconStream(Guid id);
void SaveProfileIconStream(ProfileConfigurationEntity profileConfigurationEntity, Stream stream);
ProfileCategoryEntity IsUnique(string name, Guid? id);
bool IsUnique(string name, Guid? id);
void Save(ProfileCategoryEntity profileCategoryEntity);
void SaveRange(List<ProfileCategoryEntity> profileCategoryEntities);
}
@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Storage.Repositories.Interfaces;
public interface IProfileRepository : IRepository
{
void Add(ProfileEntity profileEntity);
void Remove(ProfileEntity profileEntity);
List<ProfileEntity> GetAll();
ProfileEntity? Get(Guid id);
void Save(ProfileEntity profileEntity);
void Add(ProfileContainerEntity profileContainerEntity);
void Remove(ProfileContainerEntity profileContainerEntity);
void Save(ProfileContainerEntity profileContainerEntity);
void SaveRange(List<ProfileContainerEntity> profileContainerEntities);
void MigrateProfiles();
void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson);
}
@@ -1,14 +0,0 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.General;
namespace Artemis.Storage.Repositories.Interfaces;
public interface IQueuedActionRepository : IRepository
{
void Add(QueuedActionEntity queuedActionEntity);
void Remove(QueuedActionEntity queuedActionEntity);
List<QueuedActionEntity> GetAll();
List<QueuedActionEntity> GetByType(string type);
bool IsTypeQueued(string type);
void ClearByType(string type);
}
@@ -0,0 +1,9 @@
using Artemis.Storage.Entities.General;
namespace Artemis.Storage.Repositories.Interfaces;
public interface IReleaseRepository : IRepository
{
bool SaveVersionInstallDate(string version);
ReleaseEntity? GetPreviousInstalledVersion();
}
@@ -1,59 +1,44 @@
using System;
using System.Linq;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories;
internal class PluginRepository : IPluginRepository
internal class PluginRepository(Func<ArtemisDbContext> getContext) : IPluginRepository
{
private readonly LiteRepository _repository;
public PluginRepository(LiteRepository repository)
public PluginEntity? GetPluginByPluginGuid(Guid pluginGuid)
{
_repository = repository;
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
}
public void AddPlugin(PluginEntity pluginEntity)
{
_repository.Insert(pluginEntity);
}
public PluginEntity? GetPluginByGuid(Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginEntity>(p => p.Id == pluginGuid);
}
public void SavePlugin(PluginEntity pluginEntity)
{
_repository.Upsert(pluginEntity);
}
public void AddSetting(PluginSettingEntity pluginSettingEntity)
{
_repository.Insert(pluginSettingEntity);
}
public PluginSettingEntity? GetSettingByGuid(Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginSettingEntity>(p => p.PluginGuid == pluginGuid);
using ArtemisDbContext dbContext = getContext();
return dbContext.Plugins.Include(p => p.Features).FirstOrDefault(p => p.PluginGuid == pluginGuid);
}
public PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid)
{
return _repository.FirstOrDefault<PluginSettingEntity>(p => p.Name == name && p.PluginGuid == pluginGuid);
using ArtemisDbContext dbContext = getContext();
return dbContext.PluginSettings.FirstOrDefault(p => p.Name == name && p.PluginGuid == pluginGuid);
}
public void SaveSetting(PluginSettingEntity pluginSettingEntity)
{
_repository.Upsert(pluginSettingEntity);
}
/// <inheritdoc />
public void RemoveSettings(Guid pluginGuid)
{
_repository.DeleteMany<PluginSettingEntity>(s => s.PluginGuid == pluginGuid);
using ArtemisDbContext dbContext = getContext();
dbContext.PluginSettings.RemoveRange(dbContext.PluginSettings.Where(s => s.PluginGuid == pluginGuid));
dbContext.SaveChanges();
}
public void SaveSetting(PluginSettingEntity pluginSettingEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.PluginSettings.Update(pluginSettingEntity);
dbContext.SaveChanges();
}
public void SavePlugin(PluginEntity pluginEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.Update(pluginEntity);
dbContext.SaveChanges();
}
}
@@ -1,93 +1,75 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories;
internal class ProfileCategoryRepository : IProfileCategoryRepository
internal class ProfileCategoryRepository(Func<ArtemisDbContext> getContext, IProfileRepository profileRepository) : IProfileCategoryRepository
{
private readonly ILiteStorage<Guid> _profileIcons;
private readonly LiteRepository _repository;
public ProfileCategoryRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<ProfileCategoryEntity>().EnsureIndex(s => s.Name, true);
_profileIcons = _repository.Database.GetStorage<Guid>("profileIcons");
}
private bool _migratedProfiles;
public void Add(ProfileCategoryEntity profileCategoryEntity)
{
_repository.Insert(profileCategoryEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileCategories.Add(profileCategoryEntity);
dbContext.SaveChanges();
}
public void Remove(ProfileCategoryEntity profileCategoryEntity)
{
_repository.Delete<ProfileCategoryEntity>(profileCategoryEntity.Id);
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileCategories.Remove(profileCategoryEntity);
dbContext.SaveChanges();
}
public List<ProfileCategoryEntity> GetAll()
{
List<ProfileCategoryEntity> categories = _repository.Query<ProfileCategoryEntity>().ToList();
// Update all profile versions to the current version, profile migrations don't apply to LiteDB so anything loadable is assumed to be up to date
foreach (ProfileCategoryEntity profileCategoryEntity in categories)
UpdateProfileVersions(profileCategoryEntity);
if (!_migratedProfiles)
{
profileRepository.MigrateProfiles();
_migratedProfiles = true;
}
return categories;
using ArtemisDbContext dbContext = getContext();
return dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).ToList();
}
public ProfileCategoryEntity? Get(Guid id)
{
ProfileCategoryEntity? result = _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Id == id);
if (result == null)
return null;
if (!_migratedProfiles)
{
profileRepository.MigrateProfiles();
_migratedProfiles = true;
}
// Update all profile versions to the current version, profile migrations don't apply to LiteDB so anything loadable is assumed to be up to date
UpdateProfileVersions(result);
return result;
}
public ProfileCategoryEntity IsUnique(string name, Guid? id)
{
name = name.Trim();
if (id == null)
return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name);
return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name && p.Id != id.Value);
using ArtemisDbContext dbContext = getContext();
return dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).FirstOrDefault(c => c.Id == id);
}
public void Save(ProfileCategoryEntity profileCategoryEntity)
{
_repository.Upsert(profileCategoryEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.Update(profileCategoryEntity);
dbContext.SaveChanges();
}
public Stream? GetProfileIconStream(Guid id)
public void SaveRange(List<ProfileCategoryEntity> profileCategoryEntities)
{
if (!_profileIcons.Exists(id))
return null;
MemoryStream stream = new();
_profileIcons.Download(id, stream);
return stream;
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(profileCategoryEntities);
dbContext.SaveChanges();
}
public void SaveProfileIconStream(ProfileConfigurationEntity profileConfigurationEntity, Stream stream)
public bool IsUnique(string name, Guid? id)
{
if (profileConfigurationEntity.FileIconId == Guid.Empty)
profileConfigurationEntity.FileIconId = Guid.NewGuid();
using ArtemisDbContext dbContext = getContext();
if (stream == null && _profileIcons.Exists(profileConfigurationEntity.FileIconId))
_profileIcons.Delete(profileConfigurationEntity.FileIconId);
_profileIcons.Upload(profileConfigurationEntity.FileIconId, profileConfigurationEntity.FileIconId + ".png", stream);
}
private static void UpdateProfileVersions(ProfileCategoryEntity profileCategoryEntity)
{
foreach (ProfileConfigurationEntity profileConfigurationEntity in profileCategoryEntity.ProfileConfigurations)
profileConfigurationEntity.Version = StorageMigrationService.PROFILE_VERSION;
name = name.Trim();
return id == null
? dbContext.ProfileCategories.Any(p => p.Name == name)
: dbContext.ProfileCategories.Any(p => p.Name == name && p.Id != id.Value);
}
}
@@ -1,43 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using Artemis.Storage.Entities;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Exceptions;
using Artemis.Storage.Migrations;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
using Microsoft.EntityFrameworkCore;
using Serilog;
namespace Artemis.Storage.Repositories;
internal class ProfileRepository : IProfileRepository
public class ProfileRepository(ILogger logger, Func<ArtemisDbContext> getContext, List<IProfileMigration> profileMigrators) : IProfileRepository
{
private readonly LiteRepository _repository;
public ProfileRepository(LiteRepository repository)
public void Add(ProfileContainerEntity profileContainerEntity)
{
_repository = repository;
_repository.Database.GetCollection<ProfileEntity>().EnsureIndex(s => s.Name);
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileContainers.Add(profileContainerEntity);
dbContext.SaveChanges();
}
public void Add(ProfileEntity profileEntity)
public void Remove(ProfileContainerEntity profileContainerEntity)
{
_repository.Insert(profileEntity);
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileContainers.Remove(profileContainerEntity);
dbContext.SaveChanges();
}
public void Remove(ProfileEntity profileEntity)
public void Save(ProfileContainerEntity profileContainerEntity)
{
_repository.Delete<ProfileEntity>(profileEntity.Id);
using ArtemisDbContext dbContext = getContext();
dbContext.Update(profileContainerEntity);
dbContext.SaveChanges();
}
public List<ProfileEntity> GetAll()
public void SaveRange(List<ProfileContainerEntity> profileContainerEntities)
{
return _repository.Query<ProfileEntity>().ToList();
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(profileContainerEntities);
dbContext.SaveChanges();
}
public ProfileEntity? Get(Guid id)
public void MigrateProfiles()
{
return _repository.FirstOrDefault<ProfileEntity>(p => p.Id == id);
using ArtemisDbContext dbContext = getContext();
int max = profileMigrators.Max(m => m.Version);
// Query the ProfileContainerEntity table directly, grabbing the ID, profile, and configuration
List<RawProfileContainer> containers = dbContext.Database
.SqlQueryRaw<RawProfileContainer>("SELECT Id, Profile, ProfileConfiguration FROM ProfileContainers WHERE json_extract(ProfileConfiguration, '$.Version') < {0}", max)
.ToList();
foreach (RawProfileContainer rawProfileContainer in containers)
{
try
{
JsonObject? profileConfiguration = JsonNode.Parse(rawProfileContainer.ProfileConfiguration)?.AsObject();
JsonObject? profile = JsonNode.Parse(rawProfileContainer.Profile)?.AsObject();
if (profileConfiguration == null || profile == null)
{
logger.Error("Failed to parse profile or profile configuration of profile container {Id}", rawProfileContainer.Id);
continue;
}
MigrateProfile(profileConfiguration, profile);
rawProfileContainer.Profile = profile.ToString();
rawProfileContainer.ProfileConfiguration = profileConfiguration.ToString();
// Write the updated containers back to the database
dbContext.Database.ExecuteSqlRaw(
"UPDATE ProfileContainers SET Profile = {0}, ProfileConfiguration = {1} WHERE Id = {2}",
rawProfileContainer.Profile,
rawProfileContainer.ProfileConfiguration,
rawProfileContainer.Id);
}
catch (Exception e)
{
logger.Error(e, "Failed to migrate profile container {Id}", rawProfileContainer.Id);
}
}
}
public void Save(ProfileEntity profileEntity)
public void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson)
{
_repository.Upsert(profileEntity);
if (configurationJson == null || profileJson == null)
return;
configurationJson["Version"] ??= 0;
foreach (IProfileMigration profileMigrator in profileMigrators.OrderBy(m => m.Version))
{
if (profileMigrator.Version <= configurationJson["Version"]!.GetValue<int>())
continue;
logger.Information("Migrating profile '{Name}' from version {OldVersion} to {NewVersion}", configurationJson["Name"], configurationJson["Version"], profileMigrator.Version);
profileMigrator.Migrate(configurationJson, profileJson);
configurationJson["Version"] = profileMigrator.Version;
}
}
}
@@ -1,57 +0,0 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.General;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories;
public class QueuedActionRepository : IQueuedActionRepository
{
private readonly LiteRepository _repository;
public QueuedActionRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<QueuedActionEntity>().EnsureIndex(s => s.Type);
}
#region Implementation of IQueuedActionRepository
/// <inheritdoc />
public void Add(QueuedActionEntity queuedActionEntity)
{
_repository.Insert(queuedActionEntity);
}
/// <inheritdoc />
public void Remove(QueuedActionEntity queuedActionEntity)
{
_repository.Delete<QueuedActionEntity>(queuedActionEntity.Id);
}
/// <inheritdoc />
public List<QueuedActionEntity> GetAll()
{
return _repository.Query<QueuedActionEntity>().ToList();
}
/// <inheritdoc />
public List<QueuedActionEntity> GetByType(string type)
{
return _repository.Query<QueuedActionEntity>().Where(q => q.Type == type).ToList();
}
/// <inheritdoc />
public bool IsTypeQueued(string type)
{
return _repository.Query<QueuedActionEntity>().Where(q => q.Type == type).Count() > 0;
}
/// <inheritdoc />
public void ClearByType(string type)
{
_repository.DeleteMany<QueuedActionEntity>(q => q.Type == type);
}
#endregion
}
@@ -1,38 +1,28 @@
using System;
using System.Linq;
using Artemis.Storage.Entities.General;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories;
public class ReleaseRepository : IReleaseRepository
public class ReleaseRepository(Func<ArtemisDbContext> getContext) : IReleaseRepository
{
private readonly LiteRepository _repository;
public ReleaseRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<ReleaseEntity>().EnsureIndex(s => s.Version, true);
}
public bool SaveVersionInstallDate(string version)
{
ReleaseEntity release = _repository.Query<ReleaseEntity>().Where(r => r.Version == version).FirstOrDefault();
using ArtemisDbContext dbContext = getContext();
ReleaseEntity? release = dbContext.Releases.FirstOrDefault(r => r.Version == version);
if (release != null)
return false;
_repository.Insert(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow});
dbContext.Releases.Add(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow});
dbContext.SaveChanges();
return true;
}
public ReleaseEntity GetPreviousInstalledVersion()
public ReleaseEntity? GetPreviousInstalledVersion()
{
return _repository.Query<ReleaseEntity>().OrderByDescending(r => r.InstalledAt).Skip(1).FirstOrDefault();
using ArtemisDbContext dbContext = getContext();
return dbContext.Releases.AsEnumerable().OrderByDescending(r => r.InstalledAt).Skip(1).FirstOrDefault();
}
}
public interface IReleaseRepository : IRepository
{
bool SaveVersionInstallDate(string version);
ReleaseEntity GetPreviousInstalledVersion();
}
+16 -21
View File
@@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Linq;
using LiteDB;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage;
public static class StorageManager
{
private static bool _ranMigrations;
private static bool _inUse;
/// <summary>
@@ -19,7 +20,7 @@ public static class StorageManager
if (_inUse)
throw new Exception("Storage is already in use, can't backup now.");
string database = Path.Combine(dataFolder, "database.db");
string database = Path.Combine(dataFolder, "artemis.db");
if (!File.Exists(database))
return;
@@ -36,27 +37,21 @@ public static class StorageManager
oldest.Delete();
}
File.Copy(database, Path.Combine(backupFolder, $"database-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db"));
File.Copy(database, Path.Combine(backupFolder, $"artemis-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db"));
}
/// <summary>
/// Creates the LiteRepository that will be managed by dependency injection
/// </summary>
/// <param name="dataFolder">The Artemis data folder</param>
public static LiteRepository CreateRepository(string dataFolder)
public static ArtemisDbContext CreateDbContext(string dataFolder)
{
if (_inUse)
throw new Exception("Storage is already in use, use dependency injection to get the repository.");
_inUse = true;
try
{
_inUse = true;
return new LiteRepository($"FileName={Path.Combine(dataFolder, "database.db")}");
}
catch (LiteException e)
{
// I don't like this way of error reporting, now I need to use reflection if I want a meaningful error message
throw new Exception($"LiteDB threw error code {e.ErrorCode}. See inner exception for more details", e);
}
ArtemisDbContext dbContext = new() {DataFolder = dataFolder};
if (_ranMigrations)
return dbContext;
dbContext.Database.Migrate();
dbContext.Database.ExecuteSqlRaw("PRAGMA optimize");
_ranMigrations = true;
return dbContext;
}
}
@@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Migrations;
using LiteDB;
using Serilog;
namespace Artemis.Storage;
public class StorageMigrationService
{
public const int PROFILE_VERSION = 4;
private readonly ILogger _logger;
private readonly IList<IStorageMigration> _migrations;
private readonly LiteRepository _repository;
public StorageMigrationService(ILogger logger, LiteRepository repository, IList<IStorageMigration> migrations)
{
_logger = logger;
_repository = repository;
_migrations = migrations;
ApplyPendingMigrations();
}
public void ApplyPendingMigrations()
{
foreach (IStorageMigration storageMigration in _migrations.OrderBy(m => m.UserVersion))
{
if (_repository.Database.UserVersion >= storageMigration.UserVersion)
continue;
_logger.Information("Applying storage migration {storageMigration} to update DB from v{oldVersion} to v{newVersion}",
storageMigration.GetType().Name, _repository.Database.UserVersion, storageMigration.UserVersion);
_repository.Database.BeginTrans();
try
{
storageMigration.Apply(_repository);
}
catch (Exception)
{
_repository.Database.Rollback();
throw;
}
_repository.Database.Commit();
_repository.Database.UserVersion = storageMigration.UserVersion;
}
}
}