diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-linux-pipeline-validation.yml similarity index 98% rename from .github/workflows/ci-pipeline-validation.yml rename to .github/workflows/ci-linux-pipeline-validation.yml index 7c5400c..a6b8c41 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-linux-pipeline-validation.yml @@ -1,7 +1,7 @@ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net -name: CI - Pipeline - Validation +name: CI - Linux - Pipeline - Validation permissions: contents: read issues: read @@ -24,6 +24,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x + - name: Restore dependencies run: dotnet restore ${{ env.solutionPath }} - name: Build diff --git a/.github/workflows/ci-msbuild-pipeline-validation.yml b/.github/workflows/ci-msbuild-pipeline-validation.yml new file mode 100644 index 0000000..a3c22d0 --- /dev/null +++ b/.github/workflows/ci-msbuild-pipeline-validation.yml @@ -0,0 +1,50 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: CI - msbuild - Pipeline - Validation +permissions: + contents: read + issues: read + checks: write + pull-requests: write +on: + pull_request: + branches: [ "main" ] +env: + solutionPath: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln' + xrmToolboxPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj' +jobs: + build: + + runs-on: windows-latest + + steps: + - name: git configure long path + run: git config --global core.longpaths true + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + + - name: Setup NuGet + uses: nuget/setup-nuget@v1 + with: + nuget-version: 'latest' # Or a specific version like '5.11.1' + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.solutionPath }} # Replace with your solution file or project directory + - name: Build xrmToolBox + run: msbuild ${{ env.xrmToolboxPath }} -t:rebuild -verbosity:diag -property:Configuration=Release + + + + + + + + + diff --git a/.github/workflows/ci-tag-release.yml b/.github/workflows/ci-tag-release.yml index e694df5..ac0f3c6 100644 --- a/.github/workflows/ci-tag-release.yml +++ b/.github/workflows/ci-tag-release.yml @@ -1,4 +1,5 @@ name: Create Release on Tag + on: push: # Sequence of patterns matched against refs/tags @@ -10,10 +11,12 @@ env: solutionPath: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln' solutionFolder: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool' toolpublishlocation: '${{ github.workspace }}/configurationmigrationtool' + xtbpublishlocation: '${{ github.workspace }}/xtb' + xrmToolboxPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj' jobs: - build: + buildLinux: runs-on: ubuntu-latest @@ -37,17 +40,72 @@ jobs: with: name: configurationmigrationtool path: ${{env.toolpublishlocation}} + + buildwindows: + + runs-on: windows-latest + + steps: + - name: git configure long path + run: git config --global core.longpaths true + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v4.1.0 + with: + versionSpec: '6.3.x' + - name: Determine Version + id: gitversion # step id used as reference for output values + uses: gittools/actions/gitversion/execute@v4.1.0 + with: + updateAssemblyInfo: true + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + + - name: Setup NuGet + uses: nuget/setup-nuget@v1 + with: + nuget-version: 'latest' # Or a specific version like '5.11.1' + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.solutionPath }} # Replace with your solution file or project directory + - name: Build xrmToolBox + run: msbuild ${{ env.xrmToolboxPath }} -t:rebuild -verbosity:diag -property:Configuration=Release + + - name: Nuget Pack + run: nuget pack src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XTB.nuspec -Version ${{ steps.gitversion.outputs.assemblySemFileVer }} + - name: Create destination folder + run: mkdir -p ${{env.xtbpublishlocation}} + + - name: Copy files using wildcard + run: cp *.nupkg ${{env.xtbpublishlocation}} + - name: Upload tool artifact + uses: actions/upload-artifact@v4 + with: + name: configurationmigrationtool_xrmtoolbox + path: ${{env.xtbpublishlocation}} release: name: Create Release from tag - needs: build + needs: [ buildLinux, buildwindows] runs-on: ubuntu-latest steps: - - name: Download tool artifact + - name: Download clitool artifact uses: actions/download-artifact@v4 with: name: configurationmigrationtool path: '${{ github.workspace }}/configurationmigrationtool' + - name: Download xtbtool artifact + uses: actions/download-artifact@v4 + with: + name: configurationmigrationtool_xrmtoolbox + path: '${{ env.xtbpublishlocation }}' - name: zip artifact # This would actually build your project, using zip for an example artifact run: | zip -r configurationmigrationtool${{ github.ref_name }}.zip configurationmigrationtool @@ -61,7 +119,25 @@ jobs: release_name: Release ${{ github.ref }} draft: false prerelease: false - - name: Upload Release Asset + - name: Get nupkg path + id: get_nupkg_path + run: | + NUPKG_PATH=$(ls ./xtb/*.nupkg | head -n 1) + FILENAME=$(basename "$NUPKG_PATH") + echo "nupkg_path=$NUPKG_PATH" >> "$GITHUB_OUTPUT" + echo "filename=$FILENAME" >> "$GITHUB_OUTPUT" # Make it available to subsequent steps + + - name: Upload NuGet Package Release Asset + id: upload-nuget-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ${{ steps.get_nupkg_path.outputs.nupkg_path }} + asset_name: ${{ steps.get_nupkg_path.outputs.filename }} + asset_content_type: application/zip + - name: Upload CLI Release Asset id: upload-release-asset uses: actions/upload-release-asset@v1 env: diff --git a/.github/workflows/tag-with-gitversion.yml b/.github/workflows/tag-with-gitversion.yml new file mode 100644 index 0000000..2270cf6 --- /dev/null +++ b/.github/workflows/tag-with-gitversion.yml @@ -0,0 +1,42 @@ +name: Tag with GitVersion + +on: + workflow_dispatch: + +permissions: + contents: write + +jobs: + tag: + name: Create tag from GitVersion + runs-on: ubuntu-latest + # additional safety to ensure this runs for main branch only + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout repository (full history) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v4.1.0 + with: + versionSpec: '6.3.x' + - name: Determine Version + id: gitversion # step id used as reference for output values + uses: gittools/actions/gitversion/execute@v4.1.0 + + - name: Create and push annotated tag (minor+1, patch=0) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + MAJOR="${{ steps.gitversion.outputs.major }}" + MINOR="${{ steps.gitversion.outputs.minor }}" + # increment minor and set patch to 0 + NEW_MINOR=$((MINOR + 1)) + TAG="v${MAJOR}.${NEW_MINOR}.0" + + git tag -a $TAG -m "Tag created by GitHub Actions using GitVersion (incremented minor)" + git push origin $TAG + diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..ae9ec54 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,101 @@ +assembly-versioning-scheme: MajorMinorPatch +assembly-file-versioning-scheme: MajorMinorPatch +tag-prefix: '[vV]?' +version-in-branch-pattern: (?[vV]?\d+(\.\d+)?(\.\d+)?).* +major-version-bump-message: \+semver:\s?(breaking|major) +minor-version-bump-message: \+semver:\s?(feature|minor) +patch-version-bump-message: \+semver:\s?(fix|patch) +no-bump-message: \+semver:\s?(none|skip) +tag-pre-release-weight: 60000 +commit-date-format: yyyy-MM-dd +merge-message-formats: {} +update-build-number: true +semantic-version-format: Strict +strategies: +- ConfiguredNextVersion +- Mainline +branches: + main: + mode: ContinuousDeployment + label: '' + increment: Patch + prevent-increment: + of-merged-branch: true + track-merge-target: false + track-merge-message: true + regex: ^master$|^main$ + source-branches: [] + is-source-branch-for: [] + tracks-release-branches: false + is-release-branch: false + is-main-branch: true + pre-release-weight: 55000 + feature: + mode: ContinuousDelivery + label: '{BranchName}' + increment: Patch + prevent-increment: + when-current-commit-tagged: false + track-merge-message: true + regex: ^features?[\/-](?.+) + source-branches: + - main + is-source-branch-for: [] + is-main-branch: false + pre-release-weight: 30000 + hotfix: + mode: ContinuousDelivery + label: '{BranchName}' + increment: Patch + prevent-increment: + when-current-commit-tagged: false + regex: ^hotfix(es)?[\/-](?.+) + source-branches: + - main + is-source-branch-for: [] + is-release-branch: true + is-main-branch: false + pre-release-weight: 30000 + pull-request: + mode: ContinuousDelivery + label: PullRequest{Number} + increment: Inherit + prevent-increment: + of-merged-branch: true + when-current-commit-tagged: false + track-merge-message: true + regex: ^(pull-requests|pull|pr)[\/-](?\d*) + source-branches: + - main + - feature + - hotfix + is-source-branch-for: [] + pre-release-weight: 30000 + unknown: + increment: Patch + prevent-increment: + when-current-commit-tagged: false + regex: (?.+) + source-branches: + - main + is-source-branch-for: [] + pre-release-weight: 30000 +ignore: + sha: [] + paths: [] +mode: ContinuousDelivery +label: '{BranchName}' +increment: Inherit +prevent-increment: + of-merged-branch: false + when-branch-merged: false + when-current-commit-tagged: true +track-merge-target: false +track-merge-message: true +commit-message-incrementing: Enabled +regex: '' +source-branches: [] +is-source-branch-for: [] +tracks-release-branches: false +is-release-branch: false +is-main-branch: false \ No newline at end of file diff --git a/images/configurationmigrationtool_64.png b/images/configurationmigrationtool_64.png new file mode 100644 index 0000000..f89f2c7 Binary files /dev/null and b/images/configurationmigrationtool_64.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings index ff2c2e1..d70738f 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings +++ b/src/Dataverse.ConfigurationMigrationTool/Console.Tests/CodeCoverage.runsettings @@ -5,7 +5,7 @@ cobertura,opencover - [*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Connection.*,[*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Configuration.* + [*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Connection.*,[*]Dataverse.ConfigurationMigrationTool.Console.Services.Dataverse.Configuration.*,[*]Dataverse.ConfigurationMigrationTool.XrmToolBox.* **/Program.cs,**/IServiceCollectionExtensions.cs true 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 new file mode 100644 index 0000000..e69de29 diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs index 67e0226..e28787f 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/Domain/DataSchema.cs @@ -1,110 +1,117 @@ -using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml.Serialization; -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain +{ + [XmlRoot(ElementName = "field")] + public class FieldSchema + { -// using System.Xml.Serialization; -// XmlSerializer serializer = new XmlSerializer(typeof(Entities)); -// using (StringReader reader = new StringReader(xml)) -// { -// var test = (Entities)serializer.Deserialize(reader); -// } + [XmlAttribute(AttributeName = "displayname")] + public string Displayname { get; set; } -[XmlRoot(ElementName = "field")] -public class FieldSchema -{ + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } - [XmlAttribute(AttributeName = "displayname")] - public string Displayname { get; set; } + [XmlAttribute(AttributeName = "type")] + public string Type { get; set; } - [XmlAttribute(AttributeName = "name")] - public string Name { get; set; } + [XmlAttribute(AttributeName = "customfield")] + public bool Customfield { get; set; } - [XmlAttribute(AttributeName = "type")] - public string Type { get; set; } + [XmlAttribute(AttributeName = "primaryKey")] + public bool PrimaryKey { get; set; } - [XmlAttribute(AttributeName = "customfield")] - public bool Customfield { get; set; } + [XmlAttribute(AttributeName = "lookupType")] + public string LookupType { get; set; } + public bool ShouldSerializeCustomfield() + { + return Customfield; + } + public bool ShouldSerializePrimaryKey() + { + return PrimaryKey; + } + } - [XmlAttribute(AttributeName = "primaryKey")] - public bool PrimaryKey { get; set; } + [XmlRoot(ElementName = "fields")] + public class FieldsSchema + { - [XmlAttribute(AttributeName = "lookupType")] - public string LookupType { get; set; } -} + [XmlElement(ElementName = "field")] + public List Field { get; set; } = new List(); + } -[XmlRoot(ElementName = "fields")] -public class FieldsSchema -{ + [XmlRoot(ElementName = "relationship")] + public class RelationshipSchema + { - [XmlElement(ElementName = "field")] - public List Field { get; set; } = new List(); -} + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } -[XmlRoot(ElementName = "relationship")] -public class RelationshipSchema -{ + [XmlAttribute(AttributeName = "manyToMany")] + public bool ManyToMany { get; set; } - [XmlAttribute(AttributeName = "name")] - public string Name { get; set; } + [XmlAttribute(AttributeName = "isreflexive")] + public bool Isreflexive { get; set; } - [XmlAttribute(AttributeName = "manyToMany")] - public bool ManyToMany { get; set; } + [XmlAttribute(AttributeName = "relatedEntityName")] + public string RelatedEntityName { get; set; } - [XmlAttribute(AttributeName = "isreflexive")] - public bool Isreflexive { get; set; } + [XmlAttribute(AttributeName = "m2mTargetEntity")] + public string M2mTargetEntity { get; set; } - [XmlAttribute(AttributeName = "relatedEntityName")] - public string RelatedEntityName { get; set; } + [XmlAttribute(AttributeName = "m2mTargetEntityPrimaryKey")] + public string M2mTargetEntityPrimaryKey { get; set; } + } - [XmlAttribute(AttributeName = "m2mTargetEntity")] - public string M2mTargetEntity { get; set; } + [XmlRoot(ElementName = "relationships")] + public class RelationshipsSchema + { - [XmlAttribute(AttributeName = "m2mTargetEntityPrimaryKey")] - public string M2mTargetEntityPrimaryKey { get; set; } -} + [XmlElement(ElementName = "relationship")] + public List Relationship { get; set; } = new List(); + } -[XmlRoot(ElementName = "relationships")] -public class RelationshipsSchema -{ + [XmlRoot(ElementName = "entity")] + public class EntitySchema + { - [XmlElement(ElementName = "relationship")] - public List Relationship { get; set; } = new List(); -} + [XmlElement(ElementName = "fields")] + public FieldsSchema Fields { get; set; } = new FieldsSchema(); -[XmlRoot(ElementName = "entity")] -public class EntitySchema -{ + [XmlElement(ElementName = "relationships")] + public RelationshipsSchema Relationships { get; set; } = new RelationshipsSchema(); - [XmlElement(ElementName = "fields")] - public FieldsSchema Fields { get; set; } = new FieldsSchema(); + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } - [XmlElement(ElementName = "relationships")] - public RelationshipsSchema Relationships { get; set; } = new RelationshipsSchema(); + [XmlAttribute(AttributeName = "displayname")] + public string Displayname { get; set; } - [XmlAttribute(AttributeName = "name")] - public string Name { get; set; } + [XmlAttribute(AttributeName = "etc")] + public int Etc { get; set; } - [XmlAttribute(AttributeName = "displayname")] - public string Displayname { get; set; } + [XmlAttribute(AttributeName = "primaryidfield")] + public string Primaryidfield { get; set; } - [XmlAttribute(AttributeName = "etc")] - public int Etc { get; set; } + [XmlAttribute(AttributeName = "primarynamefield")] + public string Primarynamefield { get; set; } - [XmlAttribute(AttributeName = "primaryidfield")] - public string Primaryidfield { get; set; } + [XmlAttribute(AttributeName = "disableplugins")] + public bool Disableplugins { get; set; } + } - [XmlAttribute(AttributeName = "primarynamefield")] - public string Primarynamefield { get; set; } + [XmlRoot(ElementName = "entities")] + public class DataSchema + { - [XmlAttribute(AttributeName = "disableplugins")] - public bool Disableplugins { get; set; } + [XmlElement(ElementName = "entity")] + public List Entity { get; set; } + } } -[XmlRoot(ElementName = "entities")] -public class DataSchema -{ - [XmlElement(ElementName = "entity")] - public List Entity { get; set; } -} + + diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs index 46daf83..eab15be 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.Console/Features/Shared/IFileDataService.cs @@ -1,7 +1,12 @@ -namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared; +using System.Threading.Tasks; -public interface IFileDataService +namespace Dataverse.ConfigurationMigrationTool.Console.Features.Shared { - Task ReadAsync(string path); - Task WriteAsync(T obj, string path); + public interface IFileDataService + { + Task ReadAsync(string path); + Task WriteAsync(T obj, string path); + } } + + diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XTB.nuspec b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XTB.nuspec new file mode 100644 index 0000000..c17ef1b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XTB.nuspec @@ -0,0 +1,28 @@ + + + Dataverse.ConfigurationMigrationTool.XrmToolBox + 1.0.0 + Configuration Migration Tool for XrmToolBox + François Desjardins-Nadeau + dotnetprog + https://github.com/dotnetprog/dataverse-configuration-migration-tool + https://github.com/dotnetprog/dataverse-configuration-migration-tool/blob/main/images/configurationmigrationtool_64.png?raw=true + false + Utility tool for migrating configuration data. + + Utility tool that provides an easy way to migrate configuration data between Dataverse environments. + + + Full release notes: + https://github.com/dotnetprog/dataverse-configuration-migration-tool/releases/latest + + Copyright François Desjardins-Nadeau 2025 + XrmToolBox Plugins Dataverse ConfigurationMigrationTool data export import + + + + + + + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/BindingListExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/BindingListExtensions.cs new file mode 100644 index 0000000..462b375 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/BindingListExtensions.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Common +{ + public static class BindingListExtensions + { + public static void AddRange(this BindingList list, IEnumerable values) + { + foreach (var value in values) + { + list.Add(value); + } + } + public static void RemoveRange(this BindingList list, IEnumerable values) + { + foreach (var value in values) + { + list.Remove(value); + } + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/EntityExtensions.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/EntityExtensions.cs new file mode 100644 index 0000000..7ab7b0b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/EntityExtensions.cs @@ -0,0 +1,22 @@ +using Microsoft.Xrm.Sdk; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Common +{ + public static class EntityExtensions + { + public static Entity GetAliasedEntity(this Entity entity, string alias) + { + var aliasedEntity = new Entity(); + foreach (var attribute in entity.Attributes) + { + if (attribute.Key.StartsWith(alias + ".")) + { + var attributeName = attribute.Key.Substring(alias.Length + 1); + aliasedEntity[attributeName] = ((AliasedValue)attribute.Value)?.Value; + } + } + + return aliasedEntity; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/ToolConstants.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/ToolConstants.cs new file mode 100644 index 0000000..05428ef --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/ToolConstants.cs @@ -0,0 +1,10 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Common +{ + public static class ToolConstants + { + public const string Name = "Configuration Migration Tool"; + public const string Description = "This tool is a remake of the offical Microsoft Configuration Migration Tool with extended capabilities and supports more data types."; + public const string SmallImage = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAADsAAAA7AF5KHG9AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABGdJREFUWIXtl11sVFUQx39zzildursEhUKxwAMkBEMgaQndBMRgaCTR+kAiGnkyAauBF1EjwQdTTagJkhjiJ9HEEBQ/6pMCjdJAqAaBpqBIEIgQEQ20pQrd3dK6e874sFstPuC9Ij7xT07m3nPn/mfO3Jk558It/IeY3aoT4r7j/o2hhg06QR0PY2nwBusNR4uWL3G8DjTE4ZK4xhc9pw8Vha1qGB8MBFsavizFMP3403I+Kl+sCCxer/d75XkD24LSg1ALPABMH9EpCI3AuzfFASxHDrQyF0RHppa06JOXLaeAGcCwUSbFoYz9Cf6O+S06u1jBajV0VAzT2d0igzfK+b9C5jdrRY1npRruw7AgGCYpJIMhp4aLXjiklp3VP9PW1iY+EuvHamf2siJYmrwlEyyTvSUdLHnv6PWOrqJld7aPHdK0WrsV6lVADaiURjCl+1GZvqfzZbk3iv1ZW7TDW5aOro4/pStdewfe0W00sB24cv040Q98GGn1gCgfAP3/oHYFZbsT4czYNFOygyw00KBQLZAEcqr0AAdJczjkaYzqQIAen2QqV8mgZBQmAykgr0JvELoGihyoMjQ64NPBLOeMsC8IJxCOlRVTRphUhDUM8IkxWCBSqxVhm80zFBx7EY4b4bhXcqIkBaoNPDrO8Z4XahxwFnh/sIJNCU8G2KMgoqVcEKBc9F1RI4ByFuXgsLDeGepU6RBDIoyQ6YgaZ5wfos4meKKqwBtqmKbX9oacKgcRduZTvB3VfqLAkpxldaXnTS/UYEmMejwIdKmwO1HBW8Ym2B+EfPBsbN8q94Q8qWCpTRRIdrwqaYV1KJLM8UVUB4Yq+BxBbGDTT82yLBRJOk8tSurSI5K08Lgo+eEC+6VplRYR2j18i2WBCnNUuBwMt6lhYjCMCRaCoa9zs0Rqs7O2aJ+3HFPDV0VHfbDUe8tvwTLeOyZ6S2W5HL1RaFYliOFH4DXgDmBOWY4pc54XYW3UCAisRbkaDBe0xHl7mbMWqCyr9aA85hB+D2N5SIeYh7AAeEFKJZNV6AnCIcZxkizLozoQBOtTPMggs0XIqPCSQFpLOdUXDIcHEnyXLLBCmlapqnBJDZ0K3wehTyy5AOPUUF201KllcTAMd26WSGU4a4v2e4sJlv3ecsI7+tQw4B3pomFicMzzjiXeknbAUVF2VSVpzV4lI7BXFRlpy6Mq5+uoEQCOqnLKFnhKHXVB2RcggYJcu/8ecRcsmRrPynyOjcYyNUAeSClkgYvAIZRdNedoi2r99BSWzexlhbe0BmUyUAASQA7oBboU2vMX2RFjUTcHsQ8k81u0akyRu4OhsWh4p7tFTt6IAyaO8l3P6vrKYX5VpV2V5Wn44RoFVbnzFZ0ShzPusbyXv+p4Rg7O1r+onwXHL95QEzaztOB4BrhwUxwwRTqCHTWhTAPWlDeXfhGaT6+T9jicsXNg4QY9jGOth0XeMDdYhrzjmIzlo2/WyeW4fLHRsCH+79ctXA9/AGIK2nJS1ldXAAAAAElFTkSuQmCC"; + public const string BigImage = "iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAB8lBMVEVHcEw9d/8tiv8yg/9IaP8ilv8Wpf8+c/9KZf89dP8/c/9Dbv9KZf8njv8dm/9Dbv9JZv8lkv9KZP8jlP9FbP8gmf8fmP9IZ/9Bcf9JZ/9Gaf8+c/9JZ/8aoP9Gav9FbP9Dbf8Qq/9BcP8Yov82ff8Yof9BcP9Haf9Gaf8dnP8kk/9KZP8gmP8fmf84e/8/cv8ZoP8WpP8nkP8emv9JZv8gl/9KZf86eP9KZf8/c/8+dP8lkf9Dbf8Upv9Gav8kk/9JZv8ilf8cnf8Qq/8ilv8dnP8tiP80gP86ef86eP8Up/8TqP8oj/8Vpf8WpP8MsP8Nr/8Nr/8mkv8ilf9HaP8jlP9IaP8emv8emv8gmP9KZf8ygf8zgf86ef8ri/9Acf9BcP8PrP8SqP8jlP8ilf8cnP9LZP81fv84e/8qi/8Yov8Nr/9Acf8Prf8pjf8QrP8MsP8jlP80f/82ff8vhv8Tp/8bnv8Lsv9IZ/8hlv8whP84ev8uh/8uh/8qjP8uh/9Cbv8Vpf9Ea/9AcP9MY/9IZ/9Gav8Vpf9EbP9Haf8bnv8ri/8PrP8Rqv8kk/8/c/83fP8xhP8TqP86ef8Nr/9KZf8WpP8Zof8gmP8uh/9Fa/8Yov8an/81f/8Lsf9Dbv9Cbv8zgf84e/8cnf8Ks/8em//p1m7fAAAAg3RSTlMAAQUCGy0bDO1eFSz1XxIQwPbNGEgJDWIhijX3U4fvCMlCPSI3ceGDb2VGQl3iimh+TD/J3/PXcaef6eL46n2VdjT3U9fwybDMT7yLbt7zwtSTU5+1OeS/mXGam2Lyi9mW9jjqyOeT4cDtyne4rb3O7K7wouVas/T6fG7d96L3262lj9D78NUAAAXvSURBVFjD7ZjnVxpZGIcHmEFgcEDqgCgIWEBA6UhULCio2AF777HHxEQT0ySVBJOQ3SSbTXazSf7PvQNIDegBvuwe7yccjs+5977P/O7LhaCrcTUyBrekKBgKmUyJfID3RwqnMZTTuqmp2WM1ToI8wVlKgTjcun7jy5cv396+vdc9O/0guMMqCMenu/958yYKfPvp071gcAMpaLW6GcBLAD8Fg/dLC+E1hcPpwKAj/00s0YXDa27XwYFrfS0B7ObmDaTb161KBpgQiYVMu27EgBu+vIE9dEbiD0HPzSgw6CnSO6KMAu9f85CKwiuZAkve6HaUItIiTfBB98mxkksuXioYcMr/KOQoUrxeWNdaKxK1tgl5KOtSG0UWIHpVdWNFRUVj9a0qBT+xITBPxDbRmE+evHjx9OkfzZU1siY6egGTjGhGjdquju8v//zw+fPvAy39vX16ahTXxmH+BsaTGPD587/+Pj2dm1fmKAHF53CGPn78+PNnDPjs3bvXrxYGNwGynh0IBDKBp2c1DVknSVZdCxG8NOCr9+8HqyBOIAvwzC7PBjQA3q+BX4cgS7GBeS3Zk2PJEP6ropTX5CwK5HNcM2cWZXdTAQoW0cZrYcaBzUCbBkKbrETwBVmhGV3WdsSBA/29w51U4iuRmFgZC62XALEfA7HpPDFMPMIbeNmAag9hHJnbrtYAsfuA2J1VCEY8WhyGAqZJCXy+W5RY0lFY8lZ3OT0bsLRs+UiNxRZAOl8Imdo5vPcDAiVhWmxtEjEKM1glLAaMi3kN8zL72VkuYCi0tDyuUrcrqJhAgHGpyKJ+c3to7/3XKJDDoTFpJg7b6/WyZbKa5qg2FwDHnGbzitM4dnjY23u9vyWmTQQ4yWKIhSJ2uoc5gWUaAVU9Ma5N9/AHZAsEViOZjvrTgG40GxBZDpVFe6aJjjTgNoSv0mg2oRyWylOB5Vvy7BoadsqcFRoE4x+lAveGgYjS+kkLjWbhmJKXXLOVfIL+4jzQ7GvNWqexK2nJC0PDVXzQbQBJcEmt1+KnVUbH3NZjoRjsQknW1lIA/o/frnKMaVe6ujoGBgZaWna3KzoVQCAuxBbCkUOSAXSRSCQ8OcqIBHYJr6knG3BkVC2IRDYXMej1nfqqRQVGPICoqkEISLPKw1N7Piksrtuy56ry0s6Rj8pPeYgpqvp2b0e0AWZ7RXWSejGKomK5pKG2SWa/0MOQ2bjfqNIbfAjSvlh1q7pve2jh3EPT6iTHz2TS/H6T319Z2XwZD0PLFftOsIMrKyv9/f0tA8/iHtICAREojVgyeSdd7Ox7CMRWg3fXpxlLF/s21EYL2GAiE2BTGrApqzjYaCg0EamCJk3svU1IKmHTOLa6elRYmQwsn7PmEJE74Vxadmh8yHjKDPcGO4lCsSSTRDjcSYjdXPOwAc95LJMUHmC2uSspsRd2hzuJTOQBfckwCAcbmy3jyGTsh/N1PBw8pKBZmQoEKCxQgHA4HDNeB6P3UZ9qkQr0Feghmi2WryQpCEOYURLNS5ZcN5e9ys5ZHxZNYiIMuRifFEvY7T2IiMNVoRhOHHEUFtrTumXPrc3S2NEIgiVOHTJ18dbw0O2vMbGZfg7bBs6TOnCuzHtlc82nlxDbrDUejoPzpLq6se9R727L63jAcoTCVbbpDnHsXTpgQ6MjE6PGlZ/fv7/88CElYEErUkssE0/V5qLOocwAAoebps1558CpZ0gzxb6gWXIgGDlTbKJzgNvYfoutVZj66s3Ni3O1c+0OYPb4hGoss50rfsN53hJLitcSxxuW/+5vBNzALR6MobQeuO8qi0OTonTdOnFHcFCkyxrr3ZnIFcFakSYIWcPhKLAnaYZYqTrv38vymSjwzQ3XNMIiETvq8+xs5H+3BLvD8VuRtW7XyYmr+2YwOMvPf81NceC3+K3ICVbAJjbMpN/bbMwWwoNQezg8kwS8113KL6jMrPXw3WmdO3aZdnPnmFqoibqwlUTCece6qSndtJJRuIk9Ljj1QrLgyxoGdDWuRsb4F9F177s1XI6TAAAAAElFTkSuQmCC"; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.cs new file mode 100644 index 0000000..8ad3e50 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.cs @@ -0,0 +1,521 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain.Abstraction; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Services; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.VIewModel; +using McTools.Xrm.Connection; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using XrmToolBox.Extensibility; +using XrmToolBox.Extensibility.Interfaces; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox +{ + public partial class ConfigurationMigrationControl : PluginControlBase, IAboutPlugin, IHelpPlugin, IGitHubPlugin + { + private Settings mySettings; + private string ToolVersion { get; } + private static readonly IFileService fileService = new XmlFileService(); + private ISolutionService solutionService { get; set; } + private IMetadataService metadataService { get; set; } + private SchemaTabViewModel schemaTabViewModel = new SchemaTabViewModel(); + private IReadOnlyCollection entityMetadataCache { get; set; } + + public string HelpUrl => "https://github.com/dotnetprog/dataverse-configuration-migration-tool"; + + public string RepositoryName => "dataverse-configuration-migration-tool"; + + public string UserName => "dotnetprog"; + + public ConfigurationMigrationControl() + { + InitializeComponent(); + InitializeInformativeText(); + InitializeEntityComponentsDataGridView(); + InitializeRelationshipComponentsDataGridView(); + var fvi = FileVersionInfo.GetVersionInfo(this.GetType().Assembly.Location); + this.ToolVersion = fvi.FileVersion; + } + private void InitializeInformativeText() + { + var info = @"{\rtf1\ansi\deff0 + {\fonttbl{\f0\fnil\fcharset0 Calibri;}} + \par {\b **Information**} + \par Currently , only schema file generation is available with this tool. \par + + \par Data Export and Data Import are planned to be available soon. + \par In the mean time, the generated schema file can be used to import and export data using the cli tool in the project repo. + \par A service principal that have permissions to your dataverse environment will be needed.\par + + \par {\b\i Project repo:} + \par https://github.com/dotnetprog/dataverse-configuration-migration-tool + "; + this.informativeRTC.Text = null; + this.informativeRTC.Rtf = info; + + } + #region Grid Initialization + private void InitializeEntityComponentsDataGridView() + { + this.entityMetadataGrid.AutoGenerateColumns = false; + this.entityMetadataGrid.DataSource = schemaTabViewModel.entityMetadataViewModels; + var checkboxColumn = new DataGridViewCheckBoxColumn + { + HeaderText = "Select", + Name = nameof(FieldComponentMetadataViewModel.IsSelected), + AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader, + DataPropertyName = nameof(FieldComponentMetadataViewModel.IsSelected), + + }; + this.entityMetadataGrid.Columns.Insert(0, checkboxColumn); + this.entityMetadataGrid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "Display Name", + DataPropertyName = nameof(FieldComponentMetadataViewModel.DisplayName), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, + ReadOnly = true, + MinimumWidth = 200 + }); + this.entityMetadataGrid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "LogicalName", + DataPropertyName = nameof(FieldComponentMetadataViewModel.LogicalName), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + MinimumWidth = 200, + ReadOnly = true + }); + this.entityMetadataGrid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "DataType", + DataPropertyName = nameof(FieldComponentMetadataViewModel.DataType), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + ReadOnly = true + }); + + } + private void InitializeRelationshipComponentsDataGridView() + { + this.relationshipm2m_grid.AutoGenerateColumns = false; + this.relationshipm2m_grid.DataSource = schemaTabViewModel.relationshipViewModels; + var checkboxColumn = new DataGridViewCheckBoxColumn + { + HeaderText = "Select", + Name = nameof(RelationshipComponentMetadataViewModel.IsSelected), + AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader, + DataPropertyName = nameof(RelationshipComponentMetadataViewModel.IsSelected) + }; + this.relationshipm2m_grid.Columns.Insert(0, checkboxColumn); + this.relationshipm2m_grid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "Schema Name", + DataPropertyName = nameof(RelationshipComponentMetadataViewModel.SchemaName), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + ReadOnly = true, + MinimumWidth = 200 + }); + this.relationshipm2m_grid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "Table Intersecct Name", + DataPropertyName = nameof(RelationshipComponentMetadataViewModel.TableName), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + MinimumWidth = 200, + ReadOnly = true + }); + this.relationshipm2m_grid.Columns.Add(new DataGridViewTextBoxColumn + { + HeaderText = "Target Entity", + DataPropertyName = nameof(RelationshipComponentMetadataViewModel.TargetEntity), + Resizable = DataGridViewTriState.False, + AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, + ReadOnly = true, + MinimumWidth = 300 + }); + + } + #endregion + + #region Grid Events + private void relationshipm2m_grid_CellClick(object sender, DataGridViewCellEventArgs e) + { + // Ensure it's the checkbox column and a valid row + if (e.ColumnIndex == relationshipm2m_grid.Columns[nameof(RelationshipComponentMetadataViewModel.IsSelected)].Index && e.RowIndex >= 0) + { + // Toggle the checkbox state + var item = this.schemaTabViewModel.relationshipViewModels[e.RowIndex]; + item.IsSelected = !item.IsSelected; + // Optionally, update row selection based on checkbox state + + relationshipm2m_grid.Rows[e.RowIndex].Selected = item.IsSelected; + if (!item.IsSelected) + { + fromentity_select_all_cbox.Checked = false; + } + + + } + } + private void entityMetadataGrid_CellClick(object sender, DataGridViewCellEventArgs e) + { + // Ensure it's the checkbox column and a valid row + if (e.ColumnIndex == entityMetadataGrid.Columns[nameof(FieldComponentMetadataViewModel.IsSelected)].Index && e.RowIndex >= 0) + { + // Toggle the checkbox state + var item = this.schemaTabViewModel.entityMetadataViewModels[e.RowIndex]; + item.IsSelected = !item.IsSelected; + // Optionally, update row selection based on checkbox state + + entityMetadataGrid.Rows[e.RowIndex].Selected = item.IsSelected; + if (!item.IsSelected) + { + fromentity_select_all_cbox.Checked = false; + } + + + } + } + #endregion + + #region Tool Events + private void MyPluginControl_Load(object sender, EventArgs e) + { + // Loads or creates the settings for the plugin + if (!SettingsManager.Instance.TryLoad(GetType(), out mySettings)) + { + mySettings = new Settings(); + + LogWarning("Settings not found => a new settings file has been created!"); + } + else + { + LogInfo("Settings found and loaded"); + } + } + + private void tsbClose_Click(object sender, EventArgs e) + { + CloseTool(); + } + + + private void SolutionLoadBtn_Click(object sender, EventArgs e) + { + ExecuteMethod(LoadSolutions); + } + /// + /// This event occurs when the plugin is closed + /// + /// + /// + private void MyPluginControl_OnCloseTool(object sender, EventArgs e) + { + // Before leaving, save the settings + SettingsManager.Instance.Save(GetType(), mySettings); + } + private void ResetControl() + { + this.schemaitem_listview.Items.Clear(); + this.schemaitem_listview.Groups.Clear(); + this.entitylist_cbox.DataSource = null; + this.solutionsCombbox.DataSource = null; + + + } + /// + /// This event occurs when the connection has been updated in XrmToolBox + /// + public override void UpdateConnection(IOrganizationService newService, ConnectionDetail detail, string actionName, object parameter) + { + ResetControl(); + this.schemaTabViewModel.Reset(); + base.UpdateConnection(newService, detail, actionName, parameter); + InitializeServices(); + if (mySettings != null && detail != null) + { + mySettings.LastUsedOrganizationWebappUrl = detail.WebApplicationUrl; + LogInfo("Connection has changed to: {0}", detail.WebApplicationUrl); + } + } + private void InitializeServices() + { + this.solutionService = new DataverseSolutionService(Service); + this.metadataService = new DataverseMetadataService(Service); + } + #endregion + + #region Async Workers + + private void GenerateAndExportSchemaFile() + { + WorkAsync(new WorkAsyncInfo + { + Message = "Generating Schema File", + Work = (worker, args) => + { + + args.Result = this.schemaTabViewModel.GenerateSchema(this.entityMetadataCache); + }, + PostWorkCallBack = (args) => + { + if (args.Error != null) + { + MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + var result = args.Result as DataSchema; + ExportSchemaFile(result); + } + }); + } + private void ExportSchemaFile(DataSchema schema) + { + var dialog = new SaveFileDialog(); + dialog.Filter = "XML|*.xml"; + dialog.Title = "Export Schema Definition File"; + dialog.ShowDialog(); + if (dialog.FileName != "") + { + // Saves the Image via a FileStream created by the OpenFile method. + fileService.WriteToFile(dialog.FileName, schema); + } + } + private void LoadEntityMetadata(string entityName) + { + + WorkAsync(new WorkAsyncInfo + { + Message = "Loading Entity Metadata", + Work = (worker, args) => + { + + args.Result = metadataService.GetEntityMetadata(entityName); + }, + PostWorkCallBack = (args) => + { + if (args.Error != null) + { + MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + var result = args.Result as EntityMetadata; + this.schemaTabViewModel.SetEntityMetadataSelection(result); + } + }); + } + + private void LoadEntitiesBySolutionId(Guid solutionId) + { + entitylist_cbox.SelectedIndex = -1; // Reset selection + WorkAsync(new WorkAsyncInfo + { + Message = "Loading Entities", + Work = (worker, args) => + { + args.Result = solutionService.GetEntitiesBySolutionId(solutionId); + }, + PostWorkCallBack = (args) => + { + if (args.Error != null) + { + MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + var result = args.Result as IReadOnlyCollection; + var filteredReseult = (from r in result + join emd in this.entityMetadataCache on r.EntityName equals emd.LogicalName + where emd.IsIntersect != true + select r).ToList(); + entitylist_cbox.DataSource = filteredReseult; + entitylist_cbox.Enabled = filteredReseult.Any(); + } + }); + } + private void LoadSolutions() + { + WorkAsync(new WorkAsyncInfo + { + Message = "Loading Solutions", + Work = (worker, args) => + { + args.Result = solutionService.GetSolutions(); + + }, + PostWorkCallBack = (args) => + { + if (args.Error != null) + { + MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + this.solutionsCombbox.Text = string.Empty; + var result = args.Result as IReadOnlyCollection; + this.solutionsCombbox.DataSource = result; + + + + } + }); + WorkAsync(new WorkAsyncInfo + { + Message = "Loading Entities", + Work = (worker, args) => + { + args.Result = metadataService.GetAllEntityMetadata(); + + }, + PostWorkCallBack = (args) => + { + if (args.Error != null) + { + MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + this.entityMetadataCache = (args.Result as IEnumerable).ToArray(); + } + }); + } + #endregion + + + #region TabSchema UI Handlers + private void solutioncbox_SelectedIndexChanged(object sender, EventArgs e) + { + ExecuteMethod(() => + { + this.fromentity_select_all_cbox.Checked = false; + this.schemaTabViewModel.ClearMetadata(); + var selectedSolution = this.solutionsCombbox.SelectedItem as Solution; + if (selectedSolution != null) + { + LoadEntitiesBySolutionId(selectedSolution.Id); + } + }); + } + private void entitylist_SelectedIndexChanged(object sender, EventArgs e) + { + ExecuteMethod(() => + { + this.fromentity_select_all_cbox.Checked = false; + this.schemaTabViewModel.ClearMetadata(); + var selectedEntity = this.entitylist_cbox.SelectedItem as SolutionEntityComponent; + if (selectedEntity != null) + { + LoadEntityMetadata(selectedEntity.EntityName); + } + }); + } + + + private void entityMetadataGrid_SelectionChanged(object sender, EventArgs e) + { + foreach (DataGridViewRow row in entityMetadataGrid.Rows) + { + var item = row.DataBoundItem as FieldComponentMetadataViewModel; + row.Selected = item.IsSelected; + } + } + private void relationshipm2m_grid_SelectionChanged(object sender, EventArgs e) + { + foreach (DataGridViewRow row in relationshipm2m_grid.Rows) + { + var item = row.DataBoundItem as RelationshipComponentMetadataViewModel; + row.Selected = item.IsSelected; + } + } + + private void fromentity_select_all_cbox_Click(object sender, EventArgs e) + { + + Task.Run(() => + { + foreach (DataGridViewRow row in entityMetadataGrid.Rows) + { + if (row.DataBoundItem is FieldComponentMetadataViewModel item) + { + item.IsSelected = fromentity_select_all_cbox.Checked; + row.Selected = item.IsSelected; // Update the selection state of the row + } + } + }); + Task.Run(() => + { + foreach (DataGridViewRow row in relationshipm2m_grid.Rows) + { + if (row.DataBoundItem is RelationshipComponentMetadataViewModel item) + { + item.IsSelected = fromentity_select_all_cbox.Checked; + row.Selected = item.IsSelected; // Update the selection state of the row + } + } + }); + } + + + + private void moveRightSchema_btn_Click(object sender, EventArgs e) + { + this.schemaTabViewModel.MoveRight(); + + this.schemaitem_listview.Items.Clear(); + foreach (var item in this.schemaTabViewModel.SelectedSchemaItems) + { + var listViewGroup = new ListViewGroup(item.Key, HorizontalAlignment.Left); + this.schemaitem_listview.Groups.Add(listViewGroup); + var items = item.Value + .OrderBy(i => i.schemaItemType) + .ThenBy(i => i.Name) + .Select(i => CreateListViewItem(i, listViewGroup)) + .ToList(); + this.schemaitem_listview.Items.AddRange(items.ToArray()); + } + + } + private ListViewItem CreateListViewItem(SchemaItem item, ListViewGroup group) + { + var listViewItem = new ListViewItem(item.Name, group); + listViewItem.SubItems.Add(item.schemaItemType.ToString()); + listViewItem.Tag = item; // Store the SchemaItem in the Tag property for later use + return listViewItem; + } + + private void moveLeftSchema_btn_Click(object sender, EventArgs e) + { + var selectedItems = this.schemaitem_listview.CheckedItems.Cast().ToList(); + var items = selectedItems.Select(i => (i.Group.Header, i.Tag as SchemaItem)) + .GroupBy(i => i.Header) + .Select(i => new KeyValuePair(i.Key, i.Select(x => x.Item2).ToArray())) + .ToList(); + this.schemaTabViewModel.MoveLeft(items); + foreach (var item in selectedItems) + { + this.schemaitem_listview.Items.Remove(item); + } + foreach (var group in this.schemaitem_listview.Groups.Cast().Where(i => i.Items.Count == 0).ToList()) + { + + this.schemaitem_listview.Groups.Remove(group); + + } + + } + + + private void GenerateSchemaFileBtn_Click(object sender, EventArgs e) + { + GenerateAndExportSchemaFile(); + } + + public void ShowAboutDialog() + { + MessageBox.Show($@"Configuration Migration Tool For XrmToolBox is bascially the out of the box Configuration Migration Tool provided by Microsoft.{Environment.NewLine}Currently, only the schema creation functionality is implemented in this tool.{Environment.NewLine}You can use the custom cli tool to import/export data with the schema file generated by this tool located in the github repo of this tool.{Environment.NewLine}{Environment.NewLine}Version: {ToolVersion}" + , $"ConfigurationMigrationTool.XrmToolBox v{ToolVersion}", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.designer.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.designer.cs new file mode 100644 index 0000000..6ce1ff6 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.designer.cs @@ -0,0 +1,485 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox +{ + partial class ConfigurationMigrationControl + { + /// + /// Variable nécessaire au concepteur. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Nettoyage des ressources utilisées. + /// + /// true si les ressources managées doivent être supprimées ; sinon, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Code généré par le Concepteur de composants + + /// + /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas + /// le contenu de cette méthode avec l'éditeur de code. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigurationMigrationControl)); + this.toolStripMenu = new System.Windows.Forms.ToolStrip(); + this.tsbClose = new System.Windows.Forms.ToolStripButton(); + this.tssSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.SolutionLoadBtn = new System.Windows.Forms.ToolStripButton(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.Schema = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.solutionsCombbox = new System.Windows.Forms.ComboBox(); + this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); + this.label2 = new System.Windows.Forms.Label(); + this.entitylist_cbox = new System.Windows.Forms.ComboBox(); + this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); + this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel(); + this.fromselection_lbl = new System.Windows.Forms.Label(); + this.fromentity_select_all_cbox = new System.Windows.Forms.CheckBox(); + this.entityMetadataGrid = new System.Windows.Forms.DataGridView(); + this.relationshipm2m_grid = new System.Windows.Forms.DataGridView(); + this.moveRightSchema_btn = new System.Windows.Forms.Button(); + this.moveLeftSchema_btn = new System.Windows.Forms.Button(); + this.schemaitem_listview = new System.Windows.Forms.ListView(); + this.ColumnDisplayName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ColumnDisplayType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.schemaDefinition_lbl = new System.Windows.Forms.Label(); + this.GenerateSchemaFileBtn = new System.Windows.Forms.Button(); + this.informativeRTC = new System.Windows.Forms.RichTextBox(); + this.toolStripMenu.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.Schema.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.flowLayoutPanel3.SuspendLayout(); + this.flowLayoutPanel4.SuspendLayout(); + this.flowLayoutPanel5.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.entityMetadataGrid)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.relationshipm2m_grid)).BeginInit(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // toolStripMenu + // + this.toolStripMenu.ImageScalingSize = new System.Drawing.Size(24, 24); + this.toolStripMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsbClose, + this.tssSeparator1, + this.SolutionLoadBtn}); + this.toolStripMenu.Location = new System.Drawing.Point(0, 0); + this.toolStripMenu.Name = "toolStripMenu"; + this.toolStripMenu.Size = new System.Drawing.Size(990, 25); + this.toolStripMenu.TabIndex = 4; + this.toolStripMenu.Text = "toolStrip1"; + // + // tsbClose + // + this.tsbClose.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsbClose.Name = "tsbClose"; + this.tsbClose.Size = new System.Drawing.Size(86, 22); + this.tsbClose.Text = "Close this tool"; + this.tsbClose.Click += new System.EventHandler(this.tsbClose_Click); + // + // tssSeparator1 + // + this.tssSeparator1.Name = "tssSeparator1"; + this.tssSeparator1.Size = new System.Drawing.Size(6, 25); + // + // SolutionLoadBtn + // + this.SolutionLoadBtn.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.SolutionLoadBtn.Image = ((System.Drawing.Image)(resources.GetObject("SolutionLoadBtn.Image"))); + this.SolutionLoadBtn.ImageTransparentColor = System.Drawing.Color.Magenta; + this.SolutionLoadBtn.Name = "SolutionLoadBtn"; + this.SolutionLoadBtn.Size = new System.Drawing.Size(89, 22); + this.SolutionLoadBtn.Text = "Load Solutions"; + this.SolutionLoadBtn.Click += new System.EventHandler(this.SolutionLoadBtn_Click); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.Schema); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 25); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(990, 810); + this.tabControl1.TabIndex = 5; + // + // Schema + // + this.Schema.Controls.Add(this.tableLayoutPanel1); + this.Schema.Location = new System.Drawing.Point(4, 22); + this.Schema.Name = "Schema"; + this.Schema.Padding = new System.Windows.Forms.Padding(3); + this.Schema.Size = new System.Drawing.Size(982, 784); + this.Schema.TabIndex = 0; + this.Schema.Text = "Schema"; + this.Schema.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel4, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.entityMetadataGrid, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.relationshipm2m_grid, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.moveRightSchema_btn, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.moveLeftSchema_btn, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.schemaitem_listview, 2, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 2, 1); + this.tableLayoutPanel1.Controls.Add(this.informativeRTC, 2, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.ForeColor = System.Drawing.SystemColors.ControlText; + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 130F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 59F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(976, 778); + this.tableLayoutPanel1.TabIndex = 0; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.flowLayoutPanel2); + this.flowLayoutPanel1.Controls.Add(this.flowLayoutPanel3); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel1.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(432, 124); + this.flowLayoutPanel1.TabIndex = 0; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Controls.Add(this.label1); + this.flowLayoutPanel2.Controls.Add(this.solutionsCombbox); + this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Left; + this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel2.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(270, 53); + this.flowLayoutPanel2.TabIndex = 2; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Dock = System.Windows.Forms.DockStyle.Fill; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(267, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Select a solution"; + // + // solutionsCombbox + // + this.solutionsCombbox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.solutionsCombbox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.solutionsCombbox.FormattingEnabled = true; + this.solutionsCombbox.Location = new System.Drawing.Point(3, 16); + this.solutionsCombbox.MinimumSize = new System.Drawing.Size(200, 0); + this.solutionsCombbox.Name = "solutionsCombbox"; + this.solutionsCombbox.Size = new System.Drawing.Size(267, 21); + this.solutionsCombbox.TabIndex = 1; + this.solutionsCombbox.Text = "Please load solutions first"; + this.solutionsCombbox.SelectedIndexChanged += new System.EventHandler(this.solutioncbox_SelectedIndexChanged); + // + // flowLayoutPanel3 + // + this.flowLayoutPanel3.Controls.Add(this.label2); + this.flowLayoutPanel3.Controls.Add(this.entitylist_cbox); + this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Left; + this.flowLayoutPanel3.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel3.Location = new System.Drawing.Point(3, 62); + this.flowLayoutPanel3.Name = "flowLayoutPanel3"; + this.flowLayoutPanel3.Size = new System.Drawing.Size(270, 53); + this.flowLayoutPanel3.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Fill; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(3, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(267, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Choose entity"; + // + // entitylist_cbox + // + this.entitylist_cbox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.entitylist_cbox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.entitylist_cbox.Enabled = false; + this.entitylist_cbox.FormattingEnabled = true; + this.entitylist_cbox.Location = new System.Drawing.Point(3, 16); + this.entitylist_cbox.MinimumSize = new System.Drawing.Size(200, 0); + this.entitylist_cbox.Name = "entitylist_cbox"; + this.entitylist_cbox.Size = new System.Drawing.Size(267, 21); + this.entitylist_cbox.TabIndex = 1; + this.entitylist_cbox.SelectedIndexChanged += new System.EventHandler(this.entitylist_SelectedIndexChanged); + // + // flowLayoutPanel4 + // + this.flowLayoutPanel4.Controls.Add(this.flowLayoutPanel5); + this.flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel4.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel4.Location = new System.Drawing.Point(3, 133); + this.flowLayoutPanel4.Name = "flowLayoutPanel4"; + this.flowLayoutPanel4.Size = new System.Drawing.Size(432, 53); + this.flowLayoutPanel4.TabIndex = 2; + // + // flowLayoutPanel5 + // + this.flowLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.flowLayoutPanel5.Controls.Add(this.fromselection_lbl); + this.flowLayoutPanel5.Controls.Add(this.fromentity_select_all_cbox); + this.flowLayoutPanel5.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel5.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel5.Name = "flowLayoutPanel5"; + this.flowLayoutPanel5.Size = new System.Drawing.Size(488, 48); + this.flowLayoutPanel5.TabIndex = 0; + // + // fromselection_lbl + // + this.fromselection_lbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.fromselection_lbl.AutoSize = true; + this.fromselection_lbl.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.fromselection_lbl.Location = new System.Drawing.Point(3, 0); + this.fromselection_lbl.Name = "fromselection_lbl"; + this.fromselection_lbl.Size = new System.Drawing.Size(274, 13); + this.fromselection_lbl.TabIndex = 0; + this.fromselection_lbl.Text = "Select Fields and relationships for your schema"; + this.fromselection_lbl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // fromentity_select_all_cbox + // + this.fromentity_select_all_cbox.AutoSize = true; + this.fromentity_select_all_cbox.Location = new System.Drawing.Point(3, 23); + this.fromentity_select_all_cbox.Margin = new System.Windows.Forms.Padding(3, 10, 3, 3); + this.fromentity_select_all_cbox.Name = "fromentity_select_all_cbox"; + this.fromentity_select_all_cbox.Size = new System.Drawing.Size(70, 17); + this.fromentity_select_all_cbox.TabIndex = 1; + this.fromentity_select_all_cbox.Text = "Select All"; + this.fromentity_select_all_cbox.UseVisualStyleBackColor = true; + this.fromentity_select_all_cbox.Click += new System.EventHandler(this.fromentity_select_all_cbox_Click); + // + // entityMetadataGrid + // + this.entityMetadataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.entityMetadataGrid.Dock = System.Windows.Forms.DockStyle.Fill; + this.entityMetadataGrid.Location = new System.Drawing.Point(3, 192); + this.entityMetadataGrid.Name = "entityMetadataGrid"; + this.entityMetadataGrid.Size = new System.Drawing.Size(432, 288); + this.entityMetadataGrid.TabIndex = 1; + this.entityMetadataGrid.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.entityMetadataGrid_CellClick); + this.entityMetadataGrid.SelectionChanged += new System.EventHandler(this.entityMetadataGrid_SelectionChanged); + // + // relationshipm2m_grid + // + this.relationshipm2m_grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.relationshipm2m_grid.Dock = System.Windows.Forms.DockStyle.Fill; + this.relationshipm2m_grid.Location = new System.Drawing.Point(3, 486); + this.relationshipm2m_grid.Name = "relationshipm2m_grid"; + this.relationshipm2m_grid.Size = new System.Drawing.Size(432, 289); + this.relationshipm2m_grid.TabIndex = 3; + this.relationshipm2m_grid.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.relationshipm2m_grid_CellClick); + this.relationshipm2m_grid.SelectionChanged += new System.EventHandler(this.relationshipm2m_grid_SelectionChanged); + // + // moveRightSchema_btn + // + this.moveRightSchema_btn.BackgroundImage = global::Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties.Resources.right_arrow; + this.moveRightSchema_btn.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.moveRightSchema_btn.Dock = System.Windows.Forms.DockStyle.Bottom; + this.moveRightSchema_btn.Location = new System.Drawing.Point(441, 403); + this.moveRightSchema_btn.Margin = new System.Windows.Forms.Padding(3, 3, 3, 30); + this.moveRightSchema_btn.Name = "moveRightSchema_btn"; + this.moveRightSchema_btn.Size = new System.Drawing.Size(94, 50); + this.moveRightSchema_btn.TabIndex = 4; + this.moveRightSchema_btn.UseVisualStyleBackColor = true; + this.moveRightSchema_btn.Click += new System.EventHandler(this.moveRightSchema_btn_Click); + // + // moveLeftSchema_btn + // + this.moveLeftSchema_btn.BackgroundImage = global::Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties.Resources.left_arrow; + this.moveLeftSchema_btn.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.moveLeftSchema_btn.Dock = System.Windows.Forms.DockStyle.Top; + this.moveLeftSchema_btn.Location = new System.Drawing.Point(441, 513); + this.moveLeftSchema_btn.Margin = new System.Windows.Forms.Padding(3, 30, 3, 3); + this.moveLeftSchema_btn.Name = "moveLeftSchema_btn"; + this.moveLeftSchema_btn.Size = new System.Drawing.Size(94, 50); + this.moveLeftSchema_btn.TabIndex = 5; + this.moveLeftSchema_btn.UseVisualStyleBackColor = true; + this.moveLeftSchema_btn.Click += new System.EventHandler(this.moveLeftSchema_btn_Click); + // + // schemaitem_listview + // + this.schemaitem_listview.Alignment = System.Windows.Forms.ListViewAlignment.Default; + this.schemaitem_listview.CheckBoxes = true; + this.schemaitem_listview.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ColumnDisplayName, + this.ColumnDisplayType}); + this.schemaitem_listview.Cursor = System.Windows.Forms.Cursors.Arrow; + this.schemaitem_listview.Dock = System.Windows.Forms.DockStyle.Fill; + this.schemaitem_listview.FullRowSelect = true; + this.schemaitem_listview.GridLines = true; + this.schemaitem_listview.HideSelection = false; + this.schemaitem_listview.Location = new System.Drawing.Point(541, 192); + this.schemaitem_listview.Name = "schemaitem_listview"; + this.tableLayoutPanel1.SetRowSpan(this.schemaitem_listview, 2); + this.schemaitem_listview.Size = new System.Drawing.Size(432, 583); + this.schemaitem_listview.TabIndex = 6; + this.schemaitem_listview.UseCompatibleStateImageBehavior = false; + this.schemaitem_listview.View = System.Windows.Forms.View.Details; + // + // ColumnDisplayName + // + this.ColumnDisplayName.Text = "Name"; + this.ColumnDisplayName.Width = 300; + // + // ColumnDisplayType + // + this.ColumnDisplayType.Text = "Type"; + this.ColumnDisplayType.Width = 150; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.Controls.Add(this.schemaDefinition_lbl, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.GenerateSchemaFileBtn, 1, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(541, 133); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 53F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(432, 53); + this.tableLayoutPanel2.TabIndex = 7; + // + // schemaDefinition_lbl + // + this.schemaDefinition_lbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.schemaDefinition_lbl.AutoSize = true; + this.schemaDefinition_lbl.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.schemaDefinition_lbl.Location = new System.Drawing.Point(3, 40); + this.schemaDefinition_lbl.Name = "schemaDefinition_lbl"; + this.schemaDefinition_lbl.Size = new System.Drawing.Size(110, 13); + this.schemaDefinition_lbl.TabIndex = 0; + this.schemaDefinition_lbl.Text = "Schema Definition"; + this.schemaDefinition_lbl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // GenerateSchemaFileBtn + // + this.GenerateSchemaFileBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.GenerateSchemaFileBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.GenerateSchemaFileBtn.Image = global::Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties.Resources.schema; + this.GenerateSchemaFileBtn.Location = new System.Drawing.Point(223, 14); + this.GenerateSchemaFileBtn.Name = "GenerateSchemaFileBtn"; + this.GenerateSchemaFileBtn.Size = new System.Drawing.Size(206, 36); + this.GenerateSchemaFileBtn.TabIndex = 1; + this.GenerateSchemaFileBtn.Text = "Generate Schema File"; + this.GenerateSchemaFileBtn.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.GenerateSchemaFileBtn.UseVisualStyleBackColor = true; + this.GenerateSchemaFileBtn.Click += new System.EventHandler(this.GenerateSchemaFileBtn_Click); + // + // informativeRTC + // + this.informativeRTC.BackColor = System.Drawing.SystemColors.Info; + this.informativeRTC.Dock = System.Windows.Forms.DockStyle.Fill; + this.informativeRTC.Location = new System.Drawing.Point(541, 3); + this.informativeRTC.Name = "informativeRTC"; + this.informativeRTC.ReadOnly = true; + this.informativeRTC.ShowSelectionMargin = true; + this.informativeRTC.Size = new System.Drawing.Size(432, 124); + this.informativeRTC.TabIndex = 8; + this.informativeRTC.Text = resources.GetString("informativeRTC.Text"); + // + // ConfigurationMigrationControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.toolStripMenu); + this.MinimumSize = new System.Drawing.Size(990, 0); + this.Name = "ConfigurationMigrationControl"; + this.PluginIcon = ((System.Drawing.Icon)(resources.GetObject("$this.PluginIcon"))); + this.Size = new System.Drawing.Size(990, 835); + this.TabIcon = global::Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties.Resources.ConfigurationMigrationTool_32x32; + this.Load += new System.EventHandler(this.MyPluginControl_Load); + this.toolStripMenu.ResumeLayout(false); + this.toolStripMenu.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.Schema.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + this.flowLayoutPanel3.ResumeLayout(false); + this.flowLayoutPanel3.PerformLayout(); + this.flowLayoutPanel4.ResumeLayout(false); + this.flowLayoutPanel5.ResumeLayout(false); + this.flowLayoutPanel5.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.entityMetadataGrid)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.relationshipm2m_grid)).EndInit(); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.ToolStrip toolStripMenu; + private System.Windows.Forms.ToolStripButton tsbClose; + private System.Windows.Forms.ToolStripSeparator tssSeparator1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage Schema; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox solutionsCombbox; + private System.Windows.Forms.ToolStripButton SolutionLoadBtn; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox entitylist_cbox; + private System.Windows.Forms.DataGridView entityMetadataGrid; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5; + private System.Windows.Forms.Label fromselection_lbl; + private System.Windows.Forms.CheckBox fromentity_select_all_cbox; + private System.Windows.Forms.DataGridView relationshipm2m_grid; + private System.Windows.Forms.Button moveRightSchema_btn; + private System.Windows.Forms.Button moveLeftSchema_btn; + public System.Windows.Forms.ColumnHeader ColumnDisplayName; + private System.Windows.Forms.ListView schemaitem_listview; + private System.Windows.Forms.ColumnHeader ColumnDisplayType; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label schemaDefinition_lbl; + private System.Windows.Forms.Button GenerateSchemaFileBtn; + private System.Windows.Forms.RichTextBox informativeRTC; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.resx b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.resx new file mode 100644 index 0000000..5c2c74f --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.resx @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG + YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9 + 0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw + bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc + VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9 + c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32 + Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo + mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+ + kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D + TgDQASA1MVpwzwAAAABJRU5ErkJggg== + + + + **Information** +Currently , only schema file generation is available with this tool. + +Data Export and Data Import are planned to be available soon. +In the mean time, the generated schema file can be used to import and export data using the cli tool in the project repo. +A service principal that have permissions to your dataverse environment will be needed. + +Project repo: +https://github.com/dotnetprog/dataverse-configuration-migration-tool + + + + AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAOwAAADsAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA/3k2EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD/eTrK/306Nf99N3D/fzSd/4Iylf+GL2f/jzAQAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA/3I+Hf95Ov//ezj//302//9/NP//gjL//4Uw//+ILez/jCxSAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/djxh/3k6//97OP//fTb//4A04v+CMpX/hjB6/4gtkv+L + K9X/jyZJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP92PHf/eTr4/3s4//99Nt3/gEAEAAAAAAAA + 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 + LB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/eTYT/301av+ANaD/gjKj/4Qvcv+H + KzX/iyvKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP+GKBMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////////9/////Af///gD///4Af//+Dn///w////////AB/4AAAf + +AAAH/gAAB/4AAAf+AAAH/gAER/4iAAf+AAAH/gAAB/4AAAf+AAAH/gAAB/4AAAf+AD///////8P///n + B///4Af///AH///4D////+////////////8= + + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationPlugin.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationPlugin.cs new file mode 100644 index 0000000..5eb5faf --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationPlugin.cs @@ -0,0 +1,85 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Common; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Reflection; +using XrmToolBox.Extensibility; +using XrmToolBox.Extensibility.Interfaces; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox +{ + // Do not forget to update version number and author (company attribute) in AssemblyInfo.cs class + // To generate Base64 string for Images below, you can use https://www.base64-image.de/ + [Export(typeof(IXrmToolBoxPlugin)), + ExportMetadata("Name", ToolConstants.Name), + ExportMetadata("Description", ToolConstants.Description), + // Please specify the base64 content of a 32x32 pixels image + ExportMetadata("SmallImageBase64", ToolConstants.SmallImage), + // Please specify the base64 content of a 80x80 pixels image + ExportMetadata("BigImageBase64", ToolConstants.BigImage), + ExportMetadata("BackgroundColor", "Lavender"), + ExportMetadata("PrimaryFontColor", "Black"), + ExportMetadata("SecondaryFontColor", "Gray")] + public class ConfigurationMigrationPlugin : PluginBase + { + public override IXrmToolBoxPluginControl GetControl() + { + return new ConfigurationMigrationControl(); + } + + /// + /// Constructor + /// + public ConfigurationMigrationPlugin() + { + // If you have external assemblies that you need to load, uncomment the following to + // hook into the event that will fire when an Assembly fails to resolve + // AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolveEventHandler); + } + + /// + /// Event fired by CLR when an assembly reference fails to load + /// Assumes that related assemblies will be loaded from a subfolder named the same as the Plugin + /// For example, a folder named Sample.XrmToolBox.MyPlugin + /// + /// + /// + /// + private Assembly AssemblyResolveEventHandler(object sender, ResolveEventArgs args) + { + Assembly loadAssembly = null; + Assembly currAssembly = Assembly.GetExecutingAssembly(); + + // base name of the assembly that failed to resolve + var argName = args.Name.Substring(0, args.Name.IndexOf(",")); + + // check to see if the failing assembly is one that we reference. + List refAssemblies = currAssembly.GetReferencedAssemblies().ToList(); + var refAssembly = refAssemblies.Where(a => a.Name == argName).FirstOrDefault(); + + // if the current unresolved assembly is referenced by our plugin, attempt to load + if (refAssembly != null) + { + // load from the path to this plugin assembly, not host executable + string dir = Path.GetDirectoryName(currAssembly.Location).ToLower(); + string folder = Path.GetFileNameWithoutExtension(currAssembly.Location); + dir = Path.Combine(dir, folder); + + var assmbPath = Path.Combine(dir, $"{argName}.dll"); + + if (File.Exists(assmbPath)) + { + loadAssembly = Assembly.LoadFrom(assmbPath); + } + else + { + throw new FileNotFoundException($"Unable to locate dependency: {assmbPath}"); + } + } + + return loadAssembly; + } + } +} \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj new file mode 100644 index 0000000..b8457cb --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj @@ -0,0 +1,376 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46} + Library + Properties + Dataverse.ConfigurationMigrationTool.XrmToolBox + Dataverse.ConfigurationMigrationTool.XrmToolBox + v4.8 + 512 + + + + + Dataverse.ConfigurationMigrationTool.XrmToolBox + 1.0.0 + Francois Desjardins-Nadeau + FDN Tech Solutions + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MscrmTools.Xrm.Connection.1.2025.7.63\lib\net48\McTools.Xrm.Connection.dll + + + ..\packages\MscrmTools.Xrm.Connection.1.2025.7.63\lib\net48\McTools.Xrm.Connection.WinForms.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.7\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Microsoft.CrmSdk.CoreAssemblies.9.0.2.59\lib\net462\Microsoft.Crm.Sdk.Proxy.dll + + + ..\packages\Microsoft.Extensions.AI.9.7.1\lib\net462\Microsoft.Extensions.AI.dll + + + ..\packages\Microsoft.Extensions.AI.Abstractions.9.7.1\lib\net462\Microsoft.Extensions.AI.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Caching.Abstractions.9.0.7\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.9.0.7\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.9.0.7\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Primitives.9.0.7\lib\net462\Microsoft.Extensions.Primitives.dll + + + ..\packages\Microsoft.IdentityModel.7.0.0\lib\net35\microsoft.identitymodel.dll + + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.5.3.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.2.3.24\lib\net461\Microsoft.Rest.ClientRuntime.dll + + + ..\packages\Microsoft.VisualStudio.CoreUtility.17.14.249\lib\net472\Microsoft.VisualStudio.CoreUtility.dll + + + ..\packages\Microsoft.VisualStudio.Threading.17.13.2\lib\net472\Microsoft.VisualStudio.Threading.dll + + + ..\packages\Microsoft.VisualStudio.Validation.17.8.8\lib\netstandard2.0\Microsoft.VisualStudio.Validation.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.3351.48\lib\net462\Microsoft.Web.WebView2.Core.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.3351.48\lib\net462\Microsoft.Web.WebView2.WinForms.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.3351.48\lib\net462\Microsoft.Web.WebView2.Wpf.dll + + + ..\packages\Microsoft.Web.Xdt.3.2.0\lib\netstandard2.0\Microsoft.Web.XmlTransform.dll + + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\Microsoft.CrmSdk.CoreAssemblies.9.0.2.59\lib\net462\Microsoft.Xrm.Sdk.dll + + + ..\packages\Microsoft.CrmSdk.Deployment.9.0.2.34\lib\net462\Microsoft.Xrm.Sdk.Deployment.dll + + + ..\packages\Microsoft.CrmSdk.Workflow.9.0.2.59\lib\net462\Microsoft.Xrm.Sdk.Workflow.dll + + + ..\packages\Microsoft.CrmSdk.XrmTooling.CoreAssembly.9.1.1.65\lib\net462\Microsoft.Xrm.Tooling.Connector.dll + + + ..\packages\Microsoft.CrmSdk.XrmTooling.WpfControls.9.1.1.65\lib\net462\Microsoft.Xrm.Tooling.CrmConnectControl.dll + + + ..\packages\Microsoft.CrmSdk.XrmTooling.WpfControls.9.1.1.65\lib\net462\Microsoft.Xrm.Tooling.Ui.Styles.dll + + + ..\packages\Microsoft.CrmSdk.XrmTooling.WpfControls.9.1.1.65\lib\net462\Microsoft.Xrm.Tooling.WebResourceUtility.dll + + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\jacobslusser.ScintillaNET.3.6.3\lib\net40\ScintillaNET.dll + + + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.8.0.0\lib\net462\System.Collections.Immutable.dll + + + + + ..\packages\System.Diagnostics.DiagnosticSource.9.0.7\lib\net462\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll + True + True + + + ..\packages\System.IO.Pipelines.9.0.7\lib\net462\System.IO.Pipelines.dll + + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll + + + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + True + True + + + + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll + True + True + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll + True + True + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + True + True + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + True + True + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + True + True + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\packages\System.ServiceModel.Primitives.8.1.2\lib\net462\System.ServiceModel.Duplex.dll + + + ..\packages\System.ServiceModel.Http.8.1.2\lib\net462\System.ServiceModel.Http.dll + + + ..\packages\System.ServiceModel.Primitives.8.1.2\lib\net462\System.ServiceModel.Primitives.dll + + + ..\packages\System.ServiceModel.Primitives.8.1.2\lib\net462\System.ServiceModel.Security.dll + + + ..\packages\System.Text.Encodings.Web.9.0.7\lib\net462\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.9.0.7\lib\net462\System.Text.Json.dll + + + ..\packages\System.Threading.Channels.9.0.7\lib\net462\System.Threading.Channels.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\packages\DockPanelSuite.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.dll + + + ..\packages\DockPanelSuite.ThemeVS2015.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll + + + + ..\packages\XrmToolBoxPackage.1.2025.7.71\lib\net48\XrmToolBox.exe + + + ..\packages\XrmToolBoxPackage.1.2025.7.71\lib\net48\XrmToolBox.Extensibility.dll + + + ..\packages\XrmToolBoxPackage.1.2025.7.71\lib\net48\XrmToolBox.ToolLibrary.dll + + + + + Domain\DataSchema.cs + + + + + + + UserControl + + + ConfigurationMigrationControl.cs + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + ConfigurationMigrationControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IF $(ConfigurationName) == Debug ( + IF NOT EXIST Plugins mkdir Plugins + DEL $(TargetDir)Plugins\manifest.json + xcopy "$(TargetDir)$(TargetFileName)" "$(TargetDir)Plugins\" /Y + xcopy "$(TargetDir)$(TargetName).pdb" "$(TargetDir)Plugins\" /Y + ) + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IFileService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IFileService.cs new file mode 100644 index 0000000..f144a49 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IFileService.cs @@ -0,0 +1,7 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain.Abstraction +{ + public interface IFileService + { + void WriteToFile(string filePath, T data); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IMetadataService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IMetadataService.cs new file mode 100644 index 0000000..c19dab7 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IMetadataService.cs @@ -0,0 +1,12 @@ +using Microsoft.Xrm.Sdk.Metadata; +using System.Collections.Generic; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain.Abstraction +{ + public interface IMetadataService + { + EntityMetadata GetEntityMetadata(string entityLogicalName); + + IEnumerable GetAllEntityMetadata(); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/ISolutionService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/ISolutionService.cs new file mode 100644 index 0000000..fa5b70d --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/ISolutionService.cs @@ -0,0 +1,12 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain; +using System; +using System.Collections.Generic; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Services +{ + public interface ISolutionService + { + IReadOnlyCollection GetSolutions(); + IReadOnlyCollection GetEntitiesBySolutionId(Guid solutionId); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Solution.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Solution.cs new file mode 100644 index 0000000..dca5fc0 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Solution.cs @@ -0,0 +1,18 @@ +using System; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain +{ + public class Solution + { + public Guid Id { get; set; } + public string UniqueName { get; set; } + public string FriendlyName { get; set; } + public string Version { get; set; } + public bool IsManaged { get; set; } + + public override string ToString() + { + return $"{FriendlyName} ({Version}) {(IsManaged ? "[Managed]" : "[Unmanaged]")}"; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/SolutionEntityComponent.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/SolutionEntityComponent.cs new file mode 100644 index 0000000..69abd02 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/SolutionEntityComponent.cs @@ -0,0 +1,12 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain +{ + public class SolutionEntityComponent + { + public string EntityName { get; set; } + public string DisplayEntityName { get; set; } + public override string ToString() + { + return $"{DisplayEntityName} ({EntityName})"; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/AttributeMetadataToFIeldSchemaMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/AttributeMetadataToFIeldSchemaMapper.cs new file mode 100644 index 0000000..1cf9159 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/AttributeMetadataToFIeldSchemaMapper.cs @@ -0,0 +1,67 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; +using System; +using System.Linq; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public class AttributeMetadataToFIeldSchemaMapper : IMapper + { + public FieldSchema Map(AttributeMetadata source) + { + var fieldSchema = new FieldSchema() + { + Name = source.LogicalName, + Displayname = source.DisplayName?.UserLocalizedLabel?.Label ?? source.DisplayName.LocalizedLabels.FirstOrDefault()?.Label, + Customfield = source.IsCustomAttribute ?? false, + PrimaryKey = source.IsPrimaryId ?? false, + Type = GetFieldType(source.AttributeType), + }; + if (source is LookupAttributeMetadata lookupAttribute) + { + fieldSchema.LookupType = string.Join("|", lookupAttribute.Targets); + } + return fieldSchema; + + } + private static string GetFieldType(AttributeTypeCode? typecode) + { + if (typecode == null) + return "Unknown"; + switch (typecode.Value) + { + case AttributeTypeCode.BigInt: + return "bigint"; + case AttributeTypeCode.Boolean: + return "bool"; + case AttributeTypeCode.Lookup: + case AttributeTypeCode.Customer: + return "entityreference"; + case AttributeTypeCode.DateTime: + return "datetime"; + case AttributeTypeCode.Decimal: + return "decimal"; + case AttributeTypeCode.Double: + return "float"; + case AttributeTypeCode.Integer: + return "number"; + case AttributeTypeCode.Money: + return "money"; + case AttributeTypeCode.Picklist: + return "optionsetvalue"; + case AttributeTypeCode.State: + return "state"; + case AttributeTypeCode.Status: + return "status"; + case AttributeTypeCode.String: + return "string"; + case AttributeTypeCode.Uniqueidentifier: + return "guid"; + case AttributeTypeCode.Owner: + return "owner"; + } + throw new NotImplementedException($"{typecode} attribute type is not implemented in {nameof(AttributeMetadataToFIeldSchemaMapper)}"); + } + + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityMetadataToEntitySchemaMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityMetadataToEntitySchemaMapper.cs new file mode 100644 index 0000000..0700ea1 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityMetadataToEntitySchemaMapper.cs @@ -0,0 +1,23 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; +using System.Linq; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public class EntityMetadataToEntitySchemaMapper : IMapper + { + public EntitySchema Map(EntityMetadata source) + { + var entitySchema = new EntitySchema() + { + Disableplugins = false, + Displayname = source.DisplayName?.UserLocalizedLabel?.Label ?? source.DisplayName?.LocalizedLabels.FirstOrDefault()?.Label, + Etc = source.ObjectTypeCode ?? -1, + Name = source.LogicalName, + Primaryidfield = source.PrimaryIdAttribute, + Primarynamefield = source.PrimaryNameAttribute, + }; + return entitySchema; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionComponentMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionComponentMapper.cs new file mode 100644 index 0000000..e7a93c9 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionComponentMapper.cs @@ -0,0 +1,18 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain; +using Microsoft.Xrm.Sdk; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public class EntityToSolutionComponentMapper : IMapper + { + public SolutionEntityComponent Map(Entity source) + { + return new SolutionEntityComponent + { + EntityName = source.GetAttributeValue("logicalname"), + DisplayEntityName = source.GetAttributeValue("originallocalizedname") ?? source.GetAttributeValue("name") // Fallback to logical name if localized name is not available + }; + } + } + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionMapper.cs new file mode 100644 index 0000000..d168e0a --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionMapper.cs @@ -0,0 +1,20 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain; +using Microsoft.Xrm.Sdk; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public class EntityToSolutionMapper : IMapper + { + public Solution Map(Entity source) + { + return new Solution + { + Id = source.Id, + UniqueName = source.GetAttributeValue("uniquename"), + FriendlyName = source.GetAttributeValue("friendlyname"), + Version = source.GetAttributeValue("version"), + IsManaged = source.GetAttributeValue("ismanaged") ?? false + }; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/IMapper.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/IMapper.cs new file mode 100644 index 0000000..ca0983d --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/IMapper.cs @@ -0,0 +1,7 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public interface IMapper + { + TDestination Map(TSource source); + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/ManyToManyRelationshipMetadataToRelationshipSchema.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/ManyToManyRelationshipMetadataToRelationshipSchema.cs new file mode 100644 index 0000000..dc4781f --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/ManyToManyRelationshipMetadataToRelationshipSchema.cs @@ -0,0 +1,22 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Microsoft.Xrm.Sdk.Metadata; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers +{ + public class ManyToManyRelationshipMetadataToRelationshipSchema : IMapper + { + public RelationshipSchema Map(ManyToManyRelationshipMetadata source) + { + return new RelationshipSchema + { + Name = source.IntersectEntityName, + ManyToMany = true, + Isreflexive = false, + RelatedEntityName = source.IntersectEntityName, + M2mTargetEntity = source.Entity2LogicalName, + M2mTargetEntityPrimaryKey = source.Entity2IntersectAttribute + }; + } + } + +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2a8d4c6 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Dataverse.ConfigurationMigrationTool.XrmToolBox")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("François Desjardins-Nadeau")] +[assembly: AssemblyProduct("Dataverse.ConfigurationMigrationTool.XrmToolBox")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7dd84f5d-0f62-40be-99f6-47e49bbd1c46")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersion("1.1.2-AddXrmtoolBox.0+Branch.feature-AddXrmtoolBox.Sha.e4430db29230de99c44c859669fd6b06ef375737")] diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.Designer.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.Designer.cs new file mode 100644 index 0000000..a1aa083 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.Designer.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Dataverse.ConfigurationMigrationTool.XrmToolBox.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ConfigurationMigrationTool_32x32 { + get { + object obj = ResourceManager.GetObject("ConfigurationMigrationTool_32x32", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap left_arrow { + get { + object obj = ResourceManager.GetObject("left-arrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap right_arrow { + get { + object obj = ResourceManager.GetObject("right-arrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap schema { + get { + object obj = ResourceManager.GetObject("schema", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap xml { + get { + object obj = ResourceManager.GetObject("xml", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.resx b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.resx new file mode 100644 index 0000000..704fd70 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.resx @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\ConfigurationMigrationTool_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\right-arrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\left-arrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\xml.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\schema.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/ConfigurationMigrationTool_32x32.png b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/ConfigurationMigrationTool_32x32.png new file mode 100644 index 0000000..9652927 Binary files /dev/null and b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/ConfigurationMigrationTool_32x32.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/left-arrow.png b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/left-arrow.png new file mode 100644 index 0000000..7054524 Binary files /dev/null and b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/left-arrow.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/right-arrow.png b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/right-arrow.png new file mode 100644 index 0000000..a60209a Binary files /dev/null and b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/right-arrow.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/schema.png b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/schema.png new file mode 100644 index 0000000..ce140e8 Binary files /dev/null and b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/schema.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/xml.png b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/xml.png new file mode 100644 index 0000000..d02bd71 Binary files /dev/null and b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/xml.png differ diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseMetadataService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseMetadataService.cs new file mode 100644 index 0000000..e396275 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseMetadataService.cs @@ -0,0 +1,52 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain.Abstraction; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Messages; +using Microsoft.Xrm.Sdk.Metadata; +using System.Collections.Generic; +using System.Linq; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Services +{ + public class DataverseMetadataService : IMetadataService + { + private readonly IOrganizationService _orgService; + private List _entityMetadataCache = new List(); + public DataverseMetadataService(IOrganizationService orgService) + { + _orgService = orgService; + } + + public IEnumerable GetAllEntityMetadata() + { + if (_entityMetadataCache.Any()) + { + return _entityMetadataCache; + } + var request = new RetrieveAllEntitiesRequest + { + EntityFilters = EntityFilters.All, + RetrieveAsIfPublished = true + }; + var response = (RetrieveAllEntitiesResponse)_orgService.Execute(request); + _entityMetadataCache = response.EntityMetadata.ToList(); + return _entityMetadataCache; + } + + public EntityMetadata GetEntityMetadata(string entityLogicalName) + { + var cachedValue = _entityMetadataCache.FirstOrDefault(e => e.LogicalName == entityLogicalName); + if (cachedValue != null) + { + return cachedValue; + } + var request = new RetrieveEntityRequest + { + EntityFilters = EntityFilters.All, + LogicalName = entityLogicalName, + RetrieveAsIfPublished = true + }; + var response = (RetrieveEntityResponse)_orgService.Execute(request); + return response.EntityMetadata; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseSolutionService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseSolutionService.cs new file mode 100644 index 0000000..a24c0bd --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseSolutionService.cs @@ -0,0 +1,66 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Common; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Query; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Services +{ + public class DataverseSolutionService : ISolutionService + { + private readonly IOrganizationService _organizationService; + private static readonly IMapper _mapper = new EntityToSolutionMapper(); + private static readonly IMapper _entityComponentMapper = new EntityToSolutionComponentMapper(); + + public DataverseSolutionService(IOrganizationService organizationService) + { + _organizationService = organizationService; + } + + public IReadOnlyCollection GetSolutions() + { + var query = new QueryExpression("solution") + { + ColumnSet = new ColumnSet("friendlyname", "uniquename", "version", "ismanaged") + }; + query.AddOrder("friendlyname", OrderType.Ascending); + var result = _organizationService.RetrieveMultiple(query); + return result.Entities.Select(_mapper.Map).ToArray(); + } + + public IReadOnlyCollection GetEntitiesBySolutionId(Guid solutionId) + { + var query = new QueryExpression("solutioncomponent") + { + ColumnSet = new ColumnSet("solutionid", "componenttype", "objectid"), + Criteria = new FilterExpression + { + Conditions = + { + new ConditionExpression("solutionid", ConditionOperator.Equal, solutionId), + new ConditionExpression("componenttype", ConditionOperator.Equal, 1) // 1 is for Entity + } + }, + LinkEntities = + { + new LinkEntity + { + LinkFromEntityName = "solutioncomponent", + LinkFromAttributeName = "objectid", + LinkToEntityName = "entity", + LinkToAttributeName = "entityid", + Columns = new ColumnSet("logicalname","originallocalizedname","name"), + EntityAlias = "entity", + JoinOperator = JoinOperator.Inner + } + } + }; + + var result = _organizationService.RetrieveMultiple(query); + return result.Entities.Select(e => _entityComponentMapper.Map(e.GetAliasedEntity("entity"))).ToArray(); + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/XmlFileService.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/XmlFileService.cs new file mode 100644 index 0000000..2046359 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/XmlFileService.cs @@ -0,0 +1,18 @@ +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Domain.Abstraction; +using System.IO; +using System.Xml.Serialization; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.Services +{ + public class XmlFileService : IFileService + { + public void WriteToFile(string filePath, T data) + { + var x = new XmlSerializer(typeof(T)); + using (var writer = new StreamWriter(filePath)) + { + x.Serialize(writer, data); + } + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Settings.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Settings.cs new file mode 100644 index 0000000..1962c2f --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Settings.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox +{ + /// + /// This class can help you to store settings for your plugin + /// + /// + /// This class must be XML serializable + /// + public class Settings + { + public string LastUsedOrganizationWebappUrl { get; set; } + } +} \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/FieldComponentMetadataViewModel.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/FieldComponentMetadataViewModel.cs new file mode 100644 index 0000000..2e0947b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/FieldComponentMetadataViewModel.cs @@ -0,0 +1,11 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.VIewModel +{ + + public class FieldComponentMetadataViewModel + { + public bool IsSelected { get; set; } + public string LogicalName { get; set; } + public string DisplayName { get; set; } + public string DataType { get; set; } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/RelationshipComponentMetadataViewModel.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/RelationshipComponentMetadataViewModel.cs new file mode 100644 index 0000000..52e5c7d --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/RelationshipComponentMetadataViewModel.cs @@ -0,0 +1,10 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.VIewModel +{ + public class RelationshipComponentMetadataViewModel + { + public bool IsSelected { get; set; } + public string SchemaName { get; set; } + public string TableName { get; set; } + public string TargetEntity { get; set; } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaItem.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaItem.cs new file mode 100644 index 0000000..7ebee5b --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaItem.cs @@ -0,0 +1,28 @@ +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.VIewModel +{ + public enum SchemaItemType + { + Field, + Relationship + } + public abstract class SchemaItem + { + public string Name { get; set; } + public bool IsSelected { get; set; } + public abstract SchemaItemType schemaItemType { get; } + } + public class FieldSchemaItem : SchemaItem + { + + public FieldComponentMetadataViewModel OriginalItem { get; set; } + + public override SchemaItemType schemaItemType => SchemaItemType.Field; + } + public class RelationshipSchemaItem : SchemaItem + { + + public RelationshipComponentMetadataViewModel OriginalItem { get; set; } + + public override SchemaItemType schemaItemType => SchemaItemType.Relationship; + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaTabViewModel.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaTabViewModel.cs new file mode 100644 index 0000000..42ce18f --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaTabViewModel.cs @@ -0,0 +1,158 @@ +using Dataverse.ConfigurationMigrationTool.Console.Features.Shared.Domain; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Common; +using Dataverse.ConfigurationMigrationTool.XrmToolBox.Mappers; +using Microsoft.Xrm.Sdk.Metadata; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace Dataverse.ConfigurationMigrationTool.XrmToolBox.VIewModel +{ + public class SchemaTabViewModel + { + //MultiSelect is type of virtual + private static IMapper entitySchemaMapper = new EntityMetadataToEntitySchemaMapper(); + private static IMapper attributeSchemaMapper = new AttributeMetadataToFIeldSchemaMapper(); + private static IMapper relationshipSchemaMapper = new ManyToManyRelationshipMetadataToRelationshipSchema(); + private static readonly AttributeTypeCode[] ExcludedAttributeTypes = new[] + { + AttributeTypeCode.Virtual, + AttributeTypeCode.PartyList, + AttributeTypeCode.ManagedProperty, + AttributeTypeCode.CalendarRules, + AttributeTypeCode.EntityName + }; + public Dictionary> SelectedSchemaItems = new Dictionary>(); + public EntityMetadata CurrentSelectedEntity { get; set; } + + public BindingList entityMetadataViewModels = new BindingList() + { + AllowRemove = true, + AllowNew = false + }; + public BindingList relationshipViewModels = new BindingList() + { + AllowRemove = true, + AllowNew = false + }; + public void ClearMetadata() + { + this.entityMetadataViewModels.Clear(); + this.relationshipViewModels.Clear(); + } + public void SetEntityMetadataSelection(EntityMetadata entityMetadata) + { + CurrentSelectedEntity = entityMetadata; + var fields = entityMetadata + .Attributes + .Where(a => a.IsValidForCreate == true && + a.IsValidForUpdate == true && + a.AttributeType != null && + !ExcludedAttributeTypes.Contains(a.AttributeType.Value)) + .OrderBy(a => a.DisplayName?.UserLocalizedLabel?.Label ?? a.LogicalName) + .Select(a => new FieldComponentMetadataViewModel + { + LogicalName = a.LogicalName, + DisplayName = a.DisplayName?.UserLocalizedLabel?.Label ?? a.LogicalName, + DataType = a.AttributeType.ToString(), + IsSelected = false + }) + .ToList(); + this.entityMetadataViewModels.AddRange(fields); + + var relationships = entityMetadata.ManyToManyRelationships + .OrderBy(r => r.SchemaName) + .Where(r => r.Entity1LogicalName == entityMetadata.LogicalName) + .Select(r => new RelationshipComponentMetadataViewModel + { + TableName = r.IntersectEntityName, + SchemaName = r.SchemaName, + TargetEntity = r.Entity2LogicalName, + IsSelected = false + }).ToList(); + this.relationshipViewModels.AddRange(relationships); + } + public void MoveRight() + { + // Move to next tab logic + + + List schemaItems; + if (!SelectedSchemaItems.TryGetValue(CurrentSelectedEntity.LogicalName, out schemaItems)) + { + schemaItems = new List(); + SelectedSchemaItems[CurrentSelectedEntity.LogicalName] = schemaItems; + } + var fields = this.entityMetadataViewModels + .Where(f => f.IsSelected && schemaItems.Where(i => i.schemaItemType == SchemaItemType.Field).All(i => i.Name != f.LogicalName)); + + var relationships = this.relationshipViewModels.Where(r => r.IsSelected && + schemaItems.Where(i => i.schemaItemType == SchemaItemType.Relationship).All(i => i.Name != r.SchemaName)); + schemaItems.AddRange(fields.Select(f => new FieldSchemaItem + { + Name = f.LogicalName, + IsSelected = false, + OriginalItem = f + }).ToList()); + schemaItems.AddRange(relationships.Select(r => new RelationshipSchemaItem + { + Name = r.SchemaName, + IsSelected = false, + OriginalItem = r + }).ToList()); + + + + } + public void MoveLeft(IEnumerable> items) + { + foreach (var kv in items) + { + if (!SelectedSchemaItems.ContainsKey(kv.Key)) continue; + var list = SelectedSchemaItems[kv.Key]; + foreach (var item in kv.Value) + { + list.Remove(item); + } + } + } + public void Reset() + { + + this.ClearMetadata(); + this.SelectedSchemaItems.Clear(); + this.CurrentSelectedEntity = null; + } + public DataSchema GenerateSchema(IEnumerable AllEntitiesMetadata) + { + var schema = new DataSchema() + { + Entity = new List() + }; + foreach (var kv in SelectedSchemaItems.OrderBy(k => k.Key)) + { + var entityMetadata = AllEntitiesMetadata.FirstOrDefault(e => e.LogicalName == kv.Key); + if (entityMetadata == null) continue; + + var EntitySchema = entitySchemaMapper.Map(entityMetadata); + schema.Entity.Add(EntitySchema); + foreach (var fieldSchemaItem in kv.Value.Where(i => i.schemaItemType == SchemaItemType.Field)) + { + var fieldMetadata = entityMetadata.Attributes.FirstOrDefault(a => a.LogicalName == fieldSchemaItem.Name); + if (fieldMetadata == null) continue; + var fieldSchema = attributeSchemaMapper.Map(fieldMetadata); + EntitySchema.Fields.Field.Add(fieldSchema); + } + //Implement Relationship schema + foreach (var relationshipSchemaItem in kv.Value.Where(i => i.schemaItemType == SchemaItemType.Relationship)) + { + var relationshipMetadata = entityMetadata.ManyToManyRelationships.FirstOrDefault(r => r.SchemaName == relationshipSchemaItem.Name); + if (relationshipMetadata == null) continue; + var relationshipSchema = relationshipSchemaMapper.Map(relationshipMetadata); + EntitySchema.Relationships.Relationship.Add(relationshipSchema); + } + } + return schema; + } + } +} diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/app.config b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/app.config new file mode 100644 index 0000000..36d3625 --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/app.config @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config new file mode 100644 index 0000000..7c5e22f --- /dev/null +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln index 8ff5160..fc22cc3 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln @@ -10,8 +10,12 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + Dataverse.ConfigurationMigrationTool.XTB.nuspec = Dataverse.ConfigurationMigrationTool.XTB.nuspec + ..\..\GitVersion.yml = ..\..\GitVersion.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dataverse.ConfigurationMigrationTool.XrmToolBox", "Dataverse.ConfigurationMigrationTool.XrmToolBox\Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj", "{7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,6 +30,8 @@ Global {3219AEFD-128B-4FC1-934D-1FAACA38956C}.Debug|Any CPU.Build.0 = Debug|Any CPU {3219AEFD-128B-4FC1-934D-1FAACA38956C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3219AEFD-128B-4FC1-934D-1FAACA38956C}.Release|Any CPU.Build.0 = Release|Any CPU + {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE