Skip to content

Commit

Permalink
82 set migration paths in configuration file (#89)
Browse files Browse the repository at this point in the history
* feat: #82 provide migrations path through configrations

* fix: #82 don't show up-to-date migrations during status check

because we can have thousands of old migrations
  • Loading branch information
Ujinjinjin authored Sep 9, 2024
1 parent ec28a79 commit a9d573b
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 21 deletions.
22 changes: 11 additions & 11 deletions Src/Dingo.Cli/Controllers/MigrationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ private Command GetCreateCommand()
var nameOption = OptionFactory.CreateOption<string>(
new[] { "--name", "-n" },
"Migration name",
false
true
);
var pathOption = OptionFactory.CreateOption<string>(
new[] { "--path", "-p" },
"Destination where migration file will be created",
false
true
);

var command = CommandFactory.CreateCommand(
Expand All @@ -64,15 +64,15 @@ private Command GetCreateCommand()

private Command GetUpCommand()
{
var profileOption = OptionFactory.CreateOption<string>(
var profileOption = OptionFactory.CreateOption<string?>(
new[] { "--configuration", "-c" },
"Configuration profile name",
false
);
var pathOption = OptionFactory.CreateOption<string>(
var pathOption = OptionFactory.CreateOption<string?>(
new[] { "--path", "-p" },
"Root path to database migration files",
true
false
);

var command = CommandFactory.CreateCommand(
Expand All @@ -93,15 +93,15 @@ private Command GetUpCommand()

private Command GetDownCommand()
{
var profileOption = OptionFactory.CreateOption<string>(
var profileOption = OptionFactory.CreateOption<string?>(
new[] { "--configuration", "-c" },
"Configuration profile name",
false
);
var pathOption = OptionFactory.CreateOption<string>(
var pathOption = OptionFactory.CreateOption<string?>(
new[] { "--path", "-p" },
"Root path to database migration files",
true
false
);
var countOption = OptionFactory.CreateOption<int?>(
new[] { "--count" },
Expand Down Expand Up @@ -136,15 +136,15 @@ private Command GetDownCommand()

private Command GetStatusCommand()
{
var profileOption = OptionFactory.CreateOption<string>(
var profileOption = OptionFactory.CreateOption<string?>(
new[] { "--configuration", "-c" },
"Configuration profile name",
false
);
var pathOption = OptionFactory.CreateOption<string>(
var pathOption = OptionFactory.CreateOption<string?>(
new[] { "--path", "-p" },
"Root path to database migration files",
true
false
);

var command = CommandFactory.CreateCommand(
Expand Down
1 change: 1 addition & 0 deletions Src/Dingo.Core/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal static class Key
public const string MigrationWildcard = "migration.wildcard";
public const string MigrationDownRequired = "migration.down-required";
public const string MigrationForcePaths = "migration.force-paths";
public const string MigrationPath = "migration.path";
public const string LogLevel = "log.level";
}

Expand Down
11 changes: 11 additions & 0 deletions Src/Dingo.Core/Exceptions/PathNotProvidedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Dingo.Core.Exceptions;

internal sealed class PathNotProvidedException : DingoException
{
private const string ErrorMessage = "Migrations path not provided. Either set it in the profile or with '-p' parameter. Use '--help' for more information.";

public PathNotProvidedException()
: base(ErrorMessage)
{
}
}
6 changes: 3 additions & 3 deletions Src/Dingo.Core/Services/Handlers/IMigrationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ public interface IMigrationHandler
Task CreateAsync(string name, string path, CancellationToken ct = default);

/// <summary> Show migrations status </summary>
Task ShowStatusAsync(string? profile, string path, CancellationToken ct = default);
Task ShowStatusAsync(string? profile, string? path, CancellationToken ct = default);

/// <summary> Apply database migrations </summary>
Task MigrateAsync(string? profile, string path, CancellationToken ct = default);
Task MigrateAsync(string? profile, string? path, CancellationToken ct = default);

/// <summary> Rollback last N patches </summary>
Task RollbackAsync(string? profile, string path, int? patchCount, bool force, CancellationToken ct = default);
Task RollbackAsync(string? profile, string? path, int? patchCount, bool force, CancellationToken ct = default);
}
28 changes: 21 additions & 7 deletions Src/Dingo.Core/Services/Handlers/MigrationHandler.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using Dingo.Core.Exceptions;
using Dingo.Core.Extensions;
using Dingo.Core.IO;
using Dingo.Core.Models;
using Dingo.Core.Services.Config;
using Dingo.Core.Services.Migrations;
using Dingo.Core.Utils;
using Microsoft.Extensions.Logging;
using Trico.Configuration;

namespace Dingo.Core.Services.Handlers;

Expand All @@ -16,6 +19,7 @@ internal sealed class MigrationHandler : IMigrationHandler
private readonly IMigrationScanner _migrationScanner;
private readonly IMigrationRunner _migrationRunner;
private readonly IConfigProfileLoader _profileLoader;
private readonly IConfiguration _configuration;
private readonly IOutput _output;
private readonly ILogger _logger;

Expand All @@ -25,6 +29,7 @@ public MigrationHandler(
IMigrationScanner migrationScanner,
IMigrationRunner migrationRunner,
IConfigProfileLoader profileLoader,
IConfiguration configuration,
IOutput output,
ILoggerFactory loggerFactory
)
Expand All @@ -34,6 +39,7 @@ ILoggerFactory loggerFactory
_migrationScanner = migrationScanner.Required(nameof(migrationScanner));
_migrationRunner = migrationRunner.Required(nameof(migrationRunner));
_profileLoader = profileLoader.Required(nameof(profileLoader));
_configuration = configuration.Required(nameof(configuration));
_output = output.Required(nameof(output));
_logger = loggerFactory.Required(nameof(loggerFactory))
.CreateLogger<MigrationHandler>()
Expand All @@ -57,14 +63,14 @@ public async Task CreateAsync(string name, string path, CancellationToken ct = d
}

/// <inheritdoc />
public async Task MigrateAsync(string? profile, string path, CancellationToken ct = default)
public async Task MigrateAsync(string? profile, string? path, CancellationToken ct = default)
{
using var _ = new CodeTiming(_logger);

try
{
await _profileLoader.LoadAsync(profile, ct);
await _migrationRunner.MigrateAsync(path, ct);
await _migrationRunner.MigrateAsync(ResolvePath(path), ct);
}
catch (Exception ex)
{
Expand All @@ -76,7 +82,7 @@ public async Task MigrateAsync(string? profile, string path, CancellationToken c
/// <inheritdoc />
public async Task RollbackAsync(
string? profile,
string path,
string? path,
int? patchCount,
bool force,
CancellationToken ct = default
Expand All @@ -87,7 +93,7 @@ public async Task RollbackAsync(
try
{
await _profileLoader.LoadAsync(profile, ct);
await _migrationRunner.RollbackAsync(path, patchCount ?? DefaultPatchRollbackCount, force, ct);
await _migrationRunner.RollbackAsync(ResolvePath(path), patchCount ?? DefaultPatchRollbackCount, force, ct);
}
catch (Exception ex)
{
Expand All @@ -97,14 +103,14 @@ public async Task RollbackAsync(
}

/// <inheritdoc />
public async Task ShowStatusAsync(string? profile, string path, CancellationToken ct = default)
public async Task ShowStatusAsync(string? profile, string? path, CancellationToken ct = default)
{
using var _ = new CodeTiming(_logger);

try
{
await _profileLoader.LoadAsync(profile, ct);
await _ShowStatusAsync(path, ct);
await _ShowStatusAsync(ResolvePath(path), ct);
}
catch (Exception ex)
{
Expand All @@ -121,7 +127,15 @@ private async Task _ShowStatusAsync(string path, CancellationToken ct = default)
_output.Write($"Total count: {migrations.Count}", LogLevel.Information);
foreach (var migration in migrations)
{
_output.Write($"{migration.Status} - '{migration.Path.Relative}'", LogLevel.Information);
if (migration.Status > MigrationStatus.UpToDate)
{
_output.Write($"{migration.Status} - '{migration.Path.Relative}'", LogLevel.Information);
}
}
}

private string ResolvePath(string? path)
{
return path ?? _configuration.Get(Configuration.Key.MigrationPath) ?? throw new PathNotProvidedException();
}
}
63 changes: 63 additions & 0 deletions docs/writerside/topics/Configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Use this section as a reference `dingo` configurations.
| `db.provider` || `PostgreSQL` |
| `migration.delimiter` || `^--\s*down$` |
| `migration.wildcard` || `*.sql` |
| `migration.force-paths` || `null` |
| `migration.path` | ~ | `null` |
| `migration.down-required` || `false` |
| `log.level` || `Information` |

Expand Down Expand Up @@ -125,6 +127,67 @@ Wildcard used to search migration files in the specified directory
</tab>
</tabs>

## migration.force-paths

List of paths containing migrations that need to be forcefully applied on every patch. The most common use-case is to reapply all stored procedures / functions after user defined types have been changed.

<tabs group="config-type">
<tab title="yaml" group-key="config-yaml">
<code-block lang="yaml">
migration:
force-paths:
- ./db/auth/procedures
- ./db/auth/types
</code-block>
</tab>
<tab title="json" group-key="config-json">
<code-block lang="json">
{
"migration": {
"force-paths": [
"./db/auth/procedures",
"./db/auth/types"
]
}
}
</code-block>
</tab>
<tab title="env" group-key="config-env">
<code-block lang="shell">
export DINGO_MIGRATION__FORCE_PATHS="./db/auth/procedures ./db/auth/types"
</code-block>
</tab>
</tabs>

> Currently space character is used as a delimiter for paths set through env variable, which wasn't a good idea, so it will be replaced with system PATH var delimiter instead
## migration.path

Path where migration files are stored. Can be provided either in the configuration file or as a `-p` parameter

<tabs group="config-type">
<tab title="yaml" group-key="config-yaml">
<code-block lang="yaml">
migration:
path: ./db/auth
</code-block>
</tab>
<tab title="json" group-key="config-json">
<code-block lang="json">
{
"migration": {
"path": "./db/auth"
}
}
</code-block>
</tab>
<tab title="env" group-key="config-env">
<code-block lang="shell">
export DINGO_MIGRATION__PATH="./db/auth"
</code-block>
</tab>
</tabs>

## migration.down-required

Specifies if `down` part of migrations is required. If `true` - applying migrations will fail if any of migration files in the specified directory lack the `down` part.
Expand Down

0 comments on commit a9d573b

Please sign in to comment.