diff --git a/src/Build.Shared.props b/src/Build.Shared.props index d7cbcab..080f0b0 100644 --- a/src/Build.Shared.props +++ b/src/Build.Shared.props @@ -5,8 +5,9 @@ 2.9.1 3.19.8 4.35.1 - 4.6.4784-weekly-2107.4 - 4.6.4784-weekly-2107.4 + 4.6.6061-weekly-2108.5 + 4.6.6061-weekly-2108.5 + 4.7.6346-master 11.0.2 2.3.20 9.0.2.42 diff --git a/src/GeneralTools/DataverseClient/Client/ConnectionService.cs b/src/GeneralTools/DataverseClient/Client/ConnectionService.cs index 422215f..a36da17 100644 --- a/src/GeneralTools/DataverseClient/Client/ConnectionService.cs +++ b/src/GeneralTools/DataverseClient/Client/ConnectionService.cs @@ -232,6 +232,17 @@ internal sealed class ConnectionService : IConnectionService, IDisposable /// private IOrganizationService _testSupportIOrg; + /// + /// Value used by the retry system while the code is running, + /// this value can scale up and down based on throttling limits. + /// + private TimeSpan _retryPauseTimeRunning; + + /// + /// Known types factory + /// + private KnownTypesFactory _knownTypesFactory = new KnownTypesFactory(); + #endregion #region Properties @@ -580,17 +591,11 @@ internal bool EnableCookieRelay /// Cookies that are being passed though clients, when cookies are used /// internal Dictionary CurrentCookieCollection { get; set; } = null; - + /// - /// Value used by the retry system while the code is running, - /// this value can scale up and down based on throttling limits. - /// - private TimeSpan _retryPauseTimeRunning; - - /// - /// Known types factory + /// Server Hint for the number of concurrent threads that would provbide optimal processing. /// - private KnownTypesFactory _knownTypesFactory = new KnownTypesFactory(); + internal int RecommendedDegreesOfParallelism { get; set; } = 5; // Default value. #endregion @@ -601,7 +606,7 @@ internal bool EnableCookieRelay /// /// /// - internal ConnectionService(IOrganizationService testIOrganziationSvc , string baseConnectUrl, HttpClient mockClient, ILogger logger) + internal ConnectionService(IOrganizationService testIOrganziationSvc, string baseConnectUrl, HttpClient mockClient, ILogger logger) { _testSupportIOrg = testIOrganziationSvc; WebApiHttpClient = mockClient; @@ -1371,7 +1376,7 @@ private async Task RefreshInstanceDetails(IOrganizationService dvService, Uri ur { resp = (RetrieveCurrentOrganizationResponse)dvService.Execute(request); } - catch(Exception ex) + catch (Exception ex) { dtQueryTimer.Stop(); logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Failed to Executed Command - RetrieveCurrentOrganizationRequest : RequestId={1} : total duration: {0}", dtQueryTimer.Elapsed.ToString(), trackingID.ToString()), TraceEventType.Error); @@ -1601,7 +1606,7 @@ internal async Task Command_WebAPIProcess_ExecuteAsync(Org if (cReq != null) { cancellationToken.ThrowIfCancellationRequested(); - requestBodyObject = Utilities.ToExpandoObject(cReq, metadataUtlity, methodToExecute , logEntry); + requestBodyObject = Utilities.ToExpandoObject(cReq, metadataUtlity, methodToExecute, logEntry); if (cReq.RelatedEntities != null && cReq.RelatedEntities.Count > 0) requestBodyObject = Utilities.ReleatedEntitiesToExpandoObject(requestBodyObject, cReq.LogicalName, cReq.RelatedEntities, metadataUtlity, methodToExecute, logEntry); } @@ -2055,7 +2060,15 @@ internal async Task Command_WebExecuteAsync(string queryStr if (resp != null) { - CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(resp.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value?.ToArray(), CurrentCookieCollection); + CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(resp.Headers.SingleOrDefault(header => header.Key == Utilities.ResponseHeaders.SETCOOKIE).Value?.ToArray(), CurrentCookieCollection); + string dregreeofparallelismHint = resp.Headers.SingleOrDefault(header => header.Key == Utilities.ResponseHeaders.RECOMMENDEDDEGREESOFPARALLELISM).Value?.FirstOrDefault(); + if (!string.IsNullOrEmpty(dregreeofparallelismHint)) + { + if (int.TryParse(dregreeofparallelismHint, out int idop)) + { + RecommendedDegreesOfParallelism = idop; + } + } } return resp; diff --git a/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs b/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs index 6c28595..2d4a7d4 100644 --- a/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs +++ b/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Configuration; using System.Linq; @@ -155,9 +155,23 @@ public void AfterReceiveReply(ref Message reply, object correlationState) if (httpResponseMessage != null && httpResponseMessage.Headers.Count > 0) { - var a = httpResponseMessage.Headers["Set-Cookie"]; - if (a != null) - _callerCdsConnectionServiceHandler.CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(httpResponseMessage.Headers["Set-Cookie"] , _callerCdsConnectionServiceHandler.CurrentCookieCollection); + string cookieHeader = httpResponseMessage.Headers[Utilities.ResponseHeaders.SETCOOKIE]; + if (cookieHeader != null) + { + _callerCdsConnectionServiceHandler.CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(httpResponseMessage.Headers[Utilities.ResponseHeaders.SETCOOKIE] , _callerCdsConnectionServiceHandler.CurrentCookieCollection); + } + + string dregreeofparallelismHint = httpResponseMessage.Headers[Utilities.ResponseHeaders.RECOMMENDEDDEGREESOFPARALLELISM]; + if (!string.IsNullOrEmpty(dregreeofparallelismHint)) + { + if(int.TryParse(dregreeofparallelismHint, out int idop)) + { + if (_callerCdsConnectionServiceHandler != null) + { + _callerCdsConnectionServiceHandler.RecommendedDegreesOfParallelism = idop; + } + } + } } } finally { }; diff --git a/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj b/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj index af8d65a..95d2a3a 100644 --- a/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj +++ b/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj @@ -1,4 +1,4 @@ - + Microsoft.PowerPlatform.Dataverse.Client @@ -24,9 +24,8 @@ - - - + + diff --git a/src/GeneralTools/DataverseClient/Client/ServiceClient.cs b/src/GeneralTools/DataverseClient/Client/ServiceClient.cs index b65232e..3816108 100644 --- a/src/GeneralTools/DataverseClient/Client/ServiceClient.cs +++ b/src/GeneralTools/DataverseClient/Client/ServiceClient.cs @@ -131,7 +131,7 @@ internal OrganizationWebProxyClientAsync OrganizationWebProxyClient if (_connectionSvc.WebClient == null) { if (_logEntry != null) - _logEntry.Log("OrganizationWebProxyClient is null", TraceEventType.Error); + _logEntry.Log("OrganizationWebProxyClientAsync is null", TraceEventType.Error); return null; } else @@ -140,13 +140,12 @@ internal OrganizationWebProxyClientAsync OrganizationWebProxyClient else { if (_logEntry != null) - _logEntry.Log("OrganizationWebProxyClient is null", TraceEventType.Error); + _logEntry.Log("OrganizationWebProxyClientAsync is null", TraceEventType.Error); return null; } } } - /// /// Enabled Log Capture in memory /// This capability enables logs that would normally be sent to your configured @@ -599,6 +598,11 @@ public bool UseWebApi set => _configuration.Value.UseWebApi = value; } + /// + /// Server Hint for the number of concurrent threads that would provbide optimal processing. + /// + public int RecommendedDegreesOfParallelism => _connectionSvc.RecommendedDegreesOfParallelism; + #endregion #region Constructor and Setup methods diff --git a/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs b/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs index 29eb2d9..52a92e2 100644 --- a/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs +++ b/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs @@ -1,4 +1,4 @@ -#region using +#region using using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.PowerPlatform.Dataverse.Client.Model; @@ -503,10 +503,14 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili if (attributeInfo is LookupAttributeMetadata attribData) { // Now get relationship to make sure we use the correct name. - var eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, sourceEntity.LogicalName); - var ERNavName = eData.ManyToOneRelationships.FirstOrDefault(w => w.ReferencingAttribute.Equals(attribData.LogicalName) && + EntityMetadata eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, sourceEntity.LogicalName); + string ERNavName = eData.ManyToOneRelationships.FirstOrDefault(w => w.ReferencingAttribute.Equals(attribData.LogicalName) && w.ReferencedEntity.Equals(entityReference.LogicalName)) ?.ReferencingEntityNavigationPropertyName; + if (string.IsNullOrEmpty(ERNavName )) + { + ERNavName = eData.ManyToOneRelationships.FirstOrDefault(w => w.ReferencingAttribute.Equals(attribData.LogicalName))?.ReferencingEntityNavigationPropertyName; + } if (!string.IsNullOrEmpty(ERNavName)) { @@ -660,10 +664,9 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili { var sourceMdata = mUtil.GetEntityMetadata(sourceEntity.LogicalName); if (sourceMdata != null ) - expandoObject.Add($"{sourceMdata.SchemaName}_activity_parties", partiesCollection); + expandoObject.Add($"{sourceMdata.LogicalName}_activity_parties", partiesCollection); } - return (ExpandoObject)expandoObject; } @@ -743,33 +746,7 @@ internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject root // Get the Entity relationship key and entity and reverse it back to the entity key name var eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, entItem.Value.Entities[0].LogicalName); - // Find the relationship that is referenced. - var ERM21 = eData.ManyToOneRelationships.FirstOrDefault(w1 => w1.SchemaName.ToLower().Equals(entItem.Key.SchemaName.ToLower())); - var ERM2M = eData.ManyToManyRelationships.FirstOrDefault(w2 => w2.SchemaName.ToLower().Equals(entItem.Key.SchemaName.ToLower())); - var ER12M = eData.OneToManyRelationships.FirstOrDefault(w3 => w3.SchemaName.ToLower().Equals(entItem.Key.SchemaName.ToLower())); - - // Determine which one hit - if (ERM21 != null) - { - isArrayRequired = true; - key = ERM21.ReferencedEntityNavigationPropertyName; - } - else if (ERM2M != null) - { - isArrayRequired = true; - if (ERM2M.Entity1LogicalName.ToLower().Equals(entityName)) - { - key = ERM2M.Entity1NavigationPropertyName; - } - else - { - key = ERM2M.Entity2NavigationPropertyName; - } - } - else if (ER12M != null) - { - key = ER12M.ReferencingAttribute; - } + key = ExtractKeyNameFromRelationship(entItem.Key.SchemaName.ToLower(), entityName, ref isArrayRequired, eData); if (string.IsNullOrEmpty(key)) // Failed to find key { @@ -785,7 +762,7 @@ internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject root } // generate object. - ExpandoObject ent1 = ToExpandoObject(ent, mUtil, requestedMethod , logger); + ExpandoObject ent1 = ToExpandoObject(ent, mUtil, requestedMethod, logger); if (((IDictionary)childEntities).Count() > 0) { @@ -804,6 +781,49 @@ internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject root return rootExpando; } + + /// + /// Helper to extract key name from one of the relationships. + /// + /// + /// + /// + /// + /// + private static string ExtractKeyNameFromRelationship(string schemaName, string entityName, ref bool isArrayRequired, EntityMetadata eData) + { + string key = ""; + // Find the relationship that is referenced. + OneToManyRelationshipMetadata ERM21 = eData.ManyToOneRelationships.FirstOrDefault(w1 => w1.SchemaName.ToLower().Equals(schemaName.ToLower())); + ManyToManyRelationshipMetadata ERM2M = eData.ManyToManyRelationships.FirstOrDefault(w2 => w2.SchemaName.ToLower().Equals(schemaName.ToLower())); + OneToManyRelationshipMetadata ER12M = eData.OneToManyRelationships.FirstOrDefault(w3 => w3.SchemaName.ToLower().Equals(schemaName.ToLower())); + + // Determine which one hit + if (ERM21 != null) + { + isArrayRequired = true; + key = ERM21.ReferencedEntityNavigationPropertyName; + } + else if (ERM2M != null) + { + isArrayRequired = true; + if (ERM2M.Entity1LogicalName.ToLower().Equals(entityName)) + { + key = ERM2M.Entity1NavigationPropertyName; + } + else + { + key = ERM2M.Entity2NavigationPropertyName; + } + } + else if (ER12M != null) + { + key = ER12M.ReferencingAttribute; + } + + return key; + } + /// /// Parses Key attribute collection for alt key support. /// @@ -950,6 +970,19 @@ internal static class RequestHeaders } + internal static class ResponseHeaders + { + /// + /// Recomended number of client connection threads Hint + /// + public const string RECOMMENDEDDEGREESOFPARALLELISM = "x-ms-dop-hint"; + + /// + /// header for Cookie's + /// + public const string SETCOOKIE = "Set-Cookie"; + } + /// /// Minim Version numbers for various features of Dataverse API's. /// diff --git a/src/GeneralTools/DataverseClient/DataverseClient_PackageTestSolution.sln b/src/GeneralTools/DataverseClient/DataverseClient_PackageTestSolution.sln new file mode 100644 index 0000000..3cd3753 --- /dev/null +++ b/src/GeneralTools/DataverseClient/DataverseClient_PackageTestSolution.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31729.503 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LivePackageTestsConsole", "UnitTests\LivePackageTestsConsole\LivePackageTestsConsole.csproj", "{AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BDA12603-BF5F-4103-81BE-24703407A609} + EndGlobalSection +EndGlobal diff --git a/src/GeneralTools/DataverseClient/Extensions/DynamicsExtension/Microsoft.PowerPlatform.Dataverse.Client.Dynamics.csproj b/src/GeneralTools/DataverseClient/Extensions/DynamicsExtension/Microsoft.PowerPlatform.Dataverse.Client.Dynamics.csproj index 44caaaf..6293beb 100644 --- a/src/GeneralTools/DataverseClient/Extensions/DynamicsExtension/Microsoft.PowerPlatform.Dataverse.Client.Dynamics.csproj +++ b/src/GeneralTools/DataverseClient/Extensions/DynamicsExtension/Microsoft.PowerPlatform.Dataverse.Client.Dynamics.csproj @@ -1,4 +1,4 @@ - + Microsoft.PowerPlatform.Dataverse.Client.Dynamics @@ -13,12 +13,8 @@ - - - - - - + + diff --git a/src/GeneralTools/DataverseClient/Extensions/Microsoft.Dynamics.Sdk.Messages/Microsoft.Dynamics.Sdk.Messages.Shell.csproj b/src/GeneralTools/DataverseClient/Extensions/Microsoft.Dynamics.Sdk.Messages/Microsoft.Dynamics.Sdk.Messages.Shell.csproj index e216c5a..c7fac2c 100644 --- a/src/GeneralTools/DataverseClient/Extensions/Microsoft.Dynamics.Sdk.Messages/Microsoft.Dynamics.Sdk.Messages.Shell.csproj +++ b/src/GeneralTools/DataverseClient/Extensions/Microsoft.Dynamics.Sdk.Messages/Microsoft.Dynamics.Sdk.Messages.Shell.csproj @@ -1,4 +1,4 @@ - + Microsoft.Dynamics.Sdk.Messages DataverseClient @@ -8,7 +8,8 @@ - + + diff --git a/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/DataverseClient_Core_UnitTests.csproj b/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/DataverseClient_Core_UnitTests.csproj index 154924f..d809a2f 100644 --- a/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/DataverseClient_Core_UnitTests.csproj +++ b/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/DataverseClient_Core_UnitTests.csproj @@ -1,4 +1,4 @@ - + true @@ -12,7 +12,6 @@ - diff --git a/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/ServiceClientTests.cs b/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/ServiceClientTests.cs index d129afe..d9f2da0 100644 --- a/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/ServiceClientTests.cs +++ b/src/GeneralTools/DataverseClient/UnitTests/CdsClient_Core_Tests/ServiceClientTests.cs @@ -659,6 +659,36 @@ public void ResetLocalMetadataCacheTest() //} + [Fact] + public void TestResponseHeaderBehavior() + { + Mock orgSvc = null; + Mock fakHttpMethodHander = null; + ServiceClient cli = null; + testSupport.SetupMockAndSupport(out orgSvc, out fakHttpMethodHander, out cli); + + + // Setup handlers to deal with both orgRequest and WebAPI request. + int baseTestDOP = 10; + int defaultDOP = 5; + var httpResp = new HttpResponseMessage(System.Net.HttpStatusCode.OK); + httpResp.Headers.Add(Utilities.ResponseHeaders.RECOMMENDEDDEGREESOFPARALLELISM, baseTestDOP.ToString()); + fakHttpMethodHander.Setup(s => s.Send(It.Is(f => f.Method.ToString().Equals("delete", StringComparison.OrdinalIgnoreCase)))).Returns(httpResp); + orgSvc.Setup(f => f.Execute(It.Is(p => p.Target.LogicalName.Equals("account") && p.Target.Id.Equals(testSupport._DefaultId)))).Returns(new DeleteResponse()); + + // Tests/ + cli.UseWebApi = false; + bool rslt = cli.ExecuteEntityDeleteRequest("account", testSupport._DefaultId); + Assert.True(rslt); + Assert.Equal(defaultDOP, cli.RecommendedDegreesOfParallelism); + + cli.UseWebApi = true; + cli.Delete("account", testSupport._DefaultId); + Assert.Equal(baseTestDOP, cli.RecommendedDegreesOfParallelism); + + + } + #region LiveConnectedTests [SkippableConnectionTest] diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/App.config b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/App.config new file mode 100644 index 0000000..cc82743 --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Auth.cs b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Auth.cs new file mode 100644 index 0000000..4b7759e --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Auth.cs @@ -0,0 +1,35 @@ +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Auth; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LivePackageTestsConsole +{ + public class Auth + { + /// + /// Sample / stand-in appID used when replacing O365 Auth + /// + internal static string SampleClientId = "51f81489-12ee-4a9e-aaae-a2591f45987d"; + /// + /// Sample / stand-in redirect URI used when replacing o365 Auth + /// + internal static string SampleRedirectUrl = "app://58145B91-0C36-4500-8554-080854F2AC97"; + + public static ServiceClient CreateClient() + { + var userName = Environment.GetEnvironmentVariable("XUNITCONNTESTUSERID"); + var password = Environment.GetEnvironmentVariable("XUNITCONNTESTPW"); + var connectionUrl = Environment.GetEnvironmentVariable("XUNITCONNTESTURI"); + if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(connectionUrl)) + { + throw new ArgumentNullException("Make sure to set XUNITCONNTESTUSERID, XUNITCONNTESTPW, XUNITCONNTESTURI environment variables"); + } + + return new ServiceClient(userName, ServiceClient.MakeSecureString(password), new Uri(connectionUrl), true, SampleClientId, new Uri(SampleRedirectUrl), PromptBehavior.Never); + } + } +} diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/BasicFlow.cs b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/BasicFlow.cs new file mode 100644 index 0000000..b740de7 --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/BasicFlow.cs @@ -0,0 +1,42 @@ +using FluentAssertions; +using Microsoft.Crm.Sdk.Messages; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Auth; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace LivePackageTestsConsole +{ + public class BasicFlow + { + + internal void Run() + { + Console.WriteLine("Starting Basic Flow"); + + var client = Auth.CreateClient(); + client.IsReady.Should().BeTrue(); + + Console.WriteLine("\nCalling WhoAmI"); + var whoAmIResponse = client.Execute(new WhoAmIRequest()) as WhoAmIResponse; + whoAmIResponse.Should().NotBeNull(); + Console.WriteLine($"OrganizationId:{whoAmIResponse.OrganizationId} UserId:{whoAmIResponse.UserId}"); + + Console.WriteLine("\nCalling RetrieveCurrentOrganizationRequest"); + var retrieveCurrentOrganizationRequest = new RetrieveCurrentOrganizationRequest(); + var retrieveCurrentOrganizationResponse = client.Execute(retrieveCurrentOrganizationRequest) as RetrieveCurrentOrganizationResponse; + retrieveCurrentOrganizationResponse.Should().NotBeNull(); + Console.WriteLine($"FriendlyName:{retrieveCurrentOrganizationResponse.Detail.FriendlyName} GEO:{retrieveCurrentOrganizationResponse.Detail.Geo}"); + + Console.WriteLine("\nCalling RetrieveVersionRequest"); + var retrieveVersionRequest = new RetrieveVersionRequest(); + var retrieveVersionResponse = client.Execute(retrieveVersionRequest) as RetrieveVersionResponse; + retrieveVersionResponse.Should().NotBeNull(); + Console.WriteLine($"Version:{retrieveVersionResponse.Version}"); + } + } +} diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj new file mode 100644 index 0000000..31f8d45 --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj @@ -0,0 +1,40 @@ + + + + Exe + net462;netcoreapp3.1 + true + DataverseClient-Tests-Package + false + LivePackageTestsConsole + LivePackageTestsConsole + true + + + + + + + $(RepoRoot)\binSigned\$(Configuration)\packages + + 0.5.9 + + + + false + MSB3277; + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Program.cs b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Program.cs new file mode 100644 index 0000000..bda6787 --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/Program.cs @@ -0,0 +1,61 @@ +using System; + +namespace LivePackageTestsConsole +{ + /// + /// This program will test run against live tests on a known Nuget Package version. + /// + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Starting Tests"); + + if (0 < args.Length) + { + if (string.Compare(args[0], "BasicFlow", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new BasicFlow(); + tests.Run(); + } + else if (string.Compare(args[0], "ListSolutions", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new SolutionTests(); + + tests.ListSolutions(); + } + else if (string.Compare(args[0], "ExportSolution", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new SolutionTests(); + + tests.ExportSolution(); + } + else if (string.Compare(args[0], "ImportSolution", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new SolutionTests(); + + tests.ImportSolution(); + } + else if (string.Compare(args[0], "DeleteSolution", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new SolutionTests(); + + tests.DeleteSolution(); + } + else if (string.Compare(args[0], "TokenRefresh", StringComparison.OrdinalIgnoreCase) == 0) + { + var tests = new TokenRefresh(); + + tests.Run(); + } + } + else + { + var tests = new BasicFlow(); + tests.Run(); + } + + Console.ReadKey(); + } + } +} diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/SolutionTests.cs b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/SolutionTests.cs new file mode 100644 index 0000000..9c27828 --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/SolutionTests.cs @@ -0,0 +1,63 @@ +using Microsoft.Crm.Sdk.Messages; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Messages; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LivePackageTestsConsole +{ + public class SolutionTests + { + public void ImportSolution() + { + Console.WriteLine("Starting ImportSolution"); + + var client = Auth.CreateClient(); + + client.ImportSolution(Path.Combine("TestData", "TestSolution_1_0_0_1.zip"), out var importId); + Console.WriteLine($"ImportSolution id:{importId}"); + } + + public void ExportSolution() + { + Console.WriteLine("Starting ExportSolution"); + + var client = Auth.CreateClient(); + + var request = new ExportSolutionRequest() + { + SolutionName = "TestSolution" + }; + + var response = client.Execute(request) as ExportSolutionResponse; + Console.WriteLine($"ExportSolutionFile length:{response.ExportSolutionFile.Length}"); + } + + public void ListSolutions() + { + Console.WriteLine("Starting ListSolutions"); + + var client = Auth.CreateClient(); + + var request = new RetrieveOrganizationInfoRequest(); + var response = client.Execute(request) as RetrieveOrganizationInfoResponse; + Console.WriteLine($"Solutions.Count:{response.organizationInfo.Solutions.Count}"); + } + + public void DeleteSolution() + { + Console.WriteLine("Starting DeleteSolution"); + + var client = Auth.CreateClient(); + + var request = new DeleteRequest() { Target = new EntityReference("solution", new Guid("a50ac31a-b3f3-4fd3-b691-20ddc4d494d7")) }; + //var request = new DeleteRequest() { Target = new EntityReference("solutions", "UniqueName", "TestSolution") }; + var response = client.Execute(request) as DeleteResponse; + Console.WriteLine("Done"); + } + } +} diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TestData/TestSolution_1_0_0_1.zip b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TestData/TestSolution_1_0_0_1.zip new file mode 100644 index 0000000..4dd8ba2 Binary files /dev/null and b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TestData/TestSolution_1_0_0_1.zip differ diff --git a/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TokenRefresh.cs b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TokenRefresh.cs new file mode 100644 index 0000000..f20505d --- /dev/null +++ b/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/TokenRefresh.cs @@ -0,0 +1,36 @@ +using FluentAssertions; +using Microsoft.Crm.Sdk.Messages; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Auth; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace LivePackageTestsConsole +{ + public class TokenRefresh + { + + internal void Run() + { + Console.WriteLine("Starting TokenRefresh"); + + var client = Auth.CreateClient(); + client.IsReady.Should().BeTrue(); + + Console.WriteLine("Calling WhoAmI"); + var response1 = client.Execute(new WhoAmIRequest()) as WhoAmIResponse; + response1.Should().NotBeNull(); + + Console.WriteLine("Going to sleep for 26 hours until token expires"); + Thread.Sleep(1000 * 60 * 60 * 26); + + Console.WriteLine("Calling WhoAmI after long sleep"); + var response2 = client.Execute(new WhoAmIRequest()) as WhoAmIResponse; + response2.Should().NotBeNull(); + } + } +} diff --git a/src/GeneralTools/DataverseClient/UnitTests/LiveTestsConsole/LiveTestsConsole.csproj b/src/GeneralTools/DataverseClient/UnitTests/LiveTestsConsole/LiveTestsConsole.csproj index b834103..d201006 100644 --- a/src/GeneralTools/DataverseClient/UnitTests/LiveTestsConsole/LiveTestsConsole.csproj +++ b/src/GeneralTools/DataverseClient/UnitTests/LiveTestsConsole/LiveTestsConsole.csproj @@ -12,7 +12,6 @@ - diff --git a/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.ReleaseNotes.txt b/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.ReleaseNotes.txt index 3f71b19..d2edf8b 100644 --- a/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.ReleaseNotes.txt +++ b/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.ReleaseNotes.txt @@ -8,6 +8,15 @@ Notice: Note: that only OAuth, Certificate, ClientSecret Authentication types are supported at this time. ++CURRENTRELEASEID++ +Added new property "RecommendedDegreesOfParallelism". + This property will report the recommended number of threads for communicating with Dataverse. + +0.5.8: +Changed internal dependencies to better align with Dataverse Server. +Fixed an issue with updating ManyToOneRelationship based attributes. +Fixed an issue with activity parties relationships. + +0.5.7: Removed Sealed attribute from the Service Client Class. Fixed an issue with null values being sent to the API causing a null exception to be raised. Fixed missing nuget dependency for Microsoft.Identity.Client.Extensions.Msal, Nuget now properly includes that package. diff --git a/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.nuspec b/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.nuspec index 0234110..4877577 100644 --- a/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.nuspec +++ b/src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.nuspec @@ -23,7 +23,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -126,23 +126,23 @@ - + - + - + - + - +