Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Dataverse.ConfigurationMigrationTool.Console.Features;
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
using Dataverse.ConfigurationMigrationTool.Console.Tests.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using Shouldly;
using System.Reflection;

namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features;
public class CommandProcessorHostingServiceTests
{
private readonly IOptions<CommandProcessorHostingServiceOptions> _options;
private readonly IServiceScopeFactory _serviceProviderFactory;
private readonly IHostApplicationLifetime _lifetime;
private readonly IServiceScope _serviceScope;
private readonly IServiceCollection ServiceCollection = new ServiceCollection();
private readonly ILogger<FakeCommand> commandLogger = Substitute.For<ILogger<FakeCommand>>();
private readonly CommandProcessorHostingServiceOptions HostingOptions = new CommandProcessorHostingServiceOptions
{
CommandVerb = "fake"
};

public CommandProcessorHostingServiceTests()
{

ServiceCollection.AddSingleton<ILogger<FakeCommand>>(commandLogger);
_serviceScope = Substitute.For<IServiceScope>();
_serviceScope.ServiceProvider.Returns(ServiceCollection.BuildServiceProvider());
_options = Substitute.For<IOptions<CommandProcessorHostingServiceOptions>>();
_options.Value.Returns(HostingOptions);
_serviceProviderFactory = Substitute.For<IServiceScopeFactory>();
_serviceProviderFactory.CreateScope().Returns(_serviceScope);
_lifetime = Substitute.For<IHostApplicationLifetime>();

}

[Fact]
public async Task GivenACommandVerb_WhenCommandProcessorRuns_ThenItShouldExecuteTheProperCommand()
{
//Arrange

var host = new CommandProcessorHostingService(_options, _serviceProviderFactory, _lifetime, Assembly.GetExecutingAssembly());
//Act
await host.StartAsync(CancellationToken.None);

//Assert
commandLogger.ShouldHaveLogged(LogLevel.Information, "Fake command executed successfully.");

}

[Fact]
public async Task GivenAnNonExistantCommandVerb_WhenCommandProcessorRuns_ThenItShouldExecuteTheProperCommand()
{
//Arrange
var host = new CommandProcessorHostingService(_options, _serviceProviderFactory, _lifetime);
//Act
Func<Task> act = () => host.StartAsync(CancellationToken.None);

//Assert
var ex = await act.ShouldThrowAsync<InvalidOperationException>();
ex.Message.ShouldBe($"No command found for verb '{HostingOptions.CommandVerb}'");

}



}
[CommandVerb("fake")]
public class FakeCommand : ICommand
{
private readonly ILogger<FakeCommand> _logger;

public FakeCommand(ILogger<FakeCommand> logger)
{
_logger = logger;
}

public Task Execute()
{
_logger.LogInformation("Fake command executed successfully.");
return Task.CompletedTask;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task GivenDataToImportWithSchema_WhenTheCommandExecutes_ThenItShoul
_importDataProvider.ReadSchemaFromFile(SchemaFilePath).Returns(importSchema);
_schemaValidator.Validate(importSchema).Returns(new ValidationResult());
//Act
await _importCommands.Import(SchemaFilePath, DataFilePath);
await _importCommands.Execute();

//Assert
Received.InOrder(async () =>
Expand All @@ -79,6 +79,44 @@ public async Task GivenDataToImportWithSchema_WhenTheCommandExecutes_ThenItShoul
await _importDataService.Execute(Arg.Is<ImportDataTask>(x => x.RelationshipSchema == FakeSchemas.Contact.Relationships.Relationship.First()), datasets);
});

}
[Fact]
public async Task GivenDataToImportWithSchema_WhenTheCommandExecutesAndFails_ThenItShouldThrowAnError()
{
//Arrange
var importSchema = new ImportSchema
{
Entity = new()
{
FakeSchemas.Account,
FakeSchemas.Contact,
FakeSchemas.Opportunity

}
};
var datasets = new Entities
{
Entity = new()
{
FakeDatasets.AccountSets,
FakeDatasets.ContactSets,
FakeDatasets.OpportunitiesSet
}
};
_importDataService.Execute(Arg.Any<ImportDataTask>(), Arg.Any<Entities>())
.Returns(TaskResult.Failed);
_importDataProvider.ReadFromFile(DataFilePath).Returns(datasets);
_importDataProvider.ReadSchemaFromFile(SchemaFilePath).Returns(importSchema);
_schemaValidator.Validate(importSchema).Returns(new ValidationResult());
//Act
Func<Task> act = () => _importCommands.Execute();

//Assert
var ex = await act.ShouldThrowAsync<Exception>();
ex.Message.ShouldBe("Import process failed.");



}
[Fact]
public async Task GivenAnInvalidSchema_WhenTheCommandExecutes_ThenItShouldFailAndLogIssues()
Expand Down Expand Up @@ -115,7 +153,7 @@ public async Task GivenAnInvalidSchema_WhenTheCommandExecutes_ThenItShouldFailAn
}
});
//Act
Func<Task> act = () => _importCommands.Import(SchemaFilePath, DataFilePath);
Func<Task> act = () => _importCommands.Execute();

