diff --git a/src/Artemis.Storage/Migrations/Profile/M0003SystemTextJson.cs b/src/Artemis.Storage/Migrations/Profile/M0003SystemTextJson.cs index b48cf3b1e..47771f2cb 100644 --- a/src/Artemis.Storage/Migrations/Profile/M0003SystemTextJson.cs +++ b/src/Artemis.Storage/Migrations/Profile/M0003SystemTextJson.cs @@ -18,7 +18,7 @@ internal class M0003SystemTextJson : IProfileMigration ConvertToSystemTextJson(profileJson); } - private void ConvertToSystemTextJson(JsonObject jsonObject) + internal static void ConvertToSystemTextJson(JsonObject jsonObject) { FilterType(jsonObject); @@ -52,7 +52,7 @@ internal class M0003SystemTextJson : IProfileMigration } } - private void FilterType(JsonObject jsonObject) + internal static void FilterType(JsonObject jsonObject) { // Replace or remove $type depending on whether there's a matching JsonDerivedType // This could be done with reflection but that would mean this migration automatically gains new behaviour over time. diff --git a/src/Artemis.Storage/Migrations/Profile/M0004NodeStorage.cs b/src/Artemis.Storage/Migrations/Profile/M0004NodeStorage.cs new file mode 100644 index 000000000..e985ace93 --- /dev/null +++ b/src/Artemis.Storage/Migrations/Profile/M0004NodeStorage.cs @@ -0,0 +1,138 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Artemis.Storage.Migrations.Profile +{ + /// + /// Migrates nodes to be provider-based. + /// This requires giving them a ProviderId and updating the their namespaces to match the namespace of the new plugin. + /// + internal class M0004NodeStorage : IProfileMigration + { + /// + public int Version => 4; + + /// + public void Migrate(JsonObject configurationJson, JsonObject profileJson) + { + MigrateNodeScript(configurationJson["ActivationCondition"]); + + JsonArray? folders = profileJson["Folders"]?.AsArray(); + JsonArray? layers = profileJson["Layers"]?.AsArray(); + + if (folders != null) + { + foreach (JsonNode? folder in folders) + MigrateProfileElement(folder); + } + + if (layers != null) + { + foreach (JsonNode? layer in layers) + { + MigrateProfileElement(layer); + MigratePropertyGroup(layer?["GeneralPropertyGroup"]); + MigratePropertyGroup(layer?["TransformPropertyGroup"]); + MigratePropertyGroup(layer?["LayerBrush"]?["PropertyGroup"]); + } + } + } + + private void MigrateProfileElement(JsonNode? profileElement) + { + if (profileElement == null) + return; + + JsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray(); + if (layerEffects != null) + { + foreach (JsonNode? layerEffect in layerEffects) + MigratePropertyGroup(layerEffect?["PropertyGroup"]); + } + + JsonNode? displayCondition = profileElement["DisplayCondition"]; + if (displayCondition != null) + MigrateNodeScript(displayCondition["Script"]); + } + + private void MigratePropertyGroup(JsonNode? propertyGroup) + { + if (propertyGroup == null) + return; + + JsonArray? properties = propertyGroup["Properties"]?.AsArray(); + JsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray(); + if (properties != null) + { + foreach (JsonNode? property in properties) + MigrateNodeScript(property?["DataBinding"]?["NodeScript"]); + } + + if (propertyGroups != null) + { + foreach (JsonNode? childPropertyGroup in propertyGroups) + MigratePropertyGroup(childPropertyGroup); + } + } + + private void MigrateNodeScript(JsonNode? nodeScript) + { + JsonArray? nodes = nodeScript?["Nodes"]?.AsArray(); + if (nodes == null) + return; + + foreach (JsonNode? jsonNode in nodes) + { + if (jsonNode == null) + continue; + + JsonObject nodeObject = jsonNode.AsObject(); + nodeObject["Storage"] = MigrateNodeStorageJson(nodeObject["Storage"]?.GetValue()); + } + } + + internal static string? MigrateNodeStorageJson(string? json) + { + if (string.IsNullOrEmpty(json)) + return json; + + try + { + JsonDocument jsonDocument = JsonDocument.Parse(json); + if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object) + return json; + + JsonObject? jsonObject = JsonObject.Create(jsonDocument.RootElement); + if (jsonObject == null) + return json; + + if (jsonObject["$type"] != null && jsonObject["$values"] != null) + { + JsonArray? values = jsonObject["$values"]?.AsArray(); + if (values != null) + { + foreach (JsonNode? jsonNode in values.ToList()) + { + if (jsonNode is JsonObject childObject) + M0003SystemTextJson.ConvertToSystemTextJson(childObject); + } + + return values.ToJsonString(); + } + } + else + { + M0003SystemTextJson.ConvertToSystemTextJson(jsonObject); + } + + return jsonObject.ToJsonString(); + } + catch (Exception e) + { + return json; + } + } + } +} diff --git a/src/Artemis.Storage/Migrations/Storage/M0026NodeStorage.cs b/src/Artemis.Storage/Migrations/Storage/M0026NodeStorage.cs new file mode 100644 index 000000000..e454cf889 --- /dev/null +++ b/src/Artemis.Storage/Migrations/Storage/M0026NodeStorage.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using Artemis.Storage.Migrations.Profile; +using LiteDB; + +namespace Artemis.Storage.Migrations.Storage; + +public class M0026NodeStorage : IStorageMigration +{ + public int UserVersion => 26; + + public void Apply(LiteRepository repository) + { + ILiteCollection categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity"); + List 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 collection = repository.Database.GetCollection("ProfileEntity"); + List 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); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage/StorageMigrationService.cs b/src/Artemis.Storage/StorageMigrationService.cs index 1d726ac8f..eca2a7dde 100644 --- a/src/Artemis.Storage/StorageMigrationService.cs +++ b/src/Artemis.Storage/StorageMigrationService.cs @@ -9,7 +9,7 @@ namespace Artemis.Storage; public class StorageMigrationService { - public const int PROFILE_VERSION = 3; + public const int PROFILE_VERSION = 4; private readonly ILogger _logger; private readonly IList _migrations;