Просмотр исходного кода

fixup! Add DocMigrator to handle deserialization and migrations

Signed-off-by: Stephen Reaves <reaves735@gmail.com>
Stephen Reaves 1 месяц назад
Родитель
Сommit
bed4bb96d2

+ 38 - 46
RackPeek.Domain/Persistence/Yaml/YamlResourceCollection.cs

@@ -27,10 +27,45 @@ public class ResourceCollection
     public List<Resource> Resources { get; } = new();
 }
 
+public class RackPeekConfigMigrationDeserializer : YamlMigrationDeserializer<YamlRoot>
+{
+    public RackPeekConfigMigrationDeserializer(IServiceProvider serviceProvider,
+            ILogger<YamlMigrationDeserializer<YamlRoot>> logger,
+            DeserializerBuilder deserializerBuilder,
+            SerializerBuilder serializerBuilder) :
+        base(serviceProvider, logger, new List<Func<IServiceProvider, Dictionary<object,object>, ValueTask>>{
+                // List migrations here
+                EnsureSchemaVersionExists,
+            },
+            deserializerBuilder, serializerBuilder) {}
+
+    #region Migrations
+
+    // Define migration functions here
+
+    public static ValueTask EnsureSchemaVersionExists(IServiceProvider serviceProvider, Dictionary<object, object> obj)
+    {
+        if (!obj.ContainsKey("schemaVersion"))
+        {
+            obj["schemaVersion"] = 0;
+            if (obj.ContainsKey("version"))
+            {
+                obj["schemaVersion"] = obj["version"];
+            }
+        }
+
+        obj.Remove("version");
+        return ValueTask.CompletedTask;
+    }
+
+    #endregion
+}
+
 public sealed class YamlResourceCollection(
     string filePath,
     ITextFileStore fileStore,
-    ResourceCollection resourceCollection)
+    ResourceCollection resourceCollection,
+    RackPeekConfigMigrationDeserializer _deserializer)
     : IResourceCollection
 {
     // Bump this when your YAML schema changes, and add a migration step below.
@@ -132,7 +167,8 @@ public sealed class YamlResourceCollection(
         {
             await BackupOriginalAsync(yaml);
 
-            root = await MigrateAsync(root);
+            root = await _deserializer.Deserialize(yaml);
+            // root = await MigrateAsync(root);
 
             // Ensure we persist the migrated root (with updated version)
             await SaveRootAsync(root);
@@ -357,50 +393,6 @@ public sealed class YamlResourceCollection(
         return map;
     }
 
-    public YamlMigrator Setup(DeserializerBuilder deserializerBuilder)
-    {
-        var services = new ServiceCollection();
-        services.AddLogging();
-        services.AddYamlMigrator(Assembly.GetExecutingAssembly());
-        var scope = services.BuildServiceProvider().CreateScope();
-        return scope.ServiceProvider.GetRequiredService<YamlMigrator>();
-        // return new YamlRootDeserializer(scope.ServiceProvider, scope.ServiceProvider.GetRequiredService<ILogger<YamlRootDeserializer>>(), deserializerBuilder);
-    }
-
-    // TODO: Wrap this in an 'instance' so we don't needlessly rebuild this?
-    //       Similar to CamelCaseNamingConvention.instance
-    public class YamlRootDeserializer : YamlMigrationDeserializer<YamlRoot>
-    {
-        public YamlRootDeserializer(IServiceProvider serviceProvider,
-                ILogger<YamlRootDeserializer> logger,
-                DeserializerBuilder deserializerBuilder) :
-            base(serviceProvider, logger, new List<Func<IServiceProvider, Dictionary<object,object>, ValueTask>>{
-                    // List migrations here
-                    EnsureSchemaVersionExists,
-                },
-                deserializerBuilder) {}
-
-        #region Migrations
-
-        // Define migration functions here
-
-        public static ValueTask EnsureSchemaVersionExists(IServiceProvider serviceProvider, Dictionary<object, object> obj)
-        {
-            if (!obj.ContainsKey("schemaVersion"))
-            {
-                obj["schemaVersion"] = 0;
-                if (obj.ContainsKey("version"))
-                {
-                    obj["schemaVersion"] = obj["version"];
-                }
-            }
-
-            obj.Remove("version");
-            return ValueTask.CompletedTask;
-        }
-
-        #endregion
-    }
 }
 
 public class YamlRoot

+ 25 - 2
RackPeek.Web.Viewer/Program.cs

@@ -5,6 +5,9 @@ using RackPeek.Domain;
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence.Yaml;
 using Shared.Rcl;
+using DocMigrator.Yaml;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
 
 namespace RackPeek.Web.Viewer;
 
@@ -34,6 +37,25 @@ public class Program
         var resources = new ResourceCollection();
         builder.Services.AddSingleton(resources);
 
+        builder.Services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
+
         var yamlDir = builder.Configuration.GetValue<string>("RPK_YAML_DIR") ?? "config";
         var yamlFilePath = $"{yamlDir}/config.yaml";
 
@@ -41,7 +63,8 @@ public class Program
             new YamlResourceCollection(
                 yamlFilePath,
                 sp.GetRequiredService<ITextFileStore>(),
-                sp.GetRequiredService<ResourceCollection>()));
+                sp.GetRequiredService<ResourceCollection>(),
+                sp.GetRequiredService<RackPeekConfigMigrationDeserializer>()));
 
         builder.Services.AddYamlRepos();
         builder.Services.AddCommands();
