Skip to content

Commit f9acc41

Browse files
committed
Redesign of command handling
1 parent b75c34f commit f9acc41

File tree

10 files changed

+102
-9
lines changed

10 files changed

+102
-9
lines changed

src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Dataverse.ConfigurationMigrationTool.Console.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.4" />
1515
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.2.7" />
1616
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
17+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta6.25358.103" />
1718
</ItemGroup>
1819
<ItemGroup>
1920
<None Update="appsettings.Development.json">

src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/CoconaAppExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public static ICoconaCommandsBuilder UseImportFeature(this ICoconaCommandsBuilde
1111
app.AddCommands<ImportCommands>();
1212
return app;
1313
}
14+
1415
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands;
2+
public class ImportCommandOptions
3+
{
4+
public string schema { get; set; }
5+
public string data { get; set; }
6+
}

src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Commands/ImportCommands.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,35 @@
22
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
33
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
44
using Microsoft.Extensions.Logging;
5+
using Microsoft.Extensions.Options;
56
using ConsoleApp = System.Console;
67

78
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands;
8-
9-
public class ImportCommands
9+
[CommandVerb("import")]
10+
public class ImportCommands : ICommand
1011
{
1112
private readonly ILogger<ImportCommands> _logger;
1213
private readonly IImportDataProvider _importDataProvider;
1314
private readonly IValidator<ImportSchema> _schemaValidator;
1415
private readonly IImportTaskProcessorService _importDataService;
16+
private readonly ImportCommandOptions _options;
1517

1618
public ImportCommands(ILogger<ImportCommands> logger,
1719
IImportDataProvider importDataProvider,
1820
IValidator<ImportSchema> schemaValidator,
19-
IImportTaskProcessorService importDataService)
21+
IImportTaskProcessorService importDataService,
22+
IOptions<ImportCommandOptions> options)
2023
{
2124
_logger = logger;
2225
_importDataProvider = importDataProvider;
2326
_schemaValidator = schemaValidator;
2427
_importDataService = importDataService;
28+
_options = options.Value;
2529
}
2630

