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 @@
-
+
-
+
-
+
-
+
-
+