@@ -53,4 +76,4 @@ public class Program
 
         await builder.Build().RunAsync();
     }
-}
+}

+ 24 - 2
RackPeek.Web/Program.cs

@@ -4,6 +4,9 @@ using RackPeek.Domain;
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence.Yaml;
 using RackPeek.Web.Components;
+using DocMigrator.Yaml;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
 using Shared.Rcl;
 
 namespace RackPeek.Web;
@@ -45,6 +48,24 @@ public class Program
 
         builder.Services.AddScoped<ITextFileStore, PhysicalTextFileStore>();
 
+        builder.Services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
 
         builder.Services.AddScoped(sp =>
         {
@@ -63,7 +84,8 @@ public class Program
             new YamlResourceCollection(
                 yamlFilePath,
                 sp.GetRequiredService<ITextFileStore>(),
-                sp.GetRequiredService<ResourceCollection>()));
+                sp.GetRequiredService<ResourceCollection>(),
+                sp.GetRequiredService<RackPeekConfigMigrationDeserializer>()));
 
         // Infrastructure
         builder.Services.AddYamlRepos();
@@ -106,4 +128,4 @@ public class Program
         var app = await BuildApp(builder);
         await app.RunAsync();
     }
-}
+}

+ 28 - 3
Shared.Rcl/CliBootstrap.cs

@@ -1,4 +1,8 @@
 using System.ComponentModel.DataAnnotations;
+using DocMigrator.Yaml;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using RackPeek.Domain;
@@ -68,10 +72,31 @@ public static class CliBootstrap
 
         if (!File.Exists(fullYamlPath)) await File.WriteAllTextAsync(fullYamlPath, "");
 
+        services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
+
         var collection = new YamlResourceCollection(
             fullYamlPath,
             new PhysicalTextFileStore(),
-            new ResourceCollection());
+            new ResourceCollection(),
+            // TODO: Is this right?
+            services.BuildServiceProvider().GetRequiredService<RackPeekConfigMigrationDeserializer>());
 
         await collection.LoadAsync();
         services.AddSingleton<IResourceCollection>(collection);
@@ -492,7 +517,7 @@ public static class CliBootstrap
         });
     }
 
-    private static int HandleException(Exception ex, ITypeResolver? arg2)
+    private static int HandleException(Exception ex, Spectre.Console.Cli.ITypeResolver? arg2)
     {
         switch (ex)
         {
@@ -554,4 +579,4 @@ public static class CliBootstrap
             _showingHelp = false;
         }
     }
-}
+}

+ 30 - 2
Tests/Yaml/HardwareDeserializationTests.cs