//Assert
var ex = await act.ShouldThrowAsync<Exception>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Cocona" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.7" />
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.2.7" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta6.25358.103" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.Development.json">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Reflection;

namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
namespace Dataverse.ConfigurationMigrationTool.Console.Features;
public class CommandProcessorHostingService : BackgroundService
{
private readonly CommandProcessorHostingServiceOptions _options;
private readonly IServiceScopeFactory _serviceProviderFactory;
private readonly IHostApplicationLifetime _lifetime;
private readonly IEnumerable<Assembly> _commandAssemblies;

public CommandProcessorHostingService(IOptions<CommandProcessorHostingServiceOptions> options, IServiceScopeFactory serviceProviderFactory, IHostApplicationLifetime lifetime)
public CommandProcessorHostingService(IOptions<CommandProcessorHostingServiceOptions> options, IServiceScopeFactory serviceProviderFactory, IHostApplicationLifetime lifetime, params Assembly[] commandAssemblies)
{
_options = options.Value;
_serviceProviderFactory = serviceProviderFactory;
_lifetime = lifetime;
_commandAssemblies = commandAssemblies.Length > 0 ? commandAssemblies : new[] { Assembly.GetExecutingAssembly() };
}
public CommandProcessorHostingService(IOptions<CommandProcessorHostingServiceOptions> options, IServiceScopeFactory serviceProviderFactory, IHostApplicationLifetime lifetime) : this(options, serviceProviderFactory, lifetime, [])
{

}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var types = from type in Assembly.GetExecutingAssembly().GetTypes()
var types = from type in _commandAssemblies.SelectMany(a => a.GetTypes())
where Attribute.IsDefined(type, typeof(CommandVerbAttribute)) &&
type.IsClass &&
!type.IsAbstract &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
namespace Dataverse.ConfigurationMigrationTool.Console.Features;
public class CommandProcessorHostingServiceOptions
{
public string CommandVerb { get; set; }
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using Cocona;
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ConsoleApp = System.Console;

namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands;
[CommandVerb("import")]
Expand Down Expand Up @@ -31,15 +29,12 @@ public ImportCommands(ILogger<ImportCommands> logger,
public async Task Execute() => await Import(_options.schema, _options.data);


[Command("import")]
public async Task Import([Option("schema")] string schemafilepath, [Option("data")] string datafilepath)
private async Task Import(string schemafilepath, string datafilepath)
{

var ImportQueue = new Queue<ImportDataTask>();
ConsoleApp.WriteLine($"{datafilepath} with schema {schemafilepath}");
var schema = await _importDataProvider.ReadSchemaFromFile(schemafilepath);
var importdata = await _importDataProvider.ReadFromFile(datafilepath);
ConsoleApp.WriteLine($"Schema Count: {schema.Entity.Count} | Data count {importdata.Entity.Count}");


var schemaValidationResult = await _schemaValidator.Validate(schema);
Expand All @@ -66,7 +61,7 @@ public async Task Import([Option("schema")] string schemafilepath, [Option("data
}
}


var taskResults = new List<TaskResult>();
while (ImportQueue.Count > 0)
{
var importTask = ImportQueue.Dequeue();
Expand All @@ -90,11 +85,16 @@ public async Task Import([Option("schema")] string schemafilepath, [Option("data
}
#endregion

await _importDataService.Execute(importTask, importdata);

var result = await _importDataService.Execute(importTask, importdata);
taskResults.Add(result);


}
if (taskResults.Any(r => r == TaskResult.Failed))
{
_logger.LogError("Import process failed.");
throw new Exception("Import process failed.");
}



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.PowerPlatform.Dataverse.Client;
using System.Reflection;
var builder = new HostBuilder();
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureHostConfiguration((config) =>
{
// Configure the host configuration, such as environment variables, command line arguments, etc.
Expand All @@ -33,10 +33,6 @@
config.AddUserSecrets(Assembly.GetExecutingAssembly());
}
Console.WriteLine($"Using configuration file: appsettings.{context.HostingEnvironment.EnvironmentName}.json");
foreach (var arg in context.Configuration.AsEnumerable())
{
Console.WriteLine($"Configuration: {arg.Key} => {arg.Value}");
}
});
builder.ConfigureServices((context, services) =>
{
Expand All @@ -56,44 +52,6 @@
.AddImportFeature(context.Configuration);
// Configure other services.
});
builder.ConfigureCocona(args, configureApplication: app =>
{
// Configure your app's commands normally as you would with app
app.UseImportFeature();
});

var app = builder.Build();
await app.RunAsync();
//var builder = CoconaApp.CreateBuilder();
//builder.Configuration
// .AddEnvironmentVariables()
// .AddJsonFile("appsettings.json", false, false)
// .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", false, false);
//Console.WriteLine($"Using configuration file: appsettings.{builder.Environment.EnvironmentName}.json");
//foreach (var arg in args)
//{
// Console.WriteLine($"Argument: {arg}");
//}
//if (!builder.Environment.IsProduction())
//{
// //Secrets should never be in clear text in source controlled file such appsettings.json.
// //For Developement, we therefore store them locally into UserSecrets Store, part of dotnet foundation.
// //For Production, secrets can be either written into appsettings.Production.json file by pipeline
// //or you can configure another Configuration Provider to provide the secrets like AzureKeyvault or Hashicorp Vault.
// builder.Configuration.AddUserSecrets(Assembly.GetExecutingAssembly());
//}
//builder.Services
// .AddLogging(lb => lb.AddConsole())
// .Configure<SdkDataverseServiceFactoryOptions>(builder.Configuration.GetSection("Dataverse"))
// .Configure<ParallelismBulkOrganizationServiceOptions>(builder.Configuration.GetSection("Dataverse"))
// .AddTransient<IImportDataProvider, FileReaderDataImportProvider>()
// .AddSingleton<IFileDataReader, XmlFileDataReader>()
// .AddTransient<IMetadataService, DataverseMetadataService>()
// .AddTransient<ServiceClient>((sp) => (ServiceClient)sp.GetRequiredService<IDataverseClientFactory>().Create())
// .AddSingleton<IBulkOrganizationService, ParallelismBulkOrganizationService>()
// .AddDataverseClient()
// .AddImportFeature();
//Console.WriteLine($"Services are completed");
//var app = builder.Build();
//app.UseImportFeature();
//Console.WriteLine($"Running App");
//await app.RunAsync();
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ public SdkDataverseServiceFactory(IOptions<SdkDataverseServiceFactoryOptions> op
}
public IOrganizationServiceAsync2 Create()
{
_logger.LogInformation("ClientId Length", _options.ClientId.ToString().Length);
_logger.LogInformation("ClientSecret Length", _options.ClientSecret.ToString().Length);
_logger.LogInformation("Url Length", _options.Url.ToString().Length);
// throw new InvalidOperationException($"test: {_options.Url} {_options.ClientId} {_options.ClientSecret}");
var serviceClient = new ServiceClient(
new Uri(_options.Url),
_options.ClientId.ToString(),
Expand Down
Loading