Skip to content

Commit

Permalink
Task #485: Add factory IFoldersConfiguration builder 'Folders' for co…
Browse files Browse the repository at this point in the history
…nvenience (#490)

Make it easier to configure custom folder configurations when using grate as a library.

Solves #485
  • Loading branch information
erikbra authored Apr 1, 2024
1 parent 6998deb commit 4ce6271
Show file tree
Hide file tree
Showing 30 changed files with 272 additions and 73 deletions.
73 changes: 73 additions & 0 deletions src/grate.core/Configuration/Folders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace grate.Configuration;

/// <summary>
/// Factory class for creating <see cref="IFoldersConfiguration"/> instances.
/// </summary>
public static class Folders
{
/// <summary>
/// The default <see cref="IFoldersConfiguration"/>.
/// </summary>
public static IFoldersConfiguration Default => FoldersConfiguration.Default();

/// <summary>
/// An empty <see cref="IFoldersConfiguration"/>.
/// </summary>
public static IFoldersConfiguration Empty => FoldersConfiguration.Empty;

/// <summary>
/// Folder configuration with the specified folders.
/// </summary>
/// <param name="folders">A list of migration folders</param>
/// <returns>An IFoldersConfiguration with the supplied folders</returns>
public static IFoldersConfiguration Create(params MigrationsFolder [] folders)
=> new FoldersConfiguration(folders);

/// <summary>
/// Folder configuration with the specified folders.
/// </summary>
/// <param name="folders">A list of migration folders</param>
/// <returns>An IFoldersConfiguration with the supplied folders</returns>
public static IFoldersConfiguration Create(IEnumerable<MigrationsFolder> folders)
=> new FoldersConfiguration(folders);

/// <summary>
/// Folder configuration with the specified Dictionary of folder names and folders.
/// </summary>
/// <param name="folders">A Dictionary of folder names and migration folders</param>
/// <returns>An IFoldersConfiguration with the supplied folders</returns>
public static IFoldersConfiguration Create(IDictionary<string, MigrationsFolder> folders)
=> new FoldersConfiguration(folders);

/// <summary>
/// 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:
///
/// <code>
/// Create("structure=relativePath:myCustomFolder,type:Once,connectionType:Admin",
/// "randomstuff=type:Once;procedures=type:Once;security=type:Once")
/// </code>
///
/// </summary>
/// <param name="folders">Separate strings with folder configurations</param>
/// <returns>An IFoldersConfiguration with the supplied folders</returns>
public static IFoldersConfiguration Create(params string[] folders)
=> FoldersCommand.Parse(string.Join(";", folders));

/// <summary>
/// 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"
/// </summary>
/// <param name="folders">A semicolon separated string with all the folder configurations, as specified on the command line</param>
/// <returns>An IFoldersConfiguration with the supplied folders</returns>
public static IFoldersConfiguration Create(string folders)
=> FoldersCommand.Parse(folders);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Reflection;
using grate.Configuration;
using grate.Exceptions;

namespace grate.Commands;
namespace grate.Configuration;

internal static class FoldersCommand
{
Expand Down
7 changes: 6 additions & 1 deletion src/grate.core/Configuration/GrateConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
/// </summary>
/// <param name="folders">A folder configuration to use. Can be DefaultConfiguration with some modifications.</param>
///
/// <param name="folders">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 <see cref="Folders"/> with factory methods for creating folder configurations.</param>
///
/// <returns>GrateConfigurationBuilder</returns>
public GrateConfigurationBuilder WithFolders(IFoldersConfiguration folders)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FluentAssertions;
using System.Collections.Immutable;
using FluentAssertions;
using grate.Configuration;
using grate.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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");
}

}
8 changes: 4 additions & 4 deletions unittests/Basic_tests/Infrastructure/FileSystem_.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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<MigrationsFolder>
{
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<string, MigrationsFolder>
{
{"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");
});
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -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<object?[]> ExpectedKnownFolderNames()
{
Expand Down
2 changes: 1 addition & 1 deletion unittests/Basic_tests/Infrastructure/TokenReplacerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void EnsureTokensAreReplaced()
[Fact]
public void EnsureConfigMakesItToTokens()
{
var folders = FoldersConfiguration.Default(null);
var folders = Folders.Default;
var config = new GrateConfiguration() { SchemaName = "Test", Folders = folders };
var provider = new TokenProvider(config, Substitute.For<IDatabase>());
var tokens = provider.GetTokens();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public override async Task Fails_if_changed_between_runs()
var db = TestConfig.RandomDatabase();

var parent = CreateRandomTempDirectory();
var knownFolders = FoldersConfiguration.Default(null);
var knownFolders = Folders.Default;
CreateDummySql(parent, knownFolders[Up]);

var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public override async Task Fails_if_changed_between_runs()
var db = TestConfig.RandomDatabase();

var parent = CreateRandomTempDirectory();
var knownFolders = FoldersConfiguration.Default(null);
var knownFolders = Folders.Default;
CreateDummySql(parent, knownFolders[Up]);

var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration)
Expand Down
Loading

0 comments on commit 4ce6271

Please sign in to comment.