From c85e4b77f49246a65629b394b8db838c1dcca370 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:31:06 -0400 Subject: [PATCH 1/8] add xtb project for schema generation --- .github/workflows/ci-pipeline-validation.yml | 2 +- GitVersion.yml | 101 ++++ images/configurationmigrationtool_64.png | Bin 0 -> 2975 bytes .../Console.Tests/CodeCoverage.runsettings | 2 +- .../EntitySchemaValidatorTests.cs | 0 .../Features/Shared/Domain/DataSchema.cs | 161 +++--- .../Features/Shared/IFileDataService.cs | 13 +- .../Common/BindingListExtensions.cs | 23 + .../Common/EntityExtensions.cs | 22 + .../Common/ToolConstants.cs | 10 + .../ConfigurationMigrationControl.cs | 521 ++++++++++++++++++ .../ConfigurationMigrationControl.designer.cs | 485 ++++++++++++++++ .../ConfigurationMigrationControl.resx | 226 ++++++++ .../ConfigurationMigrationPlugin.cs | 85 +++ ...nfigurationMigrationTool.XrmToolBox.csproj | 376 +++++++++++++ .../Domain/Abstraction/IFileService.cs | 7 + .../Domain/Abstraction/IMetadataService.cs | 12 + .../Domain/Abstraction/ISolutionService.cs | 12 + .../Domain/Solution.cs | 18 + .../Domain/SolutionEntityComponent.cs | 12 + .../AttributeMetadataToFIeldSchemaMapper.cs | 67 +++ .../EntityMetadataToEntitySchemaMapper.cs | 23 + .../EntityToSolutionComponentMapper.cs | 18 + .../Mappers/EntityToSolutionMapper.cs | 20 + .../Mappers/IMapper.cs | 7 + ...elationshipMetadataToRelationshipSchema.cs | 22 + .../Properties/AssemblyInfo.cs | 34 ++ .../Properties/Resources.Designer.cs | 113 ++++ .../Properties/Resources.resx | 136 +++++ .../ConfigurationMigrationTool_32x32.png | Bin 0 -> 1242 bytes .../Resources/left-arrow.png | Bin 0 -> 472 bytes .../Resources/right-arrow.png | Bin 0 -> 407 bytes .../Resources/schema.png | Bin 0 -> 1013 bytes .../Resources/xml.png | Bin 0 -> 13960 bytes .../Services/DataverseMetadataService.cs | 52 ++ .../Services/DataverseSolutionService.cs | 66 +++ .../Services/XmlFileService.cs | 18 + .../Settings.cs | 19 + .../FieldComponentMetadataViewModel.cs | 11 + .../RelationshipComponentMetadataViewModel.cs | 10 + .../VIewModel/SchemaItem.cs | 28 + .../VIewModel/SchemaTabViewModel.cs | 158 ++++++ .../app.config | 67 +++ .../packages.config | 59 ++ .../Dataverse.ConfigurationMigrationTool.sln | 8 + 45 files changed, 2941 insertions(+), 83 deletions(-) create mode 100644 GitVersion.yml create mode 100644 images/configurationmigrationtool_64.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Console.Tests/Features/Import/Validators/Rules/EntitySchemas/EntitySchemaValidatorTests.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/BindingListExtensions.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/EntityExtensions.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Common/ToolConstants.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.designer.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationControl.resx create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/ConfigurationMigrationPlugin.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IFileService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/IMetadataService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Abstraction/ISolutionService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/Solution.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Domain/SolutionEntityComponent.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/AttributeMetadataToFIeldSchemaMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityMetadataToEntitySchemaMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionComponentMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/EntityToSolutionMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/IMapper.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Mappers/ManyToManyRelationshipMetadataToRelationshipSchema.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.Designer.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/Resources.resx create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/ConfigurationMigrationTool_32x32.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/left-arrow.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/right-arrow.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/schema.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Resources/xml.png create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseMetadataService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/DataverseSolutionService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Services/XmlFileService.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Settings.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/FieldComponentMetadataViewModel.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/RelationshipComponentMetadataViewModel.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaItem.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/VIewModel/SchemaTabViewModel.cs create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/app.config create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index 7c5400c..e1b674a 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -16,7 +16,7 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: windows-latest steps: - uses: actions/checkout@v3 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 0000000000000000000000000000000000000000..f89f2c726148bd23144b29cca47101a18406e17a GIT binary patch literal 2975 zcmV;Q3t;q#P)ee73$qfm4&$oZv8=Axz z61+FIoqT5AnY{O&_c_n=bBr;R2lcjnBfSD_GwhQ^b9GA<1tlO2i_uc-MJAN+(oW6Uxy`C!FT&e_j zG5c0s7KA*HarsF+ZswVrbfvnHQU?^>s##wA3dl`-)B(H>GyS+y3^S!d&y=kJmsTj>2h7;C zieEtg$`{V;}m)t3nWt1Zl86ZC~>{%;X!q>x^9jADHV24`hh-Zoh+B*^WGTK zN9s&32gJYYd4E^|kC@}GriJ#Jm3&^GQNRPh2qg?XNe6UYjld^1-fbI`Y5dghYEBsN zQyF=E89WFFbd8T;+~6?2hZvDWUGs3EW!&o|?oXmn08jE|0{jR_ z>KbFP);;l{eHB;;Y)^v3S|={q_#J!TJl$2%K|ddl)GrUyg|i$&?X)KZc~iMy}wah>v>D z*a=mN$=K%|2jpm$qebB57%4}lcZ~Or2@)ese+lbdM1Xc69oXHP&-6LqpF7Aj#B>5a z4t$nwwrOC7wx4js;OUZiB$C0DKipu=XZ=uTcfyA;W&{5Q7~*)JYXBXTb|<(aag#S< zKa(pf&Hr+(?WkF256?V}>wvcYB=kY2NPZq5u?h%*5gqM(7~s_#?T-h_ZT*3z_C;XO zrL*AM@l<%RuJn%EfJ985hYh!5fix z34`7{8f0wuj%U1h?8qY913;CcR-!Ij3_KqbQFwDi!eI$qrTSB*olV>5<4#QNLGv~ zzyFH0PFSZi2|3?HF^**dBi=@bK8)>yRtMYygeFdH&t)2zYN~8kO_e=*@i?v@v@s%P z#>zn(cp^Nv5f8~DS~Ix_$@VX`*Y@9N|Fgf$Du7*slo* zW0%2rAK$eZNJFGe#-l;Nx1L^Qdy{DTaVFiT0_R!j9iQ^?wcuF~FPS4FbbM)mVXza& z6UQQaGge|R*uuridHhG#3~=q|ly@|ZtISa(<3eDfEZ&6Z!vSyjfV~*oJZz$aUv2ol zwE$eQLOIXe6_=STrrfEVKinLbIqg1W$Knfau^bo5ah5kudzUcS=pCVcSy76CX$3bGV$ zLuVgfW{0K{1PBmkVk|Y|KY|d(uT!%(4k(~i#*xJ9n(ZB#3dr}4QSi=65aJGKBEEmW z^CS68udFn`pj>B%Fat3kV{wls238He<5fxmUBAYrwkYQuEVtOC`;^}i<$Qx;;NMZA z4zzApe;7^9j2nrEx-r`i#;dF+s_c(7Rrb@F<5_5fN*Q2 zZ5itMdov^_L@r*TbBJ&`#uaTLbDWVQ_!r>s*8!JSC_jwy4U8*foG$)SnhJJgk&^wx zL6fqBl3|pQM&IZEobh5qBf$xUNH%q~O3uAo`6*BlNa-3NE54Zq8ROJf?%?=8!9za6 z&(kw`vFoIGX|;lvU*$tS;0ka2Ge)j6F4$Sof4+aRq;wH1zF7~+_*|b3=y~tG(Im&Q z!~uxF8{lZeF=uCmJ(nsSQ+rri)DA+AQNRb`_^a`@Jo8d%eAmP&!y?pja{ba87jogQ z6;_vQoqCh 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.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 = "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"; + } +} 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..9f53bde --- /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.1.2.0")] +[assembly: AssemblyFileVersion("1.1.2.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 0000000000000000000000000000000000000000..96529276e9ca1f871655f81abad6e408ed387ff1 GIT binary patch literal 1242 zcmV<01SR{4P)dnA{u!z0jq%*Q6s?z zQ%$G|k{IGA(ZmoGKkAbLjnY7<27#uaNDzn+5e>AZ3fvXU^Q2Ip@MH{D+v`suSYAt^@CNG*M~jv(al8&R#Nm>-#uq)O z4;6*2Y8W331X~)uEX6SW_|tMeDqpM!EMBkod*yip+k(=APzyW&OdruH2#vtIH^l)g zM-H?y7eq&4 zAX2LpVVlh}6dZdIjvL{)S8}2x1*#1^I?nN)7it`E7I=TKf=vle2H2w@qNQ9Apj`A_ zQ6qqI7Ukfuu-+N!ROt{yB8-sfh#F^{AEM4Cc%rJn8)}%Q*Y<{-DhCmE=2N^K)qbMD zlAL9ArRPmA)L==BdOQepsjxdy!CS^A*dK&^@N8_~D))pusbHy16i9ZwDrtpvD3Z+a zMN|idD)<~hY+%z*&9W>MNjgt0O6t-hlC77^M#e2Zm)3UgGr91ORq zVPY_h2^NB(uXD3RYO}MpXrH$nQ^Qw9M}4r=d&_UZGTu8TdP`-w9KBI!RwanxN8mN! z4q#4Mjwy4V7h!GG2nei2xf%=?w8VLPnX?wCEUWdPJmwI74`X>qhnR=30C+)4C6iU? z3Bn=9agr>{qdE`->r*_)AhSitsPK|NT1z?A7Yv675`32|utJ(vsj6^E4V?<#2cb_a zxd7wTP+hdtsbz6640mppNM$%{i}rhq36}lda>6_M)G**3>7rp`aLn_LCEk*Dz)?|l zRV8W7UL_;2R}~p)%cCmi>!=X`j;eCX7I;b>bwSw|Ac2BhyZ;_>7c7|?&&qs`jm9!H zKpsdbWPyvoA(UM;=V*&+{qz);&Fp1`x3mUjGEe{#Kn~~yx)j>;7ugYGQ8x#%4mLjOC+m*2(3#e6i86A^z{zEnE+O0AdQ-a#Ge;R{#J207*qoM6N<$ Ef=Kfz0ssI2 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..70545249ac9c37e1a07928c02abd98751099ff5a GIT binary patch literal 472 zcmV;}0Vn>6P)+7y3=JZP+Ho2iAIX!G^L@7CW5*0=XAv;7!pv5)2$)$PcmW>F zY+z=+&IXc#8qfzufNE_o^nGXEK=|4exPUP*pH#pD7qFjn27q-B{0dl1CeQ%C29~YbP-d+-QhouFJnpgJN**F7!u77_^)KyFQp?oUP^;Xv7{leUG0u~ z983BP$>B>LK3$W4gI9j)La*x*Sd}<9%&Y)bBt3Q-`FFmy00hxf789 O0000xJHx&=ckpFCl;kLl$V$5W#(lUCnpx9>g5-u&wghk1yqz7;1lBd z|Nnm=lMY~8*`_X_HA*Exe!&c^T--do{6@|m$tew8{VUe2UB6}D{sRXOojCLH`;VW$ z|NNDWITQj^UF7NF7-Dhy>SS}i1_vG%U%R3s4g0p44v9xKXxjL#qBo_D&nY z_F2;-gtXGknjT89xg9;gvD{wDaaLrI%=U|$L>9jgVV#%h?xg%FbmHtAZD-6Y1Gh8p wnZDDU!EWEnftn#SAVw<=af-p`1tVr8c>jUy85}Sb4q9e03B{_=l}o! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ce140e87fd5486f03a36d8e2a52ddfae65615ff2 GIT binary patch literal 1013 zcmV5vSyP9_m?vavz!KsRt76l6}E!O@I#%yhG9 z*R;*e+U6#YA4am&=56|i!{wgmJm-JTdG2}6l_(B3upTg#u*t{oz~BE?N=1yZacYB69{zAw2D zfnqXgOKWP1WmgbS`?pH(zjva>ZdZQ-8cPyn5gi>x^>~o%_MDu%aG{{>AEDo*;qaf* zlWW$yHC6i=Xn-;Y*ic_jG#nPYXkcBME zEUT?8<>DbhmDPH{Y;*J?q0A%`UVyP!jJNvxF^XhhGMN|%1WJ-*2^y6p>WP*kU)s%@ zlV3kOHk-|0@a)-=5tc)iV71Nd5p}+8$Wr5voj8K&9a?xbpMJ9tJ zKWL??+anxo-^xo}FD{6Daia;rC)?LD{nstNXxq$WBuub#H#g3I13={RMLuuc#J!t0 z`1U|YZkyA)ocX!AMeY`Vrjo?Q?p{u}H{so~1J@HPNhOmE9eNE}QMmZ#>zFK7jM-UI zNxf|C3tkl~TAn2_c9)ec?K$1fw~uhHXCF-m`^!2lOYr?WeQfOR<;P?1=AwLjdo!>8 z5@F4AFXZg>@fhKu^N1pi2oFni-$*7!R_Wcc1OF?1c!C4WCuZ2t^)j}l9+V|EGKPV# z=M5YUUc8&P5xF$PV@u*(l2byJe%G$0;T94@!`_`{Ei_W^KuHa@um!0mFKJbv_OPj^?>f668w z1rIr87&GG&6Qa;sB^Zmv0CXrm^w+AYdd2VaWhxo^`G}2=OT*#t#MJckI^ce(x$6Qp jQ2B357CLYhm;?9+zu;%JwSD6E00000NkvXXu0mjf@44bO literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d02bd71d559e328416409d8ae2391f49869c7c14 GIT binary patch literal 13960 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 _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..74ddecd 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,10 @@ 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}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From df0e0aa7d85093e9e541dfdef67283c1ac5b1790 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:36:22 -0400 Subject: [PATCH 2/8] fix longpath for ci-validation --- .github/workflows/ci-pipeline-validation.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index e1b674a..0d69d97 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -19,6 +19,8 @@ jobs: runs-on: windows-latest steps: + - name: git configure long path + run: git config --global core.longpaths tru - uses: actions/checkout@v3 - name: Setup .NET uses: actions/setup-dotnet@v3 From 61ced47947e2216d9abd16ed08dcf9fcd0fb0716 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:37:21 -0400 Subject: [PATCH 3/8] fix longpath for ci-validation2 --- .github/workflows/ci-pipeline-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index 0d69d97..bab69e0 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -20,7 +20,7 @@ jobs: steps: - name: git configure long path - run: git config --global core.longpaths tru + run: git config --global core.longpaths true - uses: actions/checkout@v3 - name: Setup .NET uses: actions/setup-dotnet@v3 From 5928b4271d48a98b75bb1e9033383ac9e9d5801f Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:42:25 -0400 Subject: [PATCH 4/8] . --- .github/workflows/ci-pipeline-validation.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index bab69e0..9781345 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -26,8 +26,17 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - - name: Restore dependencies - run: dotnet restore ${{ env.solutionPath }} + + - name: Setup NuGet + uses: nuget/setup-nuget@v1 + with: + nuget-version: 'latest' # Or a specific version like '5.11.1' + + - name: Restore NuGet packages + run: nuget restore ${{ env.solutionPath }} # Replace with your solution file or project directory + + # - name: Restore dependencies + # run: dotnet restore ${{ env.solutionPath }} - name: Build run: dotnet build ${{ env.solutionPath }} --configuration Release --no-restore - name: Execute unit tests From f96bb5166cd45407be45250e6b135b475b4713ac Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:55:49 -0400 Subject: [PATCH 5/8] . --- .github/workflows/ci-pipeline-validation.yml | 14 ++++++++++---- .../Dataverse.ConfigurationMigrationTool.sln | 2 -- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index 9781345..93d0efd 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -12,6 +12,8 @@ on: branches: [ "main" ] env: solutionPath: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln' + xrmToolboxConfigPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config' + xrmToolboxPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj' solutionFolder: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool' jobs: build: @@ -32,11 +34,15 @@ jobs: with: nuget-version: 'latest' # Or a specific version like '5.11.1' - - name: Restore NuGet packages - run: nuget restore ${{ env.solutionPath }} # Replace with your solution file or project directory + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 - # - name: Restore dependencies - # run: dotnet restore ${{ env.solutionPath }} + - name: Restore NuGet packages + run: nuget restore ${{ env.xrmToolboxConfigPath }} # Replace with your solution file or project directory + - name: Build xrmToolBox + run: msbuild ${{ env.xrmToolboxPath }} -t:rebuild -verbosity:diag -property:Configuration=Release + - name: Restore dependencies + run: dotnet restore ${{ env.solutionPath }} - name: Build run: dotnet build ${{ env.solutionPath }} --configuration Release --no-restore - name: Execute unit tests diff --git a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln index 74ddecd..fc22cc3 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln @@ -31,9 +31,7 @@ Global {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}.Debug|Any CPU.Build.0 = Debug|Any CPU {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7DD84F5D-0F62-40BE-99F6-47E49BBD1C46}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 17eff767e10ae9ff9fd9dd9995155ad414f6c311 Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 06:01:19 -0400 Subject: [PATCH 6/8] . --- .github/workflows/ci-pipeline-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-pipeline-validation.yml index 93d0efd..ccab245 100644 --- a/.github/workflows/ci-pipeline-validation.yml +++ b/.github/workflows/ci-pipeline-validation.yml @@ -38,7 +38,7 @@ jobs: uses: microsoft/setup-msbuild@v2 - name: Restore NuGet packages - run: nuget restore ${{ env.xrmToolboxConfigPath }} # Replace with your solution file or project directory + 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: Restore dependencies From d8ebd21197296ee30c0dd9da43c70582aa4f588a Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 06:09:37 -0400 Subject: [PATCH 7/8] . --- ...n.yml => ci-linux-pipeline-validation.yml} | 20 +------- .../ci-msbuild-pipeline-validation.yml | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 18 deletions(-) rename .github/workflows/{ci-pipeline-validation.yml => ci-linux-pipeline-validation.yml} (79%) create mode 100644 .github/workflows/ci-msbuild-pipeline-validation.yml diff --git a/.github/workflows/ci-pipeline-validation.yml b/.github/workflows/ci-linux-pipeline-validation.yml similarity index 79% rename from .github/workflows/ci-pipeline-validation.yml rename to .github/workflows/ci-linux-pipeline-validation.yml index ccab245..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 @@ -12,35 +12,19 @@ on: branches: [ "main" ] env: solutionPath: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.sln' - xrmToolboxConfigPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/packages.config' - xrmToolboxPath: 'src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Dataverse.ConfigurationMigrationTool.XrmToolBox.csproj' solutionFolder: '${{ github.workspace }}/src/Dataverse.ConfigurationMigrationTool' jobs: build: - runs-on: windows-latest + runs-on: ubuntu-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 - 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 + + + + + + + + + From 187b1091679345dd564eac8428fc16d2513c6b0e Mon Sep 17 00:00:00 2001 From: dotnetprog <24593889+dotnetprog@users.noreply.github.com> Date: Thu, 28 Aug 2025 07:26:45 -0400 Subject: [PATCH 8/8] . --- .github/workflows/ci-tag-release.yml | 84 ++++++++++++++++++- .github/workflows/tag-with-gitversion.yml | 42 ++++++++++ ...erse.ConfigurationMigrationTool.XTB.nuspec | 28 +++++++ .../Properties/AssemblyInfo.cs | 4 +- 4 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/tag-with-gitversion.yml create mode 100644 src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XTB.nuspec 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/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/Properties/AssemblyInfo.cs b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs index 9f53bde..2a8d4c6 100644 --- a/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs +++ b/src/Dataverse.ConfigurationMigrationTool/Dataverse.ConfigurationMigrationTool.XrmToolBox/Properties/AssemblyInfo.cs @@ -29,6 +29,6 @@ // Revision // -[assembly: AssemblyVersion("1.1.2.0")] -[assembly: AssemblyFileVersion("1.1.2.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.1.2-AddXrmtoolBox.0+Branch.feature-AddXrmtoolBox.Sha.e4430db29230de99c44c859669fd6b06ef375737")]