@@ -9,6 +9,12 @@ using RackPeek.Domain.Resources.Routers;
 using RackPeek.Domain.Resources.Servers;
 using RackPeek.Domain.Resources.Switches;
 using RackPeek.Domain.Resources.UpsUnits;
+using DocMigrator.Yaml;
+using System.Reflection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
 
 namespace Tests.Yaml;
 
@@ -26,8 +32,30 @@ public class HardwareDeserializationTests
         var filePath = Path.Combine(tempDir, "config.yaml");
         await File.WriteAllTextAsync(filePath, yaml);
 
+        var services = new ServiceCollection();
+        services.AddLogging();
+        services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
+        var scope = services.BuildServiceProvider().CreateScope();
+
         var yamlResourceCollection =
-            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection());
+            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection(), scope.ServiceProvider.GetRequiredService<RackPeekConfigMigrationDeserializer>());
         await yamlResourceCollection.LoadAsync();
         return yamlResourceCollection;
     }
@@ -475,4 +503,4 @@ resources:
         Assert.Equal("Volta", ups.Model);
         Assert.Equal(2200, ups.Va);
     }
-}
+}

+ 30 - 2
Tests/Yaml/ServiceDeserializationTests.cs

@@ -1,6 +1,12 @@
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence.Yaml;
 using RackPeek.Domain.Resources.Services;
+using System.Reflection;
+using DocMigrator.Yaml;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
 
 namespace Tests.Yaml;
 
@@ -18,8 +24,30 @@ public class ServiceDeserializationTests
         var filePath = Path.Combine(tempDir, "config.yaml");
         await File.WriteAllTextAsync(filePath, yaml);
 
+        var services = new ServiceCollection();
+        services.AddLogging();
+        services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
+        var scope = services.BuildServiceProvider().CreateScope();
+
         var yamlResourceCollection =
-            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection());
+            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection(), scope.ServiceProvider.GetRequiredService<RackPeekConfigMigrationDeserializer>());
         await yamlResourceCollection.LoadAsync();
 
         return yamlResourceCollection;
@@ -61,4 +89,4 @@ resources:
         Assert.Equal("TCP", service.Network.Protocol);
         Assert.Equal("http://immich.lan:8080", service.Network.Url);
     }
-}
+}

+ 30 - 2
Tests/Yaml/SystemDeserializationTests.cs

@@ -1,6 +1,12 @@
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence.Yaml;
 using RackPeek.Domain.Resources.SystemResources;
+using System.Reflection;
+using DocMigrator.Yaml;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
 
 namespace Tests.Yaml;
 
@@ -18,8 +24,30 @@ public class SystemDeserializationTests
         var filePath = Path.Combine(tempDir, "config.yaml");
         await File.WriteAllTextAsync(filePath, yaml);
 
+        var services = new ServiceCollection();
+        services.AddLogging();
+        services.AddSingleton<RackPeekConfigMigrationDeserializer>(sp => 
+        {
+            var logger = sp.GetRequiredService<ILogger<YamlMigrationDeserializer<YamlRoot>>>();
+
+            // TODO: Add options
+            var deserializerBuilder = new DeserializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .IgnoreUnmatchedProperties();
+
+            var serializerBuilder = new SerializerBuilder()
+                .WithNamingConvention(CamelCaseNamingConvention.Instance);
+
+            return new RackPeekConfigMigrationDeserializer(
+                    sp,
+                    logger,
+                    deserializerBuilder,
+                    serializerBuilder);
+        });
+        var scope = services.BuildServiceProvider().CreateScope();
+
         var yamlResourceCollection =
-            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection());
+            new YamlResourceCollection(filePath, new PhysicalTextFileStore(), new ResourceCollection(), scope.ServiceProvider.GetRequiredService<RackPeekConfigMigrationDeserializer>());
         await yamlResourceCollection.LoadAsync();
 
 
@@ -68,4 +96,4 @@ resources:
 
         Assert.Equal("dell-c6400-node-01", system.RunsOn);
     }
-}
+}