diff --git a/src/grate.core/Configuration/Folders.cs b/src/grate.core/Configuration/Folders.cs
new file mode 100644
index 00000000..e239f0b3
--- /dev/null
+++ b/src/grate.core/Configuration/Folders.cs
@@ -0,0 +1,73 @@
+namespace grate.Configuration;
+
+///
+/// Factory class for creating instances.
+///
+public static class Folders
+{
+ ///
+ /// The default .
+ ///
+ public static IFoldersConfiguration Default => FoldersConfiguration.Default();
+
+ ///
+ /// An empty .
+ ///
+ public static IFoldersConfiguration Empty => FoldersConfiguration.Empty;
+
+ ///
+ /// Folder configuration with the specified folders.
+ ///
+ /// A list of migration folders
+ /// An IFoldersConfiguration with the supplied folders
+ public static IFoldersConfiguration Create(params MigrationsFolder [] folders)
+ => new FoldersConfiguration(folders);
+
+ ///
+ /// Folder configuration with the specified folders.
+ ///
+ /// A list of migration folders
+ /// An IFoldersConfiguration with the supplied folders
+ public static IFoldersConfiguration Create(IEnumerable folders)
+ => new FoldersConfiguration(folders);
+
+ ///
+ /// Folder configuration with the specified Dictionary of folder names and folders.
+ ///
+ /// A Dictionary of folder names and migration folders
+ /// An IFoldersConfiguration with the supplied folders
+ public static IFoldersConfiguration Create(IDictionary folders)
+ => new FoldersConfiguration(folders);
+
+ ///
+ /// Folder configuration from strings, where each string is a folder configuration, as
+ /// you can specify on the command line.
+ ///
+ /// For convenience, you can specify each folder in a separate string, instead of all in one string, separated by a semicolon.
+ ///
+ /// For example:
+ ///
+ ///
+ /// Create("structure=relativePath:myCustomFolder,type:Once,connectionType:Admin",
+ /// "randomstuff=type:Once;procedures=type:Once;security=type:Once")
+ ///
+ ///
+ ///
+ /// Separate strings with folder configurations
+ /// An IFoldersConfiguration with the supplied folders
+ public static IFoldersConfiguration Create(params string[] folders)
+ => FoldersCommand.Parse(string.Join(";", folders));
+
+ ///
+ /// Folder configuration from strings, where each string is a folder configuration, as
+ /// you can specify on the command line.
+ ///
+ /// You specify multiple folders in one string, separated by a semicolon.
+ ///
+ /// For example: "structure=relativePath:myCustomFolder,type:Once,connectionType:Admin;randomstuff=type:Once;procedures=type:Once;security=type:Once"
+ ///
+ /// A semicolon separated string with all the folder configurations, as specified on the command line
+ /// An IFoldersConfiguration with the supplied folders
+ public static IFoldersConfiguration Create(string folders)
+ => FoldersCommand.Parse(folders);
+}
diff --git a/src/grate/Commands/FoldersCommand.cs b/src/grate.core/Configuration/FoldersCommand.cs
similarity index 99%
rename from src/grate/Commands/FoldersCommand.cs
rename to src/grate.core/Configuration/FoldersCommand.cs
index 2071a587..a18a7cfc 100644
--- a/src/grate/Commands/FoldersCommand.cs
+++ b/src/grate.core/Configuration/FoldersCommand.cs
@@ -1,8 +1,7 @@
using System.Reflection;
-using grate.Configuration;
using grate.Exceptions;
-namespace grate.Commands;
+namespace grate.Configuration;
internal static class FoldersCommand
{
diff --git a/src/grate.core/Configuration/GrateConfigurationBuilder.cs b/src/grate.core/Configuration/GrateConfigurationBuilder.cs
index 87de162f..5503a7ec 100644
--- a/src/grate.core/Configuration/GrateConfigurationBuilder.cs
+++ b/src/grate.core/Configuration/GrateConfigurationBuilder.cs
@@ -51,7 +51,12 @@ public GrateConfigurationBuilder WithOutputFolder(string outputFolder)
/// names of the folders and the migration type. Default is the grate default folder configuration,
/// using 'up', 'functions', 'views', 'sprocs', 'triggers', 'indexes', 'permissions' and 'after_migration'.
///
- /// A folder configuration to use. Can be DefaultConfiguration with some modifications.
+ ///
+ /// A folder configuration to use. Can be DefaultConfiguration with some modifications,
+ /// a fully customized folder set, or a slightly modified one.
+ ///
+ /// Please see the static helper class with factory methods for creating folder configurations.
+ ///
/// GrateConfigurationBuilder
public GrateConfigurationBuilder WithFolders(IFoldersConfiguration folders)
{
diff --git a/unittests/Basic_tests/CommandLineParsing/FolderConfiguration_.cs b/unittests/Basic_tests/CommandLineParsing/FolderConfiguration_.cs
index 5de4a771..ad54736b 100644
--- a/unittests/Basic_tests/CommandLineParsing/FolderConfiguration_.cs
+++ b/unittests/Basic_tests/CommandLineParsing/FolderConfiguration_.cs
@@ -20,7 +20,7 @@ public async Task Default()
var cfg = await ParseGrateConfiguration("--sqlfilesdirectory=/tmp");
_ = cfg?.SqlFilesDirectory ?? new DirectoryInfo("/tmp");
- var expected = FoldersConfiguration.Default(null);
+ var expected = Folders.Default;
var actual = cfg?.Folders;
AssertEquivalent(expected.Values, actual?.Values);
diff --git a/unittests/Basic_tests/DependencyInjection/GrateConfigurationBuilder_Factory.cs b/unittests/Basic_tests/DependencyInjection/GrateConfigurationBuilder_Factory.cs
index 673ea957..7bd60aff 100644
--- a/unittests/Basic_tests/DependencyInjection/GrateConfigurationBuilder_Factory.cs
+++ b/unittests/Basic_tests/DependencyInjection/GrateConfigurationBuilder_Factory.cs
@@ -1,4 +1,5 @@
-using FluentAssertions;
+using System.Collections.Immutable;
+using FluentAssertions;
using grate.Configuration;
using grate.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
@@ -11,7 +12,6 @@ public class GrateConfigurationBuilder_Factory
[Fact]
public void Creates_default_builder_with_non_interactive()
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
var grateConfiguration = builder.Build();
grateConfiguration.NonInteractive.Should().Be(true);
@@ -23,7 +23,6 @@ public void Creates_default_builder_with_output_folder(string outputFolder)
{
var outputDir = Directory.CreateDirectory(outputFolder);
WriteSql(Wrap(outputDir, "views"), "01_test_view.sql", "create view v_test as select 1");
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithOutputFolder(outputFolder);
var grateConfiguration = builder.Build();
@@ -38,7 +37,6 @@ public void Creates_default_builder_with_sql_folder(string sqlFolder)
{
var sqlDir = Directory.CreateDirectory(sqlFolder);
WriteSql(Wrap(sqlDir, "views"), "01_test_view.sql", "create view v_test as select 1");
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithSqlFilesDirectory(sqlFolder);
var grateConfiguration = builder.Build();
@@ -51,7 +49,6 @@ public void Creates_default_builder_with_sql_folder(string sqlFolder)
[InlineData("roundhouse")]
public void Creates_default_builder_with_schema(string schemaName)
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithSchema(schemaName);
var grateConfiguration = builder.Build();
@@ -63,7 +60,6 @@ public void Creates_default_builder_with_schema(string schemaName)
[InlineData("Data source=whatever;Database=;")]
public void Creates_default_builder_with_connection_string(string connectionString)
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithConnectionString(connectionString);
var grateConfiguration = builder.Build();
@@ -75,7 +71,6 @@ public void Creates_default_builder_with_connection_string(string connectionStri
[InlineData("Data source=whatever;Database=master;")]
public void Creates_default_builder_with_admin_connection_string(string adminConnectionString)
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithAdminConnectionString(adminConnectionString);
var grateConfiguration = builder.Build();
@@ -87,16 +82,15 @@ public void Creates_default_builder_with_admin_connection_string(string adminCon
[InlineData("1.0.0.0")]
public void Creates_default_builder_with_version(string version)
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithVersion(version);
var grateConfiguration = builder.Build();
grateConfiguration.Version.Should().Be(version);
}
+
[Fact]
public void Creates_default_builder_with_do_not_create_database()
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.DoNotCreateDatabase();
var grateConfiguration = builder.Build();
@@ -106,7 +100,6 @@ public void Creates_default_builder_with_do_not_create_database()
[Fact]
public void Creates_default_builder_with_transaction()
{
- var serviceCollection = new ServiceCollection();
var builder = GrateConfigurationBuilder.Create();
builder.WithTransaction();
var grateConfiguration = builder.Build();
@@ -128,4 +121,18 @@ public void Creates_default_builder_with_environment_name(string environmentName
grateConfiguration.Environment.Should().BeEquivalentTo(new GrateEnvironment(environmentName));
}
+ [Fact]
+ public void Creates_default_builder_with_custom_folder_configuration()
+ {
+ var builder = GrateConfigurationBuilder.Create()
+ .WithFolders(Folders.Create("up=ddl", "views=binoculars"));
+ var grateConfiguration = builder.Build();
+
+ var folders = grateConfiguration.Folders!;
+ folders.Should().HaveCount(Folders.Default.Count);
+
+ folders[KnownFolderKeys.Up]!.Path.Should().Be("ddl");
+ folders[KnownFolderKeys.Views]!.Path.Should().Be("binoculars");
+ }
+
}
diff --git a/unittests/Basic_tests/Infrastructure/FileSystem_.cs b/unittests/Basic_tests/Infrastructure/FileSystem_.cs
index 5b13bd79..f2c1717f 100644
--- a/unittests/Basic_tests/Infrastructure/FileSystem_.cs
+++ b/unittests/Basic_tests/Infrastructure/FileSystem_.cs
@@ -12,7 +12,7 @@ public class FileSystem_
public void Sorts_enumerated_files_on_filename_when_no_subfolders()
{
var parent = TestConfig.CreateRandomTempDirectory();
- var knownFolders = FoldersConfiguration.Default(null);
+ var knownFolders = Folders.Default;
var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path);
@@ -32,7 +32,7 @@ public void Sorts_enumerated_files_on_filename_when_no_subfolders()
public void Sorts_enumerated_files_on_filename_without_extension_when_no_subfolders()
{
var parent = TestConfig.CreateRandomTempDirectory();
- var knownFolders = FoldersConfiguration.Default(null);
+ var knownFolders = Folders.Default;
var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path);
@@ -52,7 +52,7 @@ public void Sorts_enumerated_files_on_filename_without_extension_when_no_subfold
public void Sorts_enumerated_files_on_sub_path_when_subfolders_are_used()
{
var parent = TestConfig.CreateRandomTempDirectory();
- var knownFolders = FoldersConfiguration.Default(null);
+ var knownFolders = Folders.Default;
var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path);
@@ -75,7 +75,7 @@ public void Sorts_enumerated_files_on_sub_path_when_subfolders_are_used()
public void Sorts_enumerated_files_on_filename_when_directory_names_are_ignored()
{
var parent = TestConfig.CreateRandomTempDirectory();
- var knownFolders = FoldersConfiguration.Default(null);
+ var knownFolders = Folders.Default;
var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path);
diff --git a/unittests/Basic_tests/Infrastructure/FolderConfiguration/Customized_Folders_Can_Be_Set_Programmatically.cs b/unittests/Basic_tests/Infrastructure/FolderConfiguration/Customized_Folders_Can_Be_Set_Programmatically.cs
new file mode 100644
index 00000000..ec9b7ca4
--- /dev/null
+++ b/unittests/Basic_tests/Infrastructure/FolderConfiguration/Customized_Folders_Can_Be_Set_Programmatically.cs
@@ -0,0 +1,115 @@
+using System.Collections.Immutable;
+using FluentAssertions;
+using grate.Configuration;
+
+namespace Basic_tests.Infrastructure.FolderConfiguration;
+
+// ReSharper disable once InconsistentNaming
+public class Customized_Folders_Can_Be_Set_Programmatically
+{
+ [Fact]
+ public void From_MigrationsFolder_list()
+ {
+ var folders = Folders.Create(
+ new MigrationsFolder("structure"),
+ new MigrationsFolder("randomstuff"),
+ new MigrationsFolder("procedures"),
+ new MigrationsFolder("security")
+ );
+ var items = folders.Values.ToImmutableArray();
+
+ Assert.Multiple(() =>
+ {
+ items[0].Should().Be(folders["structure"]);
+ items[1].Should().Be(folders["randomstuff"]);
+ items[2].Should().Be(folders["procedures"]);
+ items[3].Should().Be(folders["security"]);
+ });
+ }
+
+ [Fact]
+ public void From_Enumerable_of_MigrationsFolder()
+ {
+ var folders = Folders.Create(new List
+ {
+ new("structure"),
+ new("randomstuff"),
+ new("procedures"),
+ new("security")}
+ );
+ var items = folders.Values.ToImmutableArray();
+
+ Assert.Multiple(() =>
+ {
+ items[0].Should().Be(folders["structure"]);
+ items[1].Should().Be(folders["randomstuff"]);
+ items[2].Should().Be(folders["procedures"]);
+ items[3].Should().Be(folders["security"]);
+ });
+ }
+
+ [Fact]
+ public void From_Dictionary_of_MigrationsFolder()
+ {
+ var folders = Folders.Create(new Dictionary
+ {
+ {"structure", new("str") },
+ {"randomstuff", new("rnd") },
+ {"procedures", new("procs") },
+ {"security", new("sec") }
+ }
+ );
+ var items = folders.Values.ToImmutableArray();
+
+ Assert.Multiple(() =>
+ {
+ items[0].Should().Be(folders["structure"]);
+ items[1].Should().Be(folders["randomstuff"]);
+ items[2].Should().Be(folders["procedures"]);
+ items[3].Should().Be(folders["security"]);
+ });
+ }
+
+
+ [Fact]
+ public void From_command_line_argument_style_single_string()
+ {
+ var folders = Folders.Create("nup=tables;sprocs=storedprocedures;views=projections");
+ var items = folders.ToImmutableArray();
+
+ Assert.Multiple(() =>
+ {
+ items[0].Key.Should().Be("nup");
+ items[0].Value!.Path.Should().Be("tables");
+
+ items[1].Key.Should().Be("sprocs");
+ items[1].Value!.Path.Should().Be("storedprocedures");
+
+ items[2].Key.Should().Be("views");
+ items[2].Value!.Path.Should().Be("projections");
+ });
+ }
+
+ [Fact]
+ public void From_multiple_string_arguments()
+ {
+ var folders = Folders.Create("zup=tables", "sprocs=path:storedprocedures,type:anytime", "views=projections");
+ var items = folders.ToImmutableArray();
+
+ Assert.Multiple(() =>
+ {
+ items[0].Key.Should().Be("zup");
+ items[0].Value!.Path.Should().Be("tables");
+
+ items[1].Key.Should().Be("sprocs");
+ items[1].Value!.Path.Should().Be("storedprocedures");
+ items[1].Value!.Type.Should().Be(MigrationType.AnyTime);
+
+ items[2].Key.Should().Be("views");
+ items[2].Value!.Path.Should().Be("projections");
+ });
+ }
+
+
+
+}
diff --git a/unittests/Basic_tests/Infrastructure/FolderConfiguration/KnownFolders_Default.cs b/unittests/Basic_tests/Infrastructure/FolderConfiguration/KnownFolders_Default.cs
index 7d0144e4..25627dca 100644
--- a/unittests/Basic_tests/Infrastructure/FolderConfiguration/KnownFolders_Default.cs
+++ b/unittests/Basic_tests/Infrastructure/FolderConfiguration/KnownFolders_Default.cs
@@ -59,7 +59,7 @@ TransactionHandling transactionHandling
}
private static readonly DirectoryInfo Root = TestConfig.CreateRandomTempDirectory();
- private static readonly IFoldersConfiguration Folders = FoldersConfiguration.Default(null);
+ private static readonly IFoldersConfiguration Folders = global::grate.Configuration.Folders.Default;
public static IEnumerable