Skip to content

Commit

Permalink
Implement repository path command line argument (erikbra#454) (erikbr…
Browse files Browse the repository at this point in the history
…a#481)

* Implement repository path command line argument (erikbra#454)

RepositoryPath created as an optional parameter which is null by default.

Note that this is technically a partial implementation as the ScriptsRunErrorsTable also has a repository_path field which will remain unused, but this seems like a good start.

* Simplify repository_path handling

* Use repositoryPath from Config instead of passing as separate parameter
* Insert in ScriptsRunErrors too
* Wrote tests on the functionality

---------

Co-authored-by: Erik A. Brandstadmoen <erik@brandstadmoen.net>
  • Loading branch information
kitroed and erikbra authored Apr 1, 2024
1 parent 4c20bb2 commit 6998deb
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 11 deletions.
1 change: 1 addition & 0 deletions docs/ConfigurationOptions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ grate --connectionstring="Server=(localdb)\MSSQLLocalDB;Integrated Security=true
| --dryrun | false | **DryRun** - This instructs grate to log what would have run, but not to actually run anything against the database. Use this option if you are trying to figure out what grate is going to do. |
| --restore | - | **Restore** - This instructs grate where to find the database backup file (.bak) to restore from. If this option is not specified, no restore will be done.
| -ni<br>--noninteractive<br>--silent | false | **Silent** - tells grate not to ask for any input when it runs.
| -r<br>--repo<br>--repositorypath <repositorypath> | - | **Repository Path** - RepositoryPath - The repository. A string that can be anything. Used to track versioning along with the version. Defaults to `null`. |
| --version <version> | 1.0.0.0 | **Database Version** - specify the version of the current migration directly on the command line. |
| -v<br>--verbosity &lt;Critical\|<br>Debug\|<br>Error\|<br>Information\|<br>None\|<br>Trace\|Warning&gt; | Information | **Verbosity level** (as defined here: https://docs.microsoft.com/dotnet/api/Microsoft.Extensions.Logging.LogLevel)
| -?<br>-h<br>--help | - | Show help and usage information |
5 changes: 5 additions & 0 deletions src/grate.core/Configuration/GrateConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public record GrateConfiguration
/// </summary>
public GrateEnvironment? Environment { get; init; }

/// <summary>
/// The optional repository path value used to track along with version.
/// </summary>
public string? RepositoryPath { get; init; }

/// <summary>
/// The database version we're migrating to on this run.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/grate.core/Configuration/GrateConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ public GrateConfigurationBuilder WithAdminConnectionString(string adminConnectio
return this;
}

/// <summary>
/// Repository path of service to use. Grate will store the repository path in the database
/// for history and tracking purposes.
/// </summary>
/// <param name="repositoryPath">service migration repository path</param>
/// <returns>GrateConfigurationBuilder</returns>
public GrateConfigurationBuilder WithRepositoryPath(string repositoryPath)
{
_grateConfiguration = _grateConfiguration with { RepositoryPath = repositoryPath };
return this;
}

/// <summary>
/// Version of service to use. Grate will store the version in the database
/// for history and tracking purposes.
Expand Down
2 changes: 1 addition & 1 deletion src/grate.core/Infrastructure/TokenProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public TokenProvider(GrateConfiguration config, IDatabase db)
["OutputPath"] = _config.OutputPath.FullName, //TODO: Does RH use name or full path?
["PermissionsFolderName"] = GetFolder(KnownFolderKeys.Permissions).ToToken(),
//["RecoveryMode"] = RecoveryMode.to_string(),
//["RepositoryPath"] = RepositoryPath.to_string(),
["RepositoryPath"] = _config.RepositoryPath,
["Restore"] = _config.Restore,
//["RestoreTimeout"] = RestoreTimeout.to_string(),
["RunAfterCreateDatabaseFolderName"] = GetFolder(KnownFolderKeys.RunAfterCreateDatabase).ToToken(),
Expand Down
22 changes: 17 additions & 5 deletions src/grate.core/Migration/AnsiSqlDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,13 @@ public virtual async Task<long> VersionTheDatabase(string newVersion)
{
var sql = Parameterize($@"
INSERT INTO {VersionTable}
(version, entry_date, modified_date, entered_by, status)
VALUES(@newVersion, @entryDate, @modifiedDate, @enteredBy, @status)
(repository_path, version, entry_date, modified_date, entered_by, status)
VALUES(@repositoryPath, @newVersion, @entryDate, @modifiedDate, @enteredBy, @status)
{_syntax.ReturnId}
");
var repositoryPath = Config?.RepositoryPath;

long versionId;

try
Expand All @@ -337,6 +339,7 @@ INSERT INTO {VersionTable}
sql,
new
{
repositoryPath,
newVersion,
entryDate = DateTime.UtcNow,
modifiedDate = DateTime.UtcNow,
Expand All @@ -350,7 +353,14 @@ INSERT INTO {VersionTable}
versionId = 1;
}

Logger.LogInformation(" Versioning {DbName} database with version {Version}.", DatabaseName, newVersion);
if (repositoryPath != null)
{
Logger.LogInformation(" Versioning {DbName} database with version {Version} based on {RepositoryPath}.", DatabaseName, newVersion, repositoryPath);
}
else
{
Logger.LogInformation(" Versioning {DbName} database with version {Version}.", DatabaseName, newVersion);
}

return versionId;
}
Expand Down Expand Up @@ -538,14 +548,16 @@ public async Task InsertScriptRunError(string scriptName, string? sql, string er
{
var insertSql = Parameterize($@"
INSERT INTO {ScriptsRunErrorsTable}
(version, script_name, text_of_script, erroneous_part_of_script, error_message, entry_date, modified_date, entered_by)
VALUES (@version, @scriptName, @sql, @errorSql, @errorMessage, @now, @now, @usr)");
(repository_path, version, script_name, text_of_script, erroneous_part_of_script, error_message, entry_date, modified_date, entered_by)
VALUES (@repositoryPath, @version, @scriptName, @sql, @errorSql, @errorMessage, @now, @now, @usr)");

var versionSql = Parameterize($"SELECT version FROM {VersionTable} WHERE id = @versionId");

var version = await ExecuteScalarAsync<string>(ActiveConnection, versionSql, new { versionId });
var repositoryPath = Config?.RepositoryPath;

var scriptRunErrors = new DynamicParameters();
scriptRunErrors.Add(nameof(repositoryPath), repositoryPath);
scriptRunErrors.Add(nameof(version), version);
scriptRunErrors.Add(nameof(scriptName), scriptName);
scriptRunErrors.Add(nameof(sql), sql, DbType.String);
Expand Down
17 changes: 13 additions & 4 deletions src/grate.oracle/Migration/OracleDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,15 @@ public override async Task<long> VersionTheDatabase(string newVersion)
{
var sql = (string)$@"
INSERT INTO {VersionTable}
(version, entry_date, modified_date, entered_by, status)
VALUES(:newVersion, :entryDate, :modifiedDate, :enteredBy, :status)
(repository_path, version, entry_date, modified_date, entered_by, status)
VALUES(:repositoryPath, :newVersion, :entryDate, :modifiedDate, :enteredBy, :status)
RETURNING id into :id
";
var repositoryPath = Config?.RepositoryPath;

var parameters = new
{
repositoryPath,
newVersion,
entryDate = DateTime.UtcNow,
modifiedDate = DateTime.UtcNow,
Expand All @@ -125,8 +128,14 @@ INSERT INTO {VersionTable}
versionId = 1;
}


Logger.LogInformation(" Versioning {DbName} database with version {Version}.", DatabaseName, newVersion);
if (repositoryPath != null)
{
Logger.LogInformation(" Versioning {DbName} database with version {Version} based on {RepositoryPath}.", DatabaseName, newVersion, repositoryPath);
}
else
{
Logger.LogInformation(" Versioning {DbName} database with version {Version}.", DatabaseName, newVersion);
}

return versionId;
}
Expand Down
7 changes: 7 additions & 0 deletions src/grate/Commands/MigrateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public MigrateCommand(IGrateMigrator mi) : base("Migrates the database")
Add(Environment());
Add(SchemaName());
Add(Silent());
Add(RepositoryPath());
Add(Version());
Add(Drop());
Add(CreateDatabase());
Expand Down Expand Up @@ -281,6 +282,12 @@ private static Option<bool> Silent() =>
"Silent - tells grate not to ask for any input when it runs."
);

private static Option<string> RepositoryPath() =>
new(
new[] { "-r", "--repo", "--repositorypath" },
"Repository Path - The repository. A string that can be anything. Used to track versioning along with the version. Defaults to NULL."
);

private static Option<string> Version() =>
new(
new[] { "--version" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ public async Task OutputPath(string argName)
cfg?.OutputPath.ToString().Should().Be(database);
}

[Theory]
[InlineData("-r ")]
[InlineData("-r=")]
[InlineData("--repo ")]
[InlineData("--repo=")]
[InlineData("--repositorypath ")]
[InlineData("--repositorypath=")]
public async Task RepositoryPath(string argName)
{
var repositorypath = "git@example.com:user/repo.git";
var commandline = argName + repositorypath;
var cfg = await ParseGrateConfiguration(commandline);

cfg?.RepositoryPath.Should().Be(repositorypath);
}

[Theory]
[InlineData("--version=")]
[InlineData("--version ")]
Expand Down
2 changes: 1 addition & 1 deletion unittests/Basic_tests/Infrastructure/MockDbMigrator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using grate.Configuration;
using grate.Configuration;
using grate.Infrastructure;
using grate.Migration;
using grate.Sqlite.Migration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,48 @@ public async Task Inserts_Failed_Scripts_Into_ScriptRunErrors_Table()

scripts.Should().HaveCount(1);
}

[Fact]
public async Task Inserts_RepositoryPath_Into_ScriptRunErrors_Table()
{
var db = TestConfig.RandomDatabase();

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

var repositoryPath = "https://github.com/blah/blah.git";

var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration)
.WithConnectionString(Context.ConnectionString(db))
.WithFolders(knownFolders)
.WithSqlFilesDirectory(parent)
.WithRepositoryPath(repositoryPath)
.Build();

await using (var migrator = Context.Migrator.WithConfiguration(config))
{
try
{
await migrator.Migrate();
}
catch (MigrationFailed)
{
}
}

string? loggedRepositoryPath;
string sql = $"SELECT repository_path FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}";

using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
{
using var conn = Context.CreateDbConnection(db);
loggedRepositoryPath = (await conn.QuerySingleOrDefaultAsync<string>(sql));
}

loggedRepositoryPath.Should().Be(repositoryPath);
}


[Fact]
public async Task Inserts_Large_Failed_Scripts_Into_ScriptRunErrors_Table()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,44 @@ public async Task Creates_a_new_version_with_status_InProgress()
var version = entries.Single(x => x.version == dbVersion);
version.status.Should().Be(MigrationStatus.InProgress);
}

[Fact]
[Trait("Category", "Versioning")]
public async Task Includes_RepositoryPath_in_version_table()
{
var db = TestConfig.RandomDatabase();
var dbVersion = "1.2.3.4";

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

var repositoryPath = "any repository path";
var config = GrateConfigurationBuilder.Create(Context.DefaultConfiguration)
.WithConnectionString(Context.ConnectionString(db))
.WithFolders(knownFolders)
.WithSqlFilesDirectory(parent)
.WithRepositoryPath(repositoryPath)
.WithVersion(dbVersion)
.Build();

await using (var migrator = Context.Migrator.WithConfiguration(config))
{
//Calling migrate here to setup the database and such.
await migrator.Migrate();
}

string? loggedRepositoryPath;
string sql = $"SELECT repository_path FROM {Context.Syntax.TableWithSchema("grate", "Version")}";

using (var conn = Context.CreateDbConnection(db))
{
loggedRepositoryPath = await conn.QuerySingleOrDefaultAsync<string>(sql);
}

loggedRepositoryPath.Should().Be(repositoryPath);
}


[Fact]
[Trait("Category", "Versioning")]
Expand Down

0 comments on commit 6998deb

Please sign in to comment.