31+
public async Task Execute() => await Import(_options.schema, _options.data);
32+
33+
2734
[Command("import")]
2835
public async Task Import([Option("schema")] string schemafilepath, [Option("data")] string datafilepath)
2936
{

src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
23
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators;
34
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas;
45
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
56
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas;
67
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters;
78
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
9+
using Microsoft.Extensions.Configuration;
810
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Options;
912
using System.Reflection;
1013

1114
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import;
1215

1316
public static class IServiceCollectionExtensions
1417
{
15-
public static IServiceCollection AddImportFeature(this IServiceCollection services)
18+
public static IServiceCollection AddImportFeature(this IServiceCollection services, IConfiguration Configuration)
1619
{
1720

1821
return services.RegisterFromReflection<IFieldSchemaValidationRule>()
@@ -26,7 +29,14 @@ public static IServiceCollection AddImportFeature(this IServiceCollection servic
2629
.AddSingleton<IDataverseValueConverter, DataverseValueConverter>()
2730
.AddTransient<IValidator<ImportSchema>, SchemaValidator>()
2831
.AddTransient<IValidator<EntitySchema>, EntitySchemaValidator>()
29-
.AddSingleton<IImportTaskProcessorService, ImportTaskProcessorService>();
32+
.AddSingleton<IImportTaskProcessorService, ImportTaskProcessorService>()
33+
.Configure<ImportCommandOptions>(Configuration);
34+
35+
}
36+
public static IServiceCollection UseCommands(this IServiceCollection services, params string[] args)
37+
{
38+
services.AddSingleton<IOptions<CommandProcessorHostingServiceOptions>>(Options.Create(new CommandProcessorHostingServiceOptions() { CommandVerb = args[0] }));
39+
return services.AddHostedService<CommandProcessorHostingService>();
3040
}
3141
public static IServiceCollection RegisterFromReflection<T>(this IServiceCollection services)
3242
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Hosting;
3+
using Microsoft.Extensions.Options;
4+
using System.Reflection;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
7+
public class CommandProcessorHostingService : BackgroundService
8+
{
9+
private readonly CommandProcessorHostingServiceOptions _options;
10+
private readonly IServiceScopeFactory _serviceProviderFactory;
11+
private readonly IHostApplicationLifetime _lifetime;
12+
13+
public CommandProcessorHostingService(IOptions<CommandProcessorHostingServiceOptions> options, IServiceScopeFactory serviceProviderFactory, IHostApplicationLifetime lifetime)
14+
{
15+
_options = options.Value;
16+
_serviceProviderFactory = serviceProviderFactory;
17+
_lifetime = lifetime;
18+
}
19+
20+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
21+
{
22+
var types = from type in Assembly.GetExecutingAssembly().GetTypes()
23+
where Attribute.IsDefined(type, typeof(CommandVerbAttribute)) &&
24+
type.IsClass &&
25+
!type.IsAbstract &&
26+
typeof(ICommand).IsAssignableFrom(type)
27+
select (type, (CommandVerbAttribute)Attribute.GetCustomAttribute(type, typeof(CommandVerbAttribute)));
28+
29+
var mathingVerbCommandType = types.FirstOrDefault(t => t.Item2.Verb.Equals(_options.CommandVerb, StringComparison.OrdinalIgnoreCase)).type;
30+
using (var scope = _serviceProviderFactory.CreateScope())
31+
{
32+
if (mathingVerbCommandType == null)
33+
{
34+
throw new InvalidOperationException($"No command found for verb '{_options.CommandVerb}'");
35+
}
36+
var command = ActivatorUtilities.CreateInstance(scope.ServiceProvider, mathingVerbCommandType) as ICommand;
37+
await command.Execute();
38+
39+
40+
}
41+
_lifetime.StopApplication();
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
2+
public class CommandProcessorHostingServiceOptions
3+
{
4+
public string CommandVerb { get; set; }
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
2+
[AttributeUsage(AttributeTargets.Class)]
3+
public class CommandVerbAttribute : Attribute
4+
{
5+
public string Verb { get; }
6+
public CommandVerbAttribute(string verb)
7+
{
8+
Verb = verb ?? throw new ArgumentNullException(nameof(verb), "Verb cannot be null");
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
2+
public interface ICommand
3+
{
4+
Task Execute();
5+
}

src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Program.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
builder.ConfigureHostConfiguration((config) =>
1515
{
1616
// Configure the host configuration, such as environment variables, command line arguments, etc.
17-
config.AddEnvironmentVariables();
17+
config.AddEnvironmentVariables().AddCommandLine(args);
18+
1819
});
1920
builder.ConfigureAppConfiguration((context, config) =>
2021
{
2122
config
2223
.AddEnvironmentVariables()
24+
.AddCommandLine(args)
2325
.AddJsonFile("appsettings.json", false, false)
2426
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", false, false);
2527
if (!context.HostingEnvironment.IsProduction())
@@ -41,6 +43,7 @@
4143

4244
services
4345
.AddLogging(lb => lb.AddConsole())
46+
4447
.Configure<SdkDataverseServiceFactoryOptions>(context.Configuration.GetSection("Dataverse"))
4548
.Configure<ParallelismBulkOrganizationServiceOptions>(context.Configuration.GetSection("Dataverse"))
4649
.AddTransient<IImportDataProvider, FileReaderDataImportProvider>()
@@ -49,15 +52,17 @@
4952
.AddTransient<ServiceClient>((sp) => (ServiceClient)sp.GetRequiredService<IDataverseClientFactory>().Create())
5053
.AddSingleton<IBulkOrganizationService, ParallelismBulkOrganizationService>()
5154
.AddDataverseClient()
52-
.AddImportFeature();
55+
.UseCommands(args)
56+
.AddImportFeature(context.Configuration);
5357
// Configure other services.
5458
});
5559
builder.ConfigureCocona(args, configureApplication: app =>
5660
{
5761
// Configure your app's commands normally as you would with app
5862
app.UseImportFeature();
5963
});
60-
await builder.RunConsoleAsync(x => x.SuppressStatusMessages = true);
64+
var app = builder.Build();
65+
await app.RunAsync();
6166
//var builder = CoconaApp.CreateBuilder();
6267
//builder.Configuration
6368
// .AddEnvironmentVariables()

0 commit comments

Comments
 (0)