From 3e9f0347be7ee19f6f872e1009570d65fbf6a457 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 31 Jul 2025 06:49:18 -0400 Subject: [PATCH 1/4] Moving some Classes to Shared Folder For common logic and init of Export Data Command --- .../Console.Tests/FakeDatasets.cs | 2 +- .../Console.Tests/FakeSchemas.cs | 2 +- .../Import/Commands/ImportCommandsTest.cs | 11 +-- .../Import/ImportTaskProcessorServiceTests.cs | 1 + .../FieldSchemaToAttributeTypeMapperTests.cs | 2 +- .../EntitySchemaValidatorTests.cs | 10 +-- .../BaseFieldSchemaValidationRuleTests.cs | 6 +- ...stMatchWithAttributeValidationRuleTests.cs | 2 +- ...eldsTargetsMustMatchValidationRuleTests.cs | 2 +- ...eEntityNameMustMatchValidationRuleTests.cs | 2 +- ...tEntityNameMustMatchValidationRuleTests.cs | 2 +- .../Validators/Rules/SchemaValidatorTests.cs | 8 +-- .../FileReaderDataImportProviderTests.cs | 9 +-- .../Filesystem/XmlFileDataReaderTests.cs | 4 +- .../Common/IOrganizationServiceExtensions.cs | 42 +++++++++++ .../Features/Export/Commands/ExportCommand.cs | 49 +++++++++++++ .../Export/Commands/ExportCommandOption.cs | 6 ++ .../Features/Export/DataExportService.cs | 63 ++++++++++++++++ .../Mappers/DataverseRecordToRecordMapper.cs | 36 ++++++++++ .../Mappers/EntityFieldValueToFieldMapper.cs | 72 +++++++++++++++++++ .../Import/Commands/ImportCommands.cs | 7 +- .../Features/Import/IImportDataProvider.cs | 4 +- .../Import/IServiceCollectionExtensions.cs | 12 ++-- .../Import/ImportTaskProcessorService.cs | 8 +-- .../FieldSchemaToAttributeTypeMapper.cs | 4 +- .../Features/Import/Model/ImportDataTask.cs | 4 +- .../IFieldSchemaValidationRule.cs | 10 --- ...kupFieldsTargetsMustMatchValidationRule.cs | 21 ------ .../Import/Validators/SchemaValidator.cs | 35 --------- .../DataverseValueConverter.cs | 2 +- .../IDataverseValueConverter.cs | 2 +- .../Domain/DataSchema.cs} | 4 +- .../Model => Shared/Domain}/EntityImport.cs | 2 +- .../Shared}/IFileDataReader.cs | 2 +- .../Features/Shared/TaskResult.cs | 7 ++ .../EntitySchemas/EntitySchemaValidator.cs | 11 +-- ...ypeMustMatchWithAttributeValidationRule.cs | 5 +- .../IFieldSchemaValidationRule.cs | 9 +++ ...kupFieldsTargetsMustMatchValidationRule.cs | 20 ++++++ .../IRelationshipSchemaValidationRule.cs | 5 +- ...SourceEntityNameMustMatchValidationRule.cs | 5 +- ...TargetEntityNameMustMatchValidationRule.cs | 5 +- .../Validators/Rules/RuleResult.cs | 2 +- .../Shared/Validators/SchemaValidator.cs | 33 +++++++++ .../FileReaderDataImportProvider.cs | 46 ++++++------ .../Services/Filesystem/XmlFileDataReader.cs | 3 +- 46 files changed, 438 insertions(+), 161 deletions(-) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Common/IOrganizationServiceExtensions.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommandOption.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs delete mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs delete mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs delete mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/SchemaValidator.cs rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import/Model/ImportSchema.cs => Shared/Domain/DataSchema.cs} (98%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import/Model => Shared/Domain}/EntityImport.cs (99%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/{Services/Filesystem => Features/Shared}/IFileDataReader.cs (55%) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/TaskResult.cs rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs (90%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs (91%) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs (59%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs (77%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs (77%) rename src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/{Import => Shared}/Validators/Rules/RuleResult.cs (94%) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/SchemaValidator.cs diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeDatasets.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeDatasets.cs index a8eee6c..b6153ea 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeDatasets.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeDatasets.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; namespace Dataverse.ConfigurationMigrationTool.Console.Tests; internal static class FakeDatasets diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeSchemas.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeSchemas.cs index 701b595..05bd27b 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeSchemas.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeSchemas.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; namespace Dataverse.ConfigurationMigrationTool.Console.Tests; internal static class FakeSchemas diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Commands/ImportCommandsTest.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Commands/ImportCommandsTest.cs index 1df1f05..722186c 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Commands/ImportCommandsTest.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Commands/ImportCommandsTest.cs @@ -2,6 +2,7 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Tests.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -13,7 +14,7 @@ public class ImportCommandsTest { private readonly ILogger _logger; private readonly IImportDataProvider _importDataProvider; - private readonly IValidator _schemaValidator; + private readonly IValidator _schemaValidator; private readonly IImportTaskProcessorService _importDataService; private readonly ImportCommands _importCommands; const string DataFilePath = "data.json"; @@ -28,7 +29,7 @@ public ImportCommandsTest() { _logger = Substitute.For>(); _importDataProvider = Substitute.For(); - _schemaValidator = Substitute.For>(); + _schemaValidator = Substitute.For>(); _importDataService = Substitute.For(); var options = Substitute.For>(); options.Value.Returns(_options); @@ -43,7 +44,7 @@ public ImportCommandsTest() public async Task GivenDataToImportWithSchema_WhenTheCommandExecutes_ThenItShouldProcessImportsAccordingly() { //Arrange - var importSchema = new ImportSchema + var importSchema = new DataSchema { Entity = new() { @@ -84,7 +85,7 @@ public async Task GivenDataToImportWithSchema_WhenTheCommandExecutes_ThenItShoul public async Task GivenDataToImportWithSchema_WhenTheCommandExecutesAndFails_ThenItShouldThrowAnError() { //Arrange - var importSchema = new ImportSchema + var importSchema = new DataSchema { Entity = new() { @@ -122,7 +123,7 @@ public async Task GivenDataToImportWithSchema_WhenTheCommandExecutesAndFails_The public async Task GivenAnInvalidSchema_WhenTheCommandExecutes_ThenItShouldFailAndLogIssues() { //Arrange - var importSchema = new ImportSchema + var importSchema = new DataSchema { Entity = new() { diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ImportTaskProcessorServiceTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ImportTaskProcessorServiceTests.cs index 333607c..ce456a0 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ImportTaskProcessorServiceTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ImportTaskProcessorServiceTests.cs @@ -3,6 +3,7 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Connection; using Dataverse.ConfigurationMigrationTool.Console.Tests.Extensions; using Dataverse.ConfigurationMigrationTool.Console.Tests.FakeBuilders; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Mappers/FieldSchemaToAttributeTypeMapperTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Mappers/FieldSchemaToAttributeTypeMapperTests.cs index 49fa70d..514f529 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Mappers/FieldSchemaToAttributeTypeMapperTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Mappers/FieldSchemaToAttributeTypeMapperTests.cs @@ -1,5 +1,5 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Mappers; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs index 2be8437..c03316c 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs @@ -1,9 +1,9 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; using NSubstitute; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs index d51873a..9323b77 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs @@ -1,6 +1,6 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs index a0aa0f0..3e39259 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs @@ -1,5 +1,5 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs index f28851e..148a69d 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs @@ -1,5 +1,5 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs index db04d2e..1fad25c 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs @@ -1,5 +1,5 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs index f114d5e..b338372 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs @@ -1,5 +1,5 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs index fb8cae6..7bfb59a 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs @@ -1,6 +1,6 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators; +using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using NSubstitute; using Shouldly; @@ -8,7 +8,7 @@ namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Val public class SchemaValidatorTests { private readonly IValidator entityValidator = Substitute.For>(); - private readonly IValidator validator; + private readonly IValidator validator; public SchemaValidatorTests() { validator = new SchemaValidator(entityValidator); @@ -18,7 +18,7 @@ public SchemaValidatorTests() public async Task GivenValidSchema_WhenValidated_ThenShouldReturnSuccess() { // Arrange - var schema = new ImportSchema + var schema = new DataSchema { Entity = new List { diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs index 6f00c75..4753df9 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs @@ -1,4 +1,5 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem; using Microsoft.Extensions.Logging; using NSubstitute; @@ -21,13 +22,13 @@ public async Task GivenAnImportSchema_WhenProviderReadsTheImportSchema_ThenItSho { // Arrange var filePath = "test-schema.json"; - var importSchema = new ImportSchema(); - _dataReader.ReadAsync(filePath).Returns(importSchema); + var importSchema = new DataSchema(); + _dataReader.ReadAsync(filePath).Returns(importSchema); // Act var result = await _fileReaderDataImportProvider.ReadSchemaFromFile(filePath); // Assert result.ShouldBe(importSchema); - await _dataReader.Received(1).ReadAsync(filePath); + await _dataReader.Received(1).ReadAsync(filePath); } [Fact] public async Task GivenAnEntityImport_WhenProviderReadsTheEntityData_ThenItShouldUseDataReader() diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/XmlFileDataReaderTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/XmlFileDataReaderTests.cs index b0e3bfe..a5ec690 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/XmlFileDataReaderTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/XmlFileDataReaderTests.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem; using Shouldly; @@ -12,7 +12,7 @@ public async Task GivenAnXmlFile_WhenTheDataReaderReadsIt_ThenItShouldReturnData // Arrange var filePath = "assets/schema.xml"; // Path to your test XML file // Act - var result = await _xmlFileDataReader.ReadAsync(filePath); + var result = await _xmlFileDataReader.ReadAsync(filePath); // Assert result.ShouldNotBeNull(); var entity = result.Entity.First(); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Common/IOrganizationServiceExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Common/IOrganizationServiceExtensions.cs new file mode 100644 index 0000000..a16105b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Common/IOrganizationServiceExtensions.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Logging; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Query; + +namespace Dataverse.ConfigurationMigrationTool.Console.Common; +public static class IOrganizationServiceExtensions +{ + public static async Task RetrieveAll(this IOrganizationServiceAsync2 service, QueryExpression query, int page = 5000, ILogger _logger = null, CancellationToken cancellationToken = default) + { + // The records to return + List entities = new(); + + // Set the page + query.PageInfo.PageNumber = 1; + // Set the count + query.PageInfo.Count = page; + + while (true) + { + _logger?.LogInformation("Retrieving page {page} with {count} records", query.PageInfo.PageNumber, query.PageInfo.Count); + // Get the records + var results = await service.RetrieveMultipleAsync(query, cancellationToken); + + entities.AddRange(results.Entities); + _logger?.LogInformation("Retrieved {count} records from page {page}", results.Entities.Count, query.PageInfo.PageNumber); + _logger?.LogInformation("Total Retrieved {count} records", entities.Count); + if (!results.MoreRecords) + { + //Stop if there are no more records + break; + } + // Set the PagingCookie with the PagingCookie from the previous query + query.PageInfo.PagingCookie = results.PagingCookie; + + // Update the PageNumber + query.PageInfo.PageNumber++; + } + + return new EntityCollection(entities); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs new file mode 100644 index 0000000..c6b56cd --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs @@ -0,0 +1,49 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Commands; +public class ExportCommand : ICommand +{ + private readonly ILogger _logger; + private readonly ExportCommandOption _options; + private readonly IValidator _schemaValidator; + private readonly IFileDataReader _fileDataReader; + + public ExportCommand(ILogger logger, + IOptions options, + IValidator schemaValidator, + IFileDataReader fileDataReader) + { + _logger = logger; + _options = options.Value; + _schemaValidator = schemaValidator; + _fileDataReader = fileDataReader; + } + + public async Task Execute() => await Export(_options.schema, _options.output); + + private async Task Export(string schemafilepath, string outputfilepath) + { + + _logger.LogInformation("Parsing schema file from arguments"); + var schema = await _fileDataReader.ReadAsync(schemafilepath); + + var schemaValidationResult = await _schemaValidator.Validate(schema); + if (schemaValidationResult.IsError) + { + _logger.LogError("Schema failed validation process with {count} failure(s).", schemaValidationResult.Failures.Count); + foreach (var failure in schemaValidationResult.Failures) + { + _logger.LogError("schema validation failure: {property} => {failure}", failure.PropertyBound, failure.Message); + } + throw new Exception("Provided Schema was not valid."); + } + _logger.LogInformation("Schema validation succeeded."); + + + + } + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommandOption.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommandOption.cs new file mode 100644 index 0000000..5f04c26 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommandOption.cs @@ -0,0 +1,6 @@ +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Commands; +public class ExportCommandOption +{ + public string schema { get; set; } + public string output { get; set; } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs new file mode 100644 index 0000000..31e4c3b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs @@ -0,0 +1,63 @@ +using Dataverse.ConfigurationMigrationTool.Console.Common; +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Extensions.Logging; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; +using Microsoft.Xrm.Sdk.Query; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export; +public interface IDataExportService +{ + Task ExportToFile(DataSchema Schema, string outputfile); +} +public class DataExportService : IDataExportService +{ + private readonly ILogger _logger; + private readonly IOrganizationServiceAsync2 _organizationServiceAsync2; + private readonly IMetadataService _metadataService; + private static readonly IMapper<(EntityMetadata, EntitySchema, Entity), Record> _recordMapper = new DataverseRecordToRecordMapper(); + public DataExportService(ILogger logger, + IOrganizationServiceAsync2 organizationServiceAsync2, + IMetadataService metadataService) + { + _logger = logger; + _organizationServiceAsync2 = organizationServiceAsync2; + _metadataService = metadataService; + } + + public async Task ExportToFile(DataSchema Schema, string outputfile) + { + var entityImports = new Dictionary(); + foreach (var entitySchema in Schema.Entity) + { + _logger.LogInformation("Exporting entity {entityName}", entitySchema.Displayname); + + var exportfields = entitySchema.Fields.Field.Select(f => f.Name).ToList(); + var metadata = await _metadataService.GetEntity(entitySchema.Name); + var query = new QueryExpression(entitySchema.Name) + { + ColumnSet = new ColumnSet(exportfields.ToArray()), + + }; + var entityCollection = await _organizationServiceAsync2.RetrieveAll(query, page: 5000, _logger); + + var data = entityCollection.Entities.Select(e => _recordMapper.Map((metadata, entitySchema, e))).ToList(); + + //Add Relationships export + + entityImports[entitySchema.Name] = new EntityImport + { + Name = entitySchema.Name, + Displayname = entitySchema.Displayname, + Records = new Records { Record = data } + }; + + } + // Write To File + + return TaskResult.Completed; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs new file mode 100644 index 0000000..76a7df4 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs @@ -0,0 +1,36 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +public class DataverseRecordToRecordMapper : IMapper<(EntityMetadata, EntitySchema, Entity), Record> +{ + private readonly static IMapper<(AttributeMetadata, FieldSchema, object), Field> _fieldMapper = new EntityFieldValueToFieldMapper(); + public Record Map((EntityMetadata, EntitySchema, Entity) source) + { + var (entityMetadata, entitySchema, entity) = source; + var record = new Record() + { + Id = entity.Id, + Field = new List() + }; + + foreach (var fieldSchema in entitySchema.Fields.Field) + { + if (entity.Contains(fieldSchema.Name)) + { + var fieldValue = entity[fieldSchema.Name]; + var attributeMetadata = entityMetadata.Attributes.FirstOrDefault(a => a.LogicalName == fieldSchema.Name); + var field = _fieldMapper.Map((attributeMetadata, fieldSchema, fieldValue)); + if (field == null) + { + continue; + } + record.Field.Add(field); + } + } + return record; + } +} + diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs new file mode 100644 index 0000000..8068c34 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs @@ -0,0 +1,72 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +public class EntityFieldValueToFieldMapper : IMapper<(AttributeMetadata, FieldSchema, object), Field> +{ + public Field Map((AttributeMetadata, FieldSchema, object) source) + { + var (attributeMetadata, fieldSchema, value) = source; + var fieldResult = new Field + { + Name = fieldSchema.Name, + }; + if (value == null) + { + return null; + } + if (value is EntityReference reference) + { + fieldResult.Lookupentity = reference.LogicalName; + fieldResult.Lookupentityname = reference.Name; + fieldResult.Value = reference.Id.ToString(); + return fieldResult; + } + if (value is OptionSetValue optionSetValue) + { + fieldResult.Value = optionSetValue.Value.ToString(); + return fieldResult; + } + if (value is bool booleanValue) + { + fieldResult.Value = booleanValue ? "True" : "False"; + return fieldResult; + } + if (value is DateTime dateTimeValue) + { + fieldResult.Value = dateTimeValue.ToString("o"); // ISO 8601 format + return fieldResult; + } + if (value is Guid guidValue) + { + fieldResult.Value = guidValue.ToString(); + return fieldResult; + } + if (value is Money moneyValue) + { + var moneyAttr = attributeMetadata as MoneyAttributeMetadata; + var precision = moneyAttr.PrecisionSource ?? 2; + fieldResult.Value = moneyValue.Value.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + return fieldResult; + } + if (value is Decimal decimalValue) + { + var decimalAttr = attributeMetadata as DecimalAttributeMetadata; + var precision = decimalAttr.Precision ?? 2; + fieldResult.Value = decimalValue.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + return fieldResult; + } + if (value is double doubleValue) + { + var doubleAttr = attributeMetadata as DoubleAttributeMetadata; + var precision = doubleAttr.Precision ?? 2; + fieldResult.Value = doubleValue.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + return fieldResult; + } + + fieldResult.Value = value?.ToString() ?? string.Empty; + return fieldResult; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Commands/ImportCommands.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Commands/ImportCommands.cs index 82ac4e2..383aa62 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Commands/ImportCommands.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Commands/ImportCommands.cs @@ -1,5 +1,6 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -9,13 +10,13 @@ public class ImportCommands : ICommand { private readonly ILogger _logger; private readonly IImportDataProvider _importDataProvider; - private readonly IValidator _schemaValidator; + private readonly IValidator _schemaValidator; private readonly IImportTaskProcessorService _importDataService; private readonly ImportCommandOptions _options; public ImportCommands(ILogger logger, IImportDataProvider importDataProvider, - IValidator schemaValidator, + IValidator schemaValidator, IImportTaskProcessorService importDataService, IOptions options) { @@ -90,7 +91,7 @@ private async Task> ProcessQueue(Queue I } return taskResults; } - private static Queue CreateQueue(ImportSchema schema) + private static Queue CreateQueue(DataSchema schema) { var ImportQueue = new Queue(); foreach (var schemaEntity in schema.Entity) diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IImportDataProvider.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IImportDataProvider.cs index f8f0f37..d72e95d 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IImportDataProvider.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IImportDataProvider.cs @@ -1,10 +1,10 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import { public interface IImportDataProvider { Task ReadFromFile(string filePath); - Task ReadSchemaFromFile(string filePath); + Task ReadSchemaFromFile(string filePath); } } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs index c5fc540..2a23d2a 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs @@ -1,12 +1,12 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Interceptors; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -28,7 +28,7 @@ public static IServiceCollection AddImportFeature(this IServiceCollection servic return new ReflectionMainConverter(valueConverterTypes); }) .AddSingleton() - .AddTransient, SchemaValidator>() + .AddTransient, SchemaValidator>() .AddTransient, EntitySchemaValidator>() .AddSingleton() .AddSingleton((sp) => diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ImportTaskProcessorService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ImportTaskProcessorService.cs index 887b135..ebc1dd5 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ImportTaskProcessorService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ImportTaskProcessorService.cs @@ -2,6 +2,7 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Connection; using Microsoft.Extensions.Logging; using Microsoft.Xrm.Sdk; @@ -10,12 +11,7 @@ namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import; -public enum TaskResult -{ - Completed, - Failed, - Requeue -} + public interface IImportTaskProcessorService { Task Execute(ImportDataTask task, Entities dataImport); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Mappers/FieldSchemaToAttributeTypeMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Mappers/FieldSchemaToAttributeTypeMapper.cs index 2f16db5..0a7d874 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Mappers/FieldSchemaToAttributeTypeMapper.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Mappers/FieldSchemaToAttributeTypeMapper.cs @@ -1,5 +1,5 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk.Metadata; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Mappers; diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportDataTask.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportDataTask.cs index 88790f6..3c43b8a 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportDataTask.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportDataTask.cs @@ -1,4 +1,6 @@ -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model { public class ImportDataTask { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs deleted file mode 100644 index 5985278..0000000 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Microsoft.Xrm.Sdk.Metadata; - -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas -{ - public interface IFieldSchemaValidationRule - { - Task Validate(FieldSchema fieldSchema, AttributeMetadata attributeMetadata); - } -} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs deleted file mode 100644 index d7a356b..0000000 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Dataverse.ConfigurationMigrationTool.Console.Common; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Microsoft.Xrm.Sdk.Metadata; - -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas -{ - public class LookupFieldsTargetsMustMatchValidationRule : IFieldSchemaValidationRule - { - public async Task Validate(FieldSchema fieldSchema, AttributeMetadata attributeMetadata) - { - if (attributeMetadata is LookupAttributeMetadata lookupMD && - lookupMD.AttributeType != AttributeTypeCode.Owner && - !lookupMD.Targets.AreEnumerablesEqualIgnoreOrder(fieldSchema.LookupType?.Split('|') ?? Array.Empty())) - { - return RuleResult.Failure($"LookupAttribute {fieldSchema.Name} targets {fieldSchema.LookupType} but it's expected to target: {string.Join("|", lookupMD.Targets)}"); - - } - return RuleResult.Success(); - } - } -} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/SchemaValidator.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/SchemaValidator.cs deleted file mode 100644 index db7b482..0000000 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/SchemaValidator.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; - -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators -{ - public class SchemaValidator : IValidator - { - private readonly IValidator entitySchemaValidator; - - public SchemaValidator(IValidator entitySchemaValidator) - { - - this.entitySchemaValidator = entitySchemaValidator; - } - - public async Task Validate(ImportSchema value) - { - var failures = new List(); - foreach (var entitySchema in value.Entity) - { - - var validationResult = await entitySchemaValidator.Validate(entitySchema); - failures.AddRange(validationResult.Failures); - - } - - return new ValidationResult - { - Failures = failures - }; - - - } - } -} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/DataverseValueConverter.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/DataverseValueConverter.cs index 97389aa..a5405e4 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/DataverseValueConverter.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/DataverseValueConverter.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Metadata; diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/IDataverseValueConverter.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/IDataverseValueConverter.cs index caa945b..cda9ebe 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/IDataverseValueConverter.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/ValueConverters/IDataverseValueConverter.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk.Metadata; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportSchema.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs similarity index 98% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportSchema.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs index cd2fe28..67e0226 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/ImportSchema.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; // using System.Xml.Serialization; // XmlSerializer serializer = new XmlSerializer(typeof(Entities)); @@ -101,7 +101,7 @@ public class EntitySchema } [XmlRoot(ElementName = "entities")] -public class ImportSchema +public class DataSchema { [XmlElement(ElementName = "entity")] diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/EntityImport.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/EntityImport.cs similarity index 99% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/EntityImport.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/EntityImport.cs index 06fb2a0..2d43a2b 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Model/EntityImport.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/EntityImport.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; // using System.Xml.Serialization; // XmlSerializer serializer = new XmlSerializer(typeof(Entities)); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/IFileDataReader.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs similarity index 55% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/IFileDataReader.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs index a79de55..a9661ef 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/IFileDataReader.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs @@ -1,4 +1,4 @@ -namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared { public interface IFileDataReader { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/TaskResult.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/TaskResult.cs new file mode 100644 index 0000000..0a7c37b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/TaskResult.cs @@ -0,0 +1,7 @@ +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +public enum TaskResult +{ + Completed, + Failed, + Requeue +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs similarity index 90% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs index 20c8933..46c1273 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidator.cs @@ -1,10 +1,11 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas { public class EntitySchemaValidator : IValidator { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs similarity index 91% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs index b401cc7..422dda0 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRule.cs @@ -1,9 +1,8 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Mappers; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; public class FieldTypeMustMatchWithAttributeValidationRule : IFieldSchemaValidationRule { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs new file mode 100644 index 0000000..4451773 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/IFieldSchemaValidationRule.cs @@ -0,0 +1,9 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; + +public interface IFieldSchemaValidationRule +{ + Task Validate(FieldSchema fieldSchema, AttributeMetadata attributeMetadata); +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs new file mode 100644 index 0000000..c725721 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRule.cs @@ -0,0 +1,20 @@ +using Dataverse.ConfigurationMigrationTool.Console.Common; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; + +public class LookupFieldsTargetsMustMatchValidationRule : IFieldSchemaValidationRule +{ + public async Task Validate(FieldSchema fieldSchema, AttributeMetadata attributeMetadata) + { + if (attributeMetadata is LookupAttributeMetadata lookupMD && + lookupMD.AttributeType != AttributeTypeCode.Owner && + !lookupMD.Targets.AreEnumerablesEqualIgnoreOrder(fieldSchema.LookupType?.Split('|') ?? Array.Empty())) + { + return RuleResult.Failure($"LookupAttribute {fieldSchema.Name} targets {fieldSchema.LookupType} but it's expected to target: {string.Join("|", lookupMD.Targets)}"); + + } + return RuleResult.Success(); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs similarity index 59% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs index dddc3df..44eb082 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/IRelationshipSchemaValidationRule.cs @@ -1,7 +1,8 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas { public interface IRelationshipSchemaValidationRule { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs similarity index 77% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs index 2b09e40..224d611 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRule.cs @@ -1,7 +1,8 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas { public class SourceEntityNameMustMatchValidationRule : IRelationshipSchemaValidationRule { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs similarity index 77% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs index a1baaec..833ddb1 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRule.cs @@ -1,7 +1,8 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas { public class TargetEntityNameMustMatchValidationRule : IRelationshipSchemaValidationRule { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/RuleResult.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/RuleResult.cs similarity index 94% rename from src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/RuleResult.cs rename to src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/RuleResult.cs index d50abbc..2d49234 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/Validators/Rules/RuleResult.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/Rules/RuleResult.cs @@ -1,4 +1,4 @@ -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules { public class RuleResult { diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/SchemaValidator.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/SchemaValidator.cs new file mode 100644 index 0000000..f694f75 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Validators/SchemaValidator.cs @@ -0,0 +1,33 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators; + +public class SchemaValidator : IValidator +{ + private readonly IValidator entitySchemaValidator; + + public SchemaValidator(IValidator entitySchemaValidator) + { + + this.entitySchemaValidator = entitySchemaValidator; + } + + public async Task Validate(DataSchema value) + { + var failures = new List(); + foreach (var entitySchema in value.Entity) + { + + var validationResult = await entitySchemaValidator.Validate(entitySchema); + failures.AddRange(validationResult.Failures); + + } + + return new ValidationResult + { + Failures = failures + }; + + + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs index 4fe27e2..625e2af 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs @@ -1,33 +1,33 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Extensions.Logging; -namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem -{ - public class FileReaderDataImportProvider : IImportDataProvider - { - private readonly IFileDataReader _dataReader; - private readonly ILogger _logger; +namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem; - public FileReaderDataImportProvider(IFileDataReader dataReader, - ILogger logger) - { - _dataReader = dataReader; - _logger = logger; - } +public class FileReaderDataImportProvider : IImportDataProvider +{ + private readonly IFileDataReader _dataReader; + private readonly ILogger _logger; - public async Task ReadFromFile(string filePath) - { - _logger.LogInformation("Loading import data from {path}", filePath); - return await _dataReader.ReadAsync(filePath); - } + public FileReaderDataImportProvider(IFileDataReader dataReader, + ILogger logger) + { + _dataReader = dataReader; + _logger = logger; + } + public async Task ReadFromFile(string filePath) + { + _logger.LogInformation("Loading import data from {path}", filePath); + return await _dataReader.ReadAsync(filePath); + } - public async Task ReadSchemaFromFile(string filePath) - { - _logger.LogInformation("Loading schema from {path}", filePath); - return await _dataReader.ReadAsync(filePath); - } + public async Task ReadSchemaFromFile(string filePath) + { + _logger.LogInformation("Loading schema from {path}", filePath); + return await _dataReader.ReadAsync(filePath); } + } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs index 0a423df..bd8452c 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs @@ -1,4 +1,5 @@ -using System.Text; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using System.Text; using System.Xml.Serialization; namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem From 86e8afc442d7561b77b8e5430a952ded18083f84 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:02:36 -0400 Subject: [PATCH 2/4] export relationships logic --- .../Features/Export/Commands/ExportCommand.cs | 7 +- .../Features/Export/DataExportService.cs | 56 ++++++++-------- .../Features/Export/IDomainService.cs | 11 +++ .../Dataverse/DataverseDomainService.cs | 67 +++++++++++++++++++ 4 files changed, 110 insertions(+), 31 deletions(-) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs index c6b56cd..9452f4d 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs @@ -10,16 +10,19 @@ public class ExportCommand : ICommand private readonly ExportCommandOption _options; private readonly IValidator _schemaValidator; private readonly IFileDataReader _fileDataReader; + private readonly IDataExportService _dataExportService; public ExportCommand(ILogger logger, IOptions options, IValidator schemaValidator, - IFileDataReader fileDataReader) + IFileDataReader fileDataReader, + IDataExportService dataExportService) { _logger = logger; _options = options.Value; _schemaValidator = schemaValidator; _fileDataReader = fileDataReader; + _dataExportService = dataExportService; } public async Task Execute() => await Export(_options.schema, _options.output); @@ -42,7 +45,7 @@ private async Task Export(string schemafilepath, string outputfilepath) } _logger.LogInformation("Schema validation succeeded."); - + var entities = await _dataExportService.ExportEntitiesFromSchema(schema); } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs index 31e4c3b..0097ec3 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs @@ -1,63 +1,61 @@ -using Dataverse.ConfigurationMigrationTool.Console.Common; -using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Extensions.Logging; -using Microsoft.PowerPlatform.Dataverse.Client; -using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Metadata; -using Microsoft.Xrm.Sdk.Query; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export; public interface IDataExportService { - Task ExportToFile(DataSchema Schema, string outputfile); + Task> ExportEntitiesFromSchema(DataSchema Schema); } public class DataExportService : IDataExportService { private readonly ILogger _logger; - private readonly IOrganizationServiceAsync2 _organizationServiceAsync2; private readonly IMetadataService _metadataService; - private static readonly IMapper<(EntityMetadata, EntitySchema, Entity), Record> _recordMapper = new DataverseRecordToRecordMapper(); + private readonly IDomainService _domainService; + public DataExportService(ILogger logger, - IOrganizationServiceAsync2 organizationServiceAsync2, - IMetadataService metadataService) + IMetadataService metadataService, + IDomainService domainService) { _logger = logger; - _organizationServiceAsync2 = organizationServiceAsync2; _metadataService = metadataService; + _domainService = domainService; } - public async Task ExportToFile(DataSchema Schema, string outputfile) + public async Task> ExportEntitiesFromSchema(DataSchema Schema) { var entityImports = new Dictionary(); foreach (var entitySchema in Schema.Entity) { _logger.LogInformation("Exporting entity {entityName}", entitySchema.Displayname); - - var exportfields = entitySchema.Fields.Field.Select(f => f.Name).ToList(); var metadata = await _metadataService.GetEntity(entitySchema.Name); - var query = new QueryExpression(entitySchema.Name) - { - ColumnSet = new ColumnSet(exportfields.ToArray()), - - }; - var entityCollection = await _organizationServiceAsync2.RetrieveAll(query, page: 5000, _logger); - - var data = entityCollection.Entities.Select(e => _recordMapper.Map((metadata, entitySchema, e))).ToList(); - + var data = await _domainService.GetRecords(metadata, entitySchema); //Add Relationships export - + var entityRelationShips = new List(); + foreach (var relationship in entitySchema.Relationships.Relationship) + { + if (!relationship.ManyToMany) + { + continue; + } + var relMD = metadata.ManyToManyRelationships.FirstOrDefault(r => r.IntersectEntityName == relationship.RelatedEntityName); + var relationships = await _domainService.GetM2mRelationships(relMD); + entityRelationShips.AddRange(relationships); + + } entityImports[entitySchema.Name] = new EntityImport { Name = entitySchema.Name, Displayname = entitySchema.Displayname, - Records = new Records { Record = data } + Records = new Records { Record = data.ToList() }, + M2mrelationships = new M2mrelationships + { + M2mrelationship = entityRelationShips + } }; } // Write To File - - return TaskResult.Completed; + return entityImports.Select(kv => kv.Value).ToList(); } } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs new file mode 100644 index 0000000..d3788a0 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs @@ -0,0 +1,11 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export; +public interface IDomainService +{ + Task> GetRecords(EntityMetadata metadata, EntitySchema Schema); + Task> GetM2mRelationships(ManyToManyRelationshipMetadata metadata); + + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs new file mode 100644 index 0000000..f784ab9 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs @@ -0,0 +1,67 @@ +using Dataverse.ConfigurationMigrationTool.Console.Common; +using Dataverse.ConfigurationMigrationTool.Console.Features.Export; +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Extensions.Logging; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; +using Microsoft.Xrm.Sdk.Query; + +namespace Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse; +public class DataverseDomainService : IDomainService +{ + private readonly IOrganizationServiceAsync2 _orgService; + private readonly ILogger _logger; + private static readonly IMapper<(EntityMetadata, EntitySchema, Entity), Record> _recordMapper = new DataverseRecordToRecordMapper(); + public DataverseDomainService(IOrganizationServiceAsync2 orgService, ILogger logger) + { + _orgService = orgService; + _logger = logger; + } + + public async Task> GetRecords(EntityMetadata metadata, EntitySchema Schema) + { + var exportfields = Schema.Fields.Field.Select(f => f.Name).ToList(); + if (exportfields.Count == 0) + { + _logger.LogWarning("No fields specified for export in schema for entity {entityName}", Schema.Name); + return Enumerable.Empty(); + } + var query = new QueryExpression(Schema.Name) + { + ColumnSet = new ColumnSet(exportfields.ToArray()), + + }; + var entityCollection = await _orgService.RetrieveAll(query, page: 5000, _logger); + + var data = entityCollection.Entities.Select(e => _recordMapper.Map((metadata, Schema, e))).ToList(); + return data; + } + + public async Task> GetM2mRelationships(ManyToManyRelationshipMetadata metadata) + { + var query = new QueryExpression(metadata.IntersectEntityName) + { + ColumnSet = new ColumnSet(true) + }; + var entityCollection = await _orgService.RetrieveAll(query, page: 5000, _logger); + + return entityCollection.Entities.Select(e => + { + return new M2mrelationship + { + Sourceid = e.GetAttributeValue(metadata.Entity1IntersectAttribute), + Targetentityname = metadata.Entity2LogicalName, + Targetentitynameidfield = metadata.Entity2IntersectAttribute, + M2mrelationshipname = metadata.IntersectEntityName, + Targetids = new Targetids + { + Targetid = entityCollection.Entities.Select(e => e.GetAttributeValue(metadata.Entity2IntersectAttribute)).ToList() + } + }; + + }).ToList(); + } +} From b80526926880e731682329d40a088a2c6cfc414d Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Sat, 2 Aug 2025 12:03:40 -0400 Subject: [PATCH 3/4] DI Refactoring and html encode strings upon export and grouping m2m rels by sourceId for export as well --- .../EntitySchemaValidatorTests.cs | 3 +- ...stMatchWithAttributeValidationRuleTests.cs | 2 +- ...eldsTargetsMustMatchValidationRuleTests.cs | 2 +- ...eEntityNameMustMatchValidationRuleTests.cs | 2 +- ...tEntityNameMustMatchValidationRuleTests.cs | 2 +- .../Validators/Rules/SchemaValidatorTests.cs | 4 +- .../DataverseValueConverterTests.cs | 4 +- .../FileReaderDataImportProviderTests.cs | 4 +- .../Features/Export/Commands/ExportCommand.cs | 15 ++++--- .../Export/IServiceCollectionExtensions.cs | 14 ++++++ .../Mappers/EntityFieldValueToFieldMapper.cs | 11 ++--- .../Features/IServiceCollectionExtensions.cs | 45 +++++++++++++++++++ .../Import/IServiceCollectionExtensions.cs | 28 ++---------- .../Features/Shared/IFileDataReader.cs | 7 --- .../Features/Shared/IFileDataService.cs | 7 +++ .../Program.cs | 12 ++--- .../Properties/launchSettings.json | 7 +++ .../Dataverse/DataverseDomainService.cs | 6 +-- .../FileReaderDataImportProvider.cs | 4 +- .../Services/Filesystem/XmlFileDataReader.cs | 27 ++++++----- 20 files changed, 133 insertions(+), 73 deletions(-) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IServiceCollectionExtensions.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/IServiceCollectionExtensions.cs delete mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs index c03316c..4b53b88 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs @@ -1,7 +1,8 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas; + using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs index 3e39259..69c0a1e 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs index 148a69d..dbbd861 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs index 1fad25c..0192bd2 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs index b338372..3ff936b 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs @@ -1,4 +1,4 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Xrm.Sdk.Metadata; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs index 7bfb59a..6d7223f 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs @@ -1,6 +1,6 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators; using NSubstitute; using Shouldly; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ValueConverters/DataverseValueConverterTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ValueConverters/DataverseValueConverterTests.cs index 938a96b..0109596 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ValueConverters/DataverseValueConverterTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/ValueConverters/DataverseValueConverterTests.cs @@ -1,5 +1,5 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model; -using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; +using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Tests.FakeBuilders; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Metadata; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs index 4753df9..ab4f3f8 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Filesystem/FileReaderDataImportProviderTests.cs @@ -8,11 +8,11 @@ namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Services.Filesystem; public class FileReaderDataImportProviderTests { - private readonly IFileDataReader _dataReader; + private readonly IFileDataService _dataReader; private readonly FileReaderDataImportProvider _fileReaderDataImportProvider; public FileReaderDataImportProviderTests() { - _dataReader = Substitute.For(); + _dataReader = Substitute.For(); _fileReaderDataImportProvider = new FileReaderDataImportProvider(_dataReader, Substitute.For>()); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs index 9452f4d..43537a9 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Commands/ExportCommand.cs @@ -4,24 +4,25 @@ using Microsoft.Extensions.Options; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Commands; +[CommandVerb("export-data")] public class ExportCommand : ICommand { private readonly ILogger _logger; private readonly ExportCommandOption _options; private readonly IValidator _schemaValidator; - private readonly IFileDataReader _fileDataReader; + private readonly IFileDataService _fileDataService; private readonly IDataExportService _dataExportService; public ExportCommand(ILogger logger, IOptions options, IValidator schemaValidator, - IFileDataReader fileDataReader, + IFileDataService fileDataReader, IDataExportService dataExportService) { _logger = logger; _options = options.Value; _schemaValidator = schemaValidator; - _fileDataReader = fileDataReader; + _fileDataService = fileDataReader; _dataExportService = dataExportService; } @@ -31,7 +32,7 @@ private async Task Export(string schemafilepath, string outputfilepath) { _logger.LogInformation("Parsing schema file from arguments"); - var schema = await _fileDataReader.ReadAsync(schemafilepath); + var schema = await _fileDataService.ReadAsync(schemafilepath); var schemaValidationResult = await _schemaValidator.Validate(schema); if (schemaValidationResult.IsError) @@ -46,7 +47,11 @@ private async Task Export(string schemafilepath, string outputfilepath) _logger.LogInformation("Schema validation succeeded."); var entities = await _dataExportService.ExportEntitiesFromSchema(schema); - + var wrapper = new Entities + { + Entity = entities.ToList() + }; + await _fileDataService.WriteAsync(wrapper, outputfilepath); } } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IServiceCollectionExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IServiceCollectionExtensions.cs new file mode 100644 index 0000000..0920064 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IServiceCollectionExtensions.cs @@ -0,0 +1,14 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Commands; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export; +public static class IServiceCollectionExtensions +{ + public static IServiceCollection AddExportFeature(this IServiceCollection services, IConfiguration Configuration) + { + services.AddScoped() + .Configure(Configuration); + return services; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs index 8068c34..5360c34 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs @@ -2,6 +2,7 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Metadata; +using System.Web; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; public class EntityFieldValueToFieldMapper : IMapper<(AttributeMetadata, FieldSchema, object), Field> @@ -48,25 +49,25 @@ public Field Map((AttributeMetadata, FieldSchema, object) source) { var moneyAttr = attributeMetadata as MoneyAttributeMetadata; var precision = moneyAttr.PrecisionSource ?? 2; - fieldResult.Value = moneyValue.Value.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + fieldResult.Value = moneyValue.Value.ToString(); return fieldResult; } if (value is Decimal decimalValue) { var decimalAttr = attributeMetadata as DecimalAttributeMetadata; var precision = decimalAttr.Precision ?? 2; - fieldResult.Value = decimalValue.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + fieldResult.Value = decimalValue.ToString(); return fieldResult; } if (value is double doubleValue) { var doubleAttr = attributeMetadata as DoubleAttributeMetadata; var precision = doubleAttr.Precision ?? 2; - fieldResult.Value = doubleValue.ToString($"F{precision}", System.Globalization.CultureInfo.InvariantCulture); + fieldResult.Value = doubleValue.ToString(); return fieldResult; } - - fieldResult.Value = value?.ToString() ?? string.Empty; + var str = value?.ToString() ?? string.Empty; + fieldResult.Value = HttpUtility.HtmlEncode(str); return fieldResult; } } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/IServiceCollectionExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/IServiceCollectionExtensions.cs new file mode 100644 index 0000000..7abd7fc --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/IServiceCollectionExtensions.cs @@ -0,0 +1,45 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export; +using Dataverse.ConfigurationMigrationTool.Console.Features.Import; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Reflection; + +namespace Dataverse.ConfigurationMigrationTool.Console.Features; +public static class IServiceCollectionExtensions +{ + public static IServiceCollection AddConfigurationMigrationTool(this IServiceCollection services, IConfiguration Configuration) + { + return services + .AddSharedServices() + .AddImportFeature(Configuration) + .AddExportFeature(Configuration); + } + public static IServiceCollection AddSharedServices(this IServiceCollection services) + { + return services.RegisterFromReflection() + .RegisterFromReflection() + .AddTransient, SchemaValidator>() + .AddTransient, EntitySchemaValidator>(); + } + public static IServiceCollection UseCommands(this IServiceCollection services, params string[] args) + { + services.AddSingleton>(Options.Create(new CommandProcessorHostingServiceOptions() { CommandVerb = args[0] })); + return services.AddHostedService(); + } + public static IServiceCollection RegisterFromReflection(this IServiceCollection services) + { + var genericType = typeof(T); + var classes = Assembly.GetCallingAssembly().GetTypes() + .Where(type => genericType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract); + foreach (var c in classes) + services.AddTransient(genericType, c); + return services; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs index 2a23d2a..2303253 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Import/IServiceCollectionExtensions.cs @@ -1,15 +1,8 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Commands; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Interceptors; using Dataverse.ConfigurationMigrationTool.Console.Features.Import.ValueConverters; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using System.Reflection; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Import; @@ -19,8 +12,7 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddImportFeature(this IServiceCollection services, IConfiguration Configuration) { - return services.RegisterFromReflection() - .RegisterFromReflection() + return services .AddSingleton(_ => { var valueConverterTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => !t.IsAbstract && @@ -28,8 +20,7 @@ public static IServiceCollection AddImportFeature(this IServiceCollection servic return new ReflectionMainConverter(valueConverterTypes); }) .AddSingleton() - .AddTransient, SchemaValidator>() - .AddTransient, EntitySchemaValidator>() + .AddSingleton() .AddSingleton((sp) => { @@ -48,18 +39,5 @@ public static T BuildService(this IServiceProvider serviceProvider) where T : return ActivatorUtilities.CreateInstance(serviceProvider); } - public static IServiceCollection UseCommands(this IServiceCollection services, params string[] args) - { - services.AddSingleton>(Options.Create(new CommandProcessorHostingServiceOptions() { CommandVerb = args[0] })); - return services.AddHostedService(); - } - public static IServiceCollection RegisterFromReflection(this IServiceCollection services) - { - var genericType = typeof(T); - var classes = Assembly.GetCallingAssembly().GetTypes() - .Where(type => genericType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract); - foreach (var c in classes) - services.AddTransient(genericType, c); - return services; - } + } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs deleted file mode 100644 index a9661ef..0000000 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataReader.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared -{ - public interface IFileDataReader - { - Task ReadAsync(string path); - } -} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs new file mode 100644 index 0000000..46daf83 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs @@ -0,0 +1,7 @@ +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared; + +public interface IFileDataService +{ + Task ReadAsync(string path); + Task WriteAsync(T obj, string path); +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Program.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Program.cs index 1fcebc1..8518e13 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Program.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Program.cs @@ -1,4 +1,6 @@ -using Dataverse.ConfigurationMigrationTool.Console.Features.Import; +using Dataverse.ConfigurationMigrationTool.Console.Features; +using Dataverse.ConfigurationMigrationTool.Console.Features.Export; +using Dataverse.ConfigurationMigrationTool.Console.Features.Import; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse; using Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Configuration; @@ -40,21 +42,21 @@ services .AddLogging(lb => lb.AddConsole()) - + .AddScoped() .Configure(context.Configuration.GetSection("Dataverse")) .Configure(context.Configuration.GetSection("Dataverse")) .AddTransient() - .AddSingleton() + .AddSingleton() .AddTransient() .AddTransient((sp) => (ServiceClient)sp.GetRequiredService().Create()) .AddSingleton() .AddDataverseClient() + .AddConfigurationMigrationTool(context.Configuration) .UseCommands(args) .AddMemoryCache() .AddTransient() .AddTransient() - .AddTransient() - .AddImportFeature(context.Configuration); + .AddTransient(); // Configure other services. }); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Properties/launchSettings.json b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Properties/launchSettings.json index 6d13587..aef2781 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Properties/launchSettings.json +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Properties/launchSettings.json @@ -6,6 +6,13 @@ "environmentVariables": { "DOTNET_ENVIRONMENT": "Development" } + }, + "export-data": { + "commandName": "Project", + "commandLineArgs": "export-data --schema ../../../TestAssets/data_schema.xml --output ../../../TestAssets/exporteddata.xml", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } } } } \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs index f784ab9..d9e2d62 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs @@ -48,17 +48,17 @@ public async Task> GetM2mRelationships(ManyToManyRe }; var entityCollection = await _orgService.RetrieveAll(query, page: 5000, _logger); - return entityCollection.Entities.Select(e => + return entityCollection.Entities.GroupBy(e => e.GetAttributeValue(metadata.Entity1IntersectAttribute)).Select(g => { return new M2mrelationship { - Sourceid = e.GetAttributeValue(metadata.Entity1IntersectAttribute), + Sourceid = g.Key, Targetentityname = metadata.Entity2LogicalName, Targetentitynameidfield = metadata.Entity2IntersectAttribute, M2mrelationshipname = metadata.IntersectEntityName, Targetids = new Targetids { - Targetid = entityCollection.Entities.Select(e => e.GetAttributeValue(metadata.Entity2IntersectAttribute)).ToList() + Targetid = g.Select(e => e.GetAttributeValue(metadata.Entity2IntersectAttribute)).ToList() } }; diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs index 625e2af..6290d7c 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/FileReaderDataImportProvider.cs @@ -7,10 +7,10 @@ namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem; public class FileReaderDataImportProvider : IImportDataProvider { - private readonly IFileDataReader _dataReader; + private readonly IFileDataService _dataReader; private readonly ILogger _logger; - public FileReaderDataImportProvider(IFileDataReader dataReader, + public FileReaderDataImportProvider(IFileDataService dataReader, ILogger logger) { _dataReader = dataReader; diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs index bd8452c..0569310 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Filesystem/XmlFileDataReader.cs @@ -2,19 +2,26 @@ using System.Text; using System.Xml.Serialization; -namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem +namespace Dataverse.ConfigurationMigrationTool.Console.Services.Filesystem; + +public class XmlFileDataReader : IFileDataService { - public class XmlFileDataReader : IFileDataReader + public async Task ReadAsync(string path) { - public async Task ReadAsync(string path) + var xml = await File.ReadAllTextAsync(path, Encoding.UTF8); + XmlSerializer serializer = new XmlSerializer(typeof(T)); + using (StringReader reader = new StringReader(xml)) { - var xml = await File.ReadAllTextAsync(path, Encoding.UTF8); - XmlSerializer serializer = new XmlSerializer(typeof(T)); - using (StringReader reader = new StringReader(xml)) - { - var data = (T)serializer.Deserialize(reader); - return data; - } + var data = (T)serializer.Deserialize(reader); + return data; } } + + public Task WriteAsync(T obj, string path) + { + var x = new XmlSerializer(typeof(T)); + using var writer = new StreamWriter(path); + x.Serialize(writer, obj); + return Task.CompletedTask; + } } From c0e062def238c0029335612e31c91795a769ed1e Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:43:00 -0400 Subject: [PATCH 4/4] Adding Unit Tests For Export Features and moving unit tests to fit the refactored code --- .../Console.Tests/CodeCoverage.runsettings | 2 +- .../FakeBuilders/FakeEntityMetadataBuilder.cs | 2 + .../Export/Commands/ExportCommandTests.cs | 77 +++++++++++ .../Features/Export/DataExportServiceTests.cs | 92 +++++++++++++ .../DataverseRecordToRecordMapperTests.cs | 35 +++++ .../EntityFieldValueToFieldMapperTests.cs | 83 ++++++++++++ .../EntitySchemaValidatorTests.cs | 5 +- .../BaseFieldSchemaValidationRuleTests.cs | 2 +- ...stMatchWithAttributeValidationRuleTests.cs | 2 +- ...eldsTargetsMustMatchValidationRuleTests.cs | 2 +- ...eEntityNameMustMatchValidationRuleTests.cs | 2 +- ...tEntityNameMustMatchValidationRuleTests.cs | 2 +- .../Validators/Rules/SchemaValidatorTests.cs | 2 +- .../Dataverse/DataverseDomainServiceTests.cs | 127 ++++++++++++++++++ .../Features/Export/DataExportService.cs | 2 +- .../Features/Export/IDomainService.cs | 2 +- .../Mappers/DataverseRecordToRecordMapper.cs | 12 +- .../Mappers/EntityFieldValueToFieldMapper.cs | 15 +-- .../Dataverse/DataverseDomainService.cs | 6 +- 19 files changed, 439 insertions(+), 33 deletions(-) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Commands/ExportCommandTests.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/DataExportServiceTests.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/DataverseRecordToRecordMapperTests.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/EntityFieldValueToFieldMapperTests.cs rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs (98%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs (93%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs (97%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs (98%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs (96%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs (96%) rename src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/{Import => Shared}/Validators/Rules/SchemaValidatorTests.cs (97%) create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Dataverse/DataverseDomainServiceTests.cs diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings index 4bfc5f9..ff2c2e1 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings @@ -6,7 +6,7 @@ cobertura,opencover [*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Connection.*,[*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Configuration.* - **/Program.cs, + **/Program.cs,**/IServiceCollectionExtensions.cs true diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeBuilders/FakeEntityMetadataBuilder.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeBuilders/FakeEntityMetadataBuilder.cs index 341125f..1243b1a 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeBuilders/FakeEntityMetadataBuilder.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/FakeBuilders/FakeEntityMetadataBuilder.cs @@ -35,6 +35,8 @@ public FakeEntityMetadataBuilder AddRelationship(string SchemaName, string Targe { Entity1LogicalName = EntityName, Entity2LogicalName = TargetEntity, + Entity1IntersectAttribute = $"{EntityName}id", + Entity2IntersectAttribute = $"{TargetEntity}id", IntersectEntityName = SchemaName, SchemaName = SchemaName }; diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Commands/ExportCommandTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Commands/ExportCommandTests.cs new file mode 100644 index 0000000..0d519c1 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Commands/ExportCommandTests.cs @@ -0,0 +1,77 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export; +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Commands; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NSubstitute; +using Shouldly; + +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Export.Commands; +public class ExportCommandTests +{ + private readonly ILogger _logger; + private readonly ExportCommandOption _options = new ExportCommandOption(); + private readonly IOptions _optionsWrapper; + private readonly IValidator _schemaValidator; + private readonly IFileDataService _fileDataService; + private readonly IDataExportService _dataExportService; + private readonly ExportCommand _exportCommand; + public ExportCommandTests() + { + _logger = Substitute.For>(); + _optionsWrapper = Substitute.For>(); + _optionsWrapper.Value.Returns(_options); + _schemaValidator = Substitute.For>(); + _fileDataService = Substitute.For(); + _dataExportService = Substitute.For(); ; + _exportCommand = new ExportCommand(_logger, _optionsWrapper, _schemaValidator, _fileDataService, _dataExportService); + } + + [Fact] + public async Task GivenACommand_WhenItExecutesWithASchema_ThenItShouldExportData() + { + // Arrange + _options.schema = "schema.json"; + _options.output = "output.json"; + + var schema = new DataSchema(); + _fileDataService.ReadAsync(_options.schema).Returns(Task.FromResult(schema)); + var validationResult = new ValidationResult(); + _schemaValidator.Validate(schema).Returns(Task.FromResult(validationResult)); + var entities = new List + { + new EntityImport { Name = "TestEntity", Displayname = "Test Entity" } + }; + _dataExportService.ExportEntitiesFromSchema(schema).Returns(Task.FromResult(entities.AsEnumerable())); + // Act + await _exportCommand.Execute(); + // Assert + await _fileDataService.Received(1).WriteAsync(Arg.Is(e => e.Entity.Count == 1), _options.output); + } + [Fact] + public async Task GivenACommand_WhenItExecutesWithAnInvalidSchema_ThenItShouldThrowError() + { + // Arrange + _options.schema = "schema.json"; + _options.output = "output.json"; + + var schema = new DataSchema(); + _fileDataService.ReadAsync(_options.schema).Returns(Task.FromResult(schema)); + var validationResult = new ValidationResult() + { + Failures = [new("Test", "property failure")] + }; + _schemaValidator.Validate(schema).Returns(Task.FromResult(validationResult)); + var entities = new List + { + new EntityImport { Name = "TestEntity", Displayname = "Test Entity" } + }; + _dataExportService.ExportEntitiesFromSchema(schema).Returns(Task.FromResult(entities.AsEnumerable())); + // Act + var ex = await _exportCommand.Execute().ShouldThrowAsync(); + // Assert + ex.Message.ShouldBe("Provided Schema was not valid."); + } + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/DataExportServiceTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/DataExportServiceTests.cs new file mode 100644 index 0000000..7373be6 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/DataExportServiceTests.cs @@ -0,0 +1,92 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Extensions.Logging; +using NSubstitute; +using Shouldly; +using Record = Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain.Record; + +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Export; +public class DataExportServiceTests +{ + private readonly ILogger _logger; + private readonly IMetadataService _metadataService; + private readonly IDomainService _domainService; + private readonly DataExportService _dataExportService; + + public DataExportServiceTests() + { + _logger = Substitute.For>(); + _metadataService = Substitute.For(); + _domainService = Substitute.For(); + _dataExportService = new DataExportService(_logger, _metadataService, _domainService); + } + [Fact] + public async Task GivenADataExportService_WhenItExportsDataFromSchema_ThenItShouldUseDomainServiceProperly() + { + //Arrange + + var schema = FakeSchemas.Contact; + var metadata = FakeMetadata.Contact; + _metadataService.GetEntity(metadata.LogicalName).Returns(metadata); + + var records = new List + { + new() + { + Id = Guid.NewGuid(), + Field = [new(){ + Name = "firstname", + Value = "John" + }, + new(){ + Name = "lastname", + Value = "Doe" + }], + }, + new() + { + Id = Guid.NewGuid(), + Field = [new(){ + Name = "firstname", + Value = "Jane" + }, + new(){ + Name = "lastname", + Value = "Dane" + }], + } + }; + var rels = new List + { + new() + { + M2mrelationshipname = "contact_opportunities", + Sourceid = Guid.NewGuid(), + Targetentityname = "opportunity", + Targetentitynameidfield = "opportunityid", + Targetids = new Targetids + { + Targetid = [Guid.NewGuid(), Guid.NewGuid()] + } + } + }; + + _domainService.GetRecords(schema).Returns(records); + _domainService.GetM2mRelationships(metadata.ManyToManyRelationships.First()) + .Returns(rels); + + //Act + var result = await _dataExportService.ExportEntitiesFromSchema(new DataSchema + { + Entity = [schema] + }); + //Assert + var entityImport = result.Single(); + entityImport.Name.ShouldBe(schema.Name); + entityImport.Displayname.ShouldBe(schema.Displayname); + entityImport.Records.Record.ShouldBe(records); + entityImport.M2mrelationships.M2mrelationship.ShouldBe(rels, true); + } + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/DataverseRecordToRecordMapperTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/DataverseRecordToRecordMapperTests.cs new file mode 100644 index 0000000..eba0964 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/DataverseRecordToRecordMapperTests.cs @@ -0,0 +1,35 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +using Microsoft.Xrm.Sdk; +using Shouldly; + +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Export.Mappers; +public class DataverseRecordToRecordMapperTests +{ + private readonly DataverseRecordToRecordMapper _mapper = new DataverseRecordToRecordMapper(); + [Fact] + public void GivenAnEntity_WhenItIsMappedToARecord_ThenItTheRecordShouldBeProperplyCreated() + { + //Arrange + var Schema = FakeSchemas.Account; + var Entity = new Entity("account") + { + Id = Guid.NewGuid(), + ["name"] = "Test Account", + ["primarycontactid"] = new EntityReference("contact", Guid.NewGuid()) + }; + //Act + var record = _mapper.Map((Schema, Entity)); + //Assert + record.Id.ShouldBe(Entity.Id); + record.Field.ForEach(field => + { + field.ShouldNotBeNull(); + Entity.Attributes.Keys.ShouldContain(field.Name); + }); + record.Field.First(f => f.Name == "name").Value.ShouldBe("Test Account"); + var lookupField = record.Field.First(f => f.Name == "primarycontactid"); + lookupField.Value.ShouldBe(Entity.GetAttributeValue("primarycontactid").Id.ToString()); + lookupField.Lookupentity.ShouldBe("contact"); + lookupField.Lookupentityname.ShouldBeNull(); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/EntityFieldValueToFieldMapperTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/EntityFieldValueToFieldMapperTests.cs new file mode 100644 index 0000000..2e6dac2 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Export/Mappers/EntityFieldValueToFieldMapperTests.cs @@ -0,0 +1,83 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk; +using Shouldly; +using System.Web; + +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Export.Mappers; +public class EntityFieldValueToFieldMapperTests +{ + private readonly EntityFieldValueToFieldMapper _mapper = new EntityFieldValueToFieldMapper(); + private static readonly DateTime CurrentDate = DateTime.UtcNow; + private static readonly Guid RandomGuid = Guid.NewGuid(); + public static TheoryData TestData => new TheoryData + { + { + new FieldSchema { Name = "testField" }, + null, + null + }, + { + new FieldSchema { Name = "testLookupField" }, + new EntityReference("testEntity", RandomGuid), + new Field { Name = "testLookupField", Lookupentity = "testEntity", Value = RandomGuid.ToString() } + }, + { + new FieldSchema { Name = "testOptionSetField" }, + new OptionSetValue(1), + new Field { Name = "testOptionSetField", Value = "1" } + }, + { + new FieldSchema { Name = "testBooleanField" }, + true, + new Field { Name = "testBooleanField", Value = "True" } + }, + { + new FieldSchema { Name = "testDateTimeField" }, + CurrentDate, + new Field { Name = "testDateTimeField", Value = CurrentDate.ToString("o") } + }, + { + new FieldSchema { Name = "testGuidField" }, + RandomGuid, + new Field { Name = "testGuidField", Value = RandomGuid.ToString() } + }, + { + new FieldSchema { Name = "testMoneyField" }, + new Money(100.50m), + new Field { Name = "testMoneyField", Value = "100.50" } + }, + { + new FieldSchema { Name = "testDecimalField" }, + 123.45m, + new Field { Name = "testDecimalField", Value = "123.45" } + }, + { + new FieldSchema { Name = "testDoubleField" }, + 123.456789, + new Field { Name = "testDoubleField", Value = "123.456789" } + }, + { + new FieldSchema { Name = "testStringField" }, + "<>Root<>''/", + new Field { Name = "testStringField", Value = HttpUtility.HtmlEncode("<>Root<>''/") } + } + }; + + [Theory] + [MemberData(nameof(TestData))] + public void GivenAnAttributeValue_WhenItIsMappedToAField_ThenFieldShouldBeCreatedProperly(FieldSchema schema, object value, Field expected) + { + // Act + var result = _mapper.Map((schema, value)); + // Assert + if (expected == null) + { + result.ShouldBeNull(); + } + else + { + result.ShouldBeEquivalentTo(expected); + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs similarity index 98% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs index 4b53b88..340067a 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs @@ -1,5 +1,4 @@ - -using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas; @@ -9,7 +8,7 @@ using NSubstitute; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas; public class EntitySchemaValidatorTests { private readonly IMetadataService metadataService = Substitute.For(); diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs similarity index 93% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs index 9323b77..bbe3bc0 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/BaseFieldSchemaValidationRuleTests.cs @@ -3,7 +3,7 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; using Microsoft.Xrm.Sdk.Metadata; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; public abstract class BaseFieldSchemaValidationRuleTests where TRule : IFieldSchemaValidationRule { protected TMetadata CreateAttributeMetadata() where TMetadata : AttributeMetadata diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs similarity index 97% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs index 69c0a1e..c000c27 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/FieldTypeMustMatchWithAttributeValidationRuleTests.cs @@ -3,7 +3,7 @@ using Microsoft.Xrm.Sdk.Metadata; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; public class FieldTypeMustMatchWithAttributeValidationRuleTests : BaseFieldSchemaValidationRuleTests { diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs similarity index 98% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs index dbbd861..145825e 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/FieldSchemas/LookupFieldsTargetsMustMatchValidationRuleTests.cs @@ -3,7 +3,7 @@ using Microsoft.Xrm.Sdk.Metadata; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas.FieldSchemas; public class LookupFieldsTargetsMustMatchValidationRuleTests : BaseFieldSchemaValidationRuleTests { diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs similarity index 96% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs index 0192bd2..4ad4319 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/SourceEntityNameMustMatchValidationRuleTests.cs @@ -3,7 +3,7 @@ using Microsoft.Xrm.Sdk.Metadata; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; public class SourceEntityNameMustMatchValidationRuleTests { private readonly SourceEntityNameMustMatchValidationRule _rule = new(); diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs similarity index 96% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs index 3ff936b..7e7c1db 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/EntitySchemas/RelationshipSchemas/TargetEntityNameMustMatchValidationRuleTests.cs @@ -3,7 +3,7 @@ using Microsoft.Xrm.Sdk.Metadata; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules.EntitySchemas.RelationshipSchemas; public class TargetEntityNameMustMatchValidationRuleTests { private readonly TargetEntityNameMustMatchValidationRule _rule = new(); diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/SchemaValidatorTests.cs similarity index 97% rename from src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs rename to src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/SchemaValidatorTests.cs index 6d7223f..45c3394 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/SchemaValidatorTests.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Shared/Validators/Rules/SchemaValidatorTests.cs @@ -4,7 +4,7 @@ using NSubstitute; using Shouldly; -namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules; +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Shared.Validators.Rules; public class SchemaValidatorTests { private readonly IValidator entityValidator = Substitute.For>(); diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Dataverse/DataverseDomainServiceTests.cs b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Dataverse/DataverseDomainServiceTests.cs new file mode 100644 index 0000000..3cc6d3c --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/Services/Dataverse/DataverseDomainServiceTests.cs @@ -0,0 +1,127 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse; +using Dataverse.ConfigurationMigrationTool.Console.Tests.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Query; +using NSubstitute; +using Shouldly; + +namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Services.Dataverse; +public class DataverseDomainServiceTests +{ + private readonly IOrganizationServiceAsync2 _orgService; + private readonly ILogger _logger; + private readonly DataverseDomainService _domainService; + public DataverseDomainServiceTests() + { + _orgService = Substitute.For(); + _logger = Substitute.For>(); + _domainService = new DataverseDomainService(_orgService, _logger); + } + [Fact] + public async Task GivenADomainService_WhenItExportsWithEntitySchema_ThenItShouldCallDataverseProperly() + { + //Arrange + var schema = FakeSchemas.Account; + var primaryContactField = new EntityReference("contact", Guid.NewGuid()) { Name = "John Doe" }; + var firstPage = new EntityCollection + { + MoreRecords = true + }; + firstPage.Entities.Add(new(schema.Name) + { + Id = Guid.NewGuid(), + ["name"] = "Test Account", + ["primarycontactid"] = primaryContactField + }); + var secondPage = new EntityCollection() + { + MoreRecords = false + }; + secondPage.Entities.Add(new(schema.Name) + { + Id = Guid.NewGuid(), + ["name"] = "Test2 Account", + ["primarycontactid"] = primaryContactField + }); + _orgService.RetrieveMultipleAsync(Arg.Any(), Arg.Any()) + .Returns(r => + { + + var arg = r.Arg(); + if (arg.PageInfo.PageNumber == 1) + { + return Task.FromResult(firstPage); + } + else + { + return Task.FromResult(secondPage); + } + }); + + //Act + var result = (await _domainService.GetRecords(schema)).ToList(); + + //Assert + result.Count.ShouldBe(2); + result[0].Id.ShouldBe(firstPage.Entities[0].Id); + result[0].Field.ShouldContain(f => f.Name == "name" && f.Value.ToString() == "Test Account"); + result[0].Field.ShouldContain(f => f.Name == "primarycontactid" && + f.Value == primaryContactField.Id.ToString() + && f.Lookupentityname == primaryContactField.Name + && f.Lookupentity == primaryContactField.LogicalName); + result[1].Id.ShouldBe(secondPage.Entities[0].Id); + result[1].Field.ShouldContain(f => f.Name == "name" && f.Value.ToString() == "Test2 Account"); + result[1].Field.ShouldContain(f => f.Name == "primarycontactid" && + f.Value == primaryContactField.Id.ToString() + && f.Lookupentityname == primaryContactField.Name + && f.Lookupentity == primaryContactField.LogicalName); + } + [Fact] + public async Task GivenADomainService_WhenItExportsWithEntitySchemaWithNoFields_ThenItShouldNotCallDataverse() + { + //Arrange + var schema = new EntitySchema() + { + Name = "account", + }; + + //Act + var result = await _domainService.GetRecords(schema); + + //Assert + result.ShouldBeEmpty(); + _logger.ShouldHaveLogged(LogLevel.Warning, $"No fields specified for export in schema for entity {schema.Name}"); + + } + [Fact] + public async Task GivenADomainService_WhenItExportsM2mRelationships_ThenItShouldCallDataverseProperly() + { + //Arrange + var metadata = FakeMetadata.Contact; + var m2mMetadata = metadata.ManyToManyRelationships.First(); + var response = new EntityCollection(); + response.Entities.Add(new(m2mMetadata.IntersectEntityName) + { + Id = Guid.NewGuid(), + [m2mMetadata.Entity1IntersectAttribute] = Guid.NewGuid(), + [m2mMetadata.Entity2IntersectAttribute] = Guid.NewGuid() + }); + + _orgService.RetrieveMultipleAsync(Arg.Any(), Arg.Any()) + .Returns(response); + //Act + var result = (await _domainService.GetM2mRelationships(m2mMetadata)).ToList(); + //Assert + result.Count.ShouldBe(1); + var entity = response.Entities[0]; + result[0].Sourceid.ShouldBe(entity.GetAttributeValue(m2mMetadata.Entity1IntersectAttribute)); + result[0].Targetentityname.ShouldBe(m2mMetadata.Entity2LogicalName); + result[0].Targetentitynameidfield.ShouldBe(m2mMetadata.Entity2IntersectAttribute); + result[0].Targetids.Targetid.Count.ShouldBe(1); + result[0].Targetids.Targetid[0].ShouldBe(entity.GetAttributeValue(m2mMetadata.Entity2IntersectAttribute)); + + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs index 0097ec3..4e2ad36 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/DataExportService.cs @@ -29,7 +29,7 @@ public async Task> ExportEntitiesFromSchema(DataSchema { _logger.LogInformation("Exporting entity {entityName}", entitySchema.Displayname); var metadata = await _metadataService.GetEntity(entitySchema.Name); - var data = await _domainService.GetRecords(metadata, entitySchema); + var data = await _domainService.GetRecords(entitySchema); //Add Relationships export var entityRelationShips = new List(); foreach (var relationship in entitySchema.Relationships.Relationship) diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs index d3788a0..26a1f54 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/IDomainService.cs @@ -4,7 +4,7 @@ namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export; public interface IDomainService { - Task> GetRecords(EntityMetadata metadata, EntitySchema Schema); + Task> GetRecords(EntitySchema Schema); Task> GetM2mRelationships(ManyToManyRelationshipMetadata metadata); diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs index 76a7df4..9de8902 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/DataverseRecordToRecordMapper.cs @@ -1,15 +1,14 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Metadata; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; -public class DataverseRecordToRecordMapper : IMapper<(EntityMetadata, EntitySchema, Entity), Record> +public class DataverseRecordToRecordMapper : IMapper<(EntitySchema, Entity), Record> { - private readonly static IMapper<(AttributeMetadata, FieldSchema, object), Field> _fieldMapper = new EntityFieldValueToFieldMapper(); - public Record Map((EntityMetadata, EntitySchema, Entity) source) + private readonly static IMapper<(FieldSchema, object), Field> _fieldMapper = new EntityFieldValueToFieldMapper(); + public Record Map((EntitySchema, Entity) source) { - var (entityMetadata, entitySchema, entity) = source; + var (entitySchema, entity) = source; var record = new Record() { Id = entity.Id, @@ -21,8 +20,7 @@ public Record Map((EntityMetadata, EntitySchema, Entity) source) if (entity.Contains(fieldSchema.Name)) { var fieldValue = entity[fieldSchema.Name]; - var attributeMetadata = entityMetadata.Attributes.FirstOrDefault(a => a.LogicalName == fieldSchema.Name); - var field = _fieldMapper.Map((attributeMetadata, fieldSchema, fieldValue)); + var field = _fieldMapper.Map((fieldSchema, fieldValue)); if (field == null) { continue; diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs index 5360c34..5960477 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Export/Mappers/EntityFieldValueToFieldMapper.cs @@ -1,15 +1,14 @@ using Dataverse.ConfigurationMigrationTool.Console.Features.Shared; using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Metadata; using System.Web; namespace Dataverse.ConfigurationMigrationTool.Console.Features.Export.Mappers; -public class EntityFieldValueToFieldMapper : IMapper<(AttributeMetadata, FieldSchema, object), Field> +public class EntityFieldValueToFieldMapper : IMapper<(FieldSchema, object), Field> { - public Field Map((AttributeMetadata, FieldSchema, object) source) + public Field Map((FieldSchema, object) source) { - var (attributeMetadata, fieldSchema, value) = source; + var (fieldSchema, value) = source; var fieldResult = new Field { Name = fieldSchema.Name, @@ -47,22 +46,16 @@ public Field Map((AttributeMetadata, FieldSchema, object) source) } if (value is Money moneyValue) { - var moneyAttr = attributeMetadata as MoneyAttributeMetadata; - var precision = moneyAttr.PrecisionSource ?? 2; fieldResult.Value = moneyValue.Value.ToString(); return fieldResult; } - if (value is Decimal decimalValue) + if (value is decimal decimalValue) { - var decimalAttr = attributeMetadata as DecimalAttributeMetadata; - var precision = decimalAttr.Precision ?? 2; fieldResult.Value = decimalValue.ToString(); return fieldResult; } if (value is double doubleValue) { - var doubleAttr = attributeMetadata as DoubleAttributeMetadata; - var precision = doubleAttr.Precision ?? 2; fieldResult.Value = doubleValue.ToString(); return fieldResult; } diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs index d9e2d62..4be8b19 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Services/Dataverse/DataverseDomainService.cs @@ -14,14 +14,14 @@ public class DataverseDomainService : IDomainService { private readonly IOrganizationServiceAsync2 _orgService; private readonly ILogger _logger; - private static readonly IMapper<(EntityMetadata, EntitySchema, Entity), Record> _recordMapper = new DataverseRecordToRecordMapper(); + private static readonly IMapper<(EntitySchema, Entity), Record> _recordMapper = new DataverseRecordToRecordMapper(); public DataverseDomainService(IOrganizationServiceAsync2 orgService, ILogger logger) { _orgService = orgService; _logger = logger; } - public async Task> GetRecords(EntityMetadata metadata, EntitySchema Schema) + public async Task> GetRecords(EntitySchema Schema) { var exportfields = Schema.Fields.Field.Select(f => f.Name).ToList(); if (exportfields.Count == 0) @@ -36,7 +36,7 @@ public async Task> GetRecords(EntityMetadata metadata, Entit }; var entityCollection = await _orgService.RetrieveAll(query, page: 5000, _logger); - var data = entityCollection.Entities.Select(e => _recordMapper.Map((metadata, Schema, e))).ToList(); + var data = entityCollection.Entities.Select(e => _recordMapper.Map((Schema, e))).ToList(); return data; }