diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs
index 4709fc2..2fa9f3f 100644
--- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs
+++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs
@@ -4,7 +4,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+ using System.ServiceModel;
+ using System.Web.Configuration;
using Capgemini.PowerApps.PackageDeployerTemplate.Exceptions;
+ using DocumentFormat.OpenXml.Office2016.Excel;
using Microsoft.Extensions.Logging;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
@@ -17,9 +20,20 @@
///
public class CrmServiceAdapter : ICrmServiceAdapter, IDisposable
{
+ private static readonly int[] CustomizationLockErrorCodes = new int[]
+ {
+ Constants.ErrorCodes.CustomizationLockExBlockingUnknown,
+ Constants.ErrorCodes.CustomizationLockExBothKnownDifferent,
+ Constants.ErrorCodes.CustomizationLockExBothKnownSame,
+ Constants.ErrorCodes.CustomizationLockExBlockedUnknown,
+ Constants.ErrorCodes.CustomizationLockExBothUnknown,
+ };
+
private readonly CrmServiceClient crmSvc;
private readonly ILogger logger;
+ private Policy customizationLockPolicy;
+
///
/// Initializes a new instance of the class.
///
@@ -34,6 +48,24 @@ public CrmServiceAdapter(CrmServiceClient crmSvc, ILogger logger)
///
public Guid? CallerAADObjectId { get => this.crmSvc.CallerAADObjectId; set => this.crmSvc.CallerAADObjectId = value; }
+ private Policy CustomizationLockPolicy
+ {
+ get
+ {
+ this.customizationLockPolicy ??= Policy
+ .Handle()
+ .RetryForever(_ => this.WaitForSolutionHistoryRecordsToComplete())
+ .Wrap(
+ Policy
+ .Handle()
+ .WaitAndRetryForever(
+ sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(30),
+ onRetry: (_, timeSpan) => this.logger.LogInformation("A solution concurrency issue has occured. Waiting for {0} seconds before retrying.", timeSpan.TotalSeconds)));
+
+ return this.customizationLockPolicy;
+ }
+ }
+
///
public ExecuteMultipleResponse ExecuteMultiple(IEnumerable requests, bool continueOnError = true, bool returnResponses = true, int? timeout = null)
{
@@ -262,7 +294,7 @@ public string GetEntityTypeCode(string entityLogicalName)
}
///
- public TResponse Execute(OrganizationRequest request, string username, bool fallbackToExistingUser = true)
+ public TResponse Execute(OrganizationRequest request, string username, bool fallbackToExistingUser = true, bool logErrors = true)
where TResponse : OrganizationResponse
{
if (request is null)
@@ -290,7 +322,7 @@ public TResponse Execute(OrganizationRequest request, string username
{
this.logger.LogWarning($"Failed to execute {request.RequestName} as {username} as the user was not found.");
}
- else
+ else if (logErrors)
{
this.logger.LogWarning(ex, $"Failed to execute {request.RequestName} as {username}. {ex.Message}");
}
@@ -342,31 +374,13 @@ public void WaitForSolutionHistoryRecordsToComplete()
}
///
+ [Obsolete("Please use ExecuteManySolutionHistoryOperation.", true)]
public IEnumerable ExecuteMultipleSolutionHistoryOperation(IEnumerable requests, string username, int? timeout = null)
{
- var customizationLockErrorCodes = new int[]
- {
- Constants.ErrorCodes.CustomizationLockExBlockingUnknown,
- Constants.ErrorCodes.CustomizationLockExBothKnownDifferent,
- Constants.ErrorCodes.CustomizationLockExBothKnownSame,
- Constants.ErrorCodes.CustomizationLockExBlockedUnknown,
- Constants.ErrorCodes.CustomizationLockExBothUnknown,
- };
-
var firstIndexByRequest = requests.ToDictionary(request => request, request => default(int?));
var responseByRequest = requests.ToDictionary(request => request, request => (ExecuteMultipleResponseItem)null);
- var retryPolicy = Policy
- .Handle()
- .RetryForever(_ => this.WaitForSolutionHistoryRecordsToComplete())
- .Wrap(
- Policy
- .Handle()
- .WaitAndRetryForever(
- sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(30),
- onRetry: (_, timeSpan) => this.logger.LogInformation("A solution concurrency issue has occured. Waiting for {0} seconds before retrying.", timeSpan.TotalSeconds)));
-
- retryPolicy.Execute(() =>
+ this.CustomizationLockPolicy.Execute(() =>
{
var res = string.IsNullOrEmpty(username)
? this.ExecuteMultiple(requests, true, true, timeout)
@@ -384,24 +398,26 @@ public IEnumerable ExecuteMultipleSolutionHistoryOp
responseByRequest[request] = response;
}
- if (res.IsFaulted)
+ if (!res.IsFaulted)
{
- requests = res.Responses
- .Where(r => r.Fault != null)
- .Select(r => requests.ElementAt(r.RequestIndex))
- .ToList();
+ return;
+ }
- var solutionConcurrencyErrors = res.Responses.Where(r => r.Fault?.ErrorCode == Constants.ErrorCodes.SolutionConcurrencyFailure);
- if (solutionConcurrencyErrors.Any())
- {
- throw new SolutionConcurrencyException($"{solutionConcurrencyErrors.Count()} requests failed due to solution concurrency errors.");
- }
+ requests = res.Responses
+ .Where(r => r.Fault != null)
+ .Select(r => requests.ElementAt(r.RequestIndex))
+ .ToList();
- var customizationLockErrors = res.Responses.Where(r => r.Fault != null && customizationLockErrorCodes.Contains(r.Fault.ErrorCode));
- if (customizationLockErrors.Any())
- {
- throw new CustomizationLockException($"{customizationLockErrors.Count()} requests failed due to customization lock errors.");
- }
+ var solutionConcurrencyErrors = res.Responses.Where(r => r.Fault?.ErrorCode == Constants.ErrorCodes.SolutionConcurrencyFailure);
+ if (solutionConcurrencyErrors.Any())
+ {
+ throw new SolutionConcurrencyException($"{solutionConcurrencyErrors.Count()} requests failed due to solution concurrency errors.");
+ }
+
+ var customizationLockErrors = res.Responses.Where(r => r.Fault != null && CustomizationLockErrorCodes.Contains(r.Fault.ErrorCode));
+ if (customizationLockErrors.Any())
+ {
+ throw new CustomizationLockException($"{customizationLockErrors.Count()} requests failed due to customization lock errors.");
}
});
@@ -413,6 +429,53 @@ public IEnumerable ExecuteMultipleSolutionHistoryOp
return responseByRequest.Values;
}
+ ///
+ public IDictionary ExecuteManySolutionHistoryOperation(IEnumerable requests, string username, Action onError = null)
+ {
+ // Errors are expected and caught and handled. Uncaught errors are handled and logged via the onError callback.
+ var previousTraceLevel = TraceControlSettings.TraceLevel;
+ TraceControlSettings.TraceLevel = System.Diagnostics.SourceLevels.Off;
+
+ var results = requests.ToDictionary(r => r, r =>
+ {
+ try
+ {
+ return this.CustomizationLockPolicy.Execute(() =>
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(username))
+ {
+ return this.crmSvc.Execute(r);
+ }
+
+ return this.Execute(r, username, false, false);
+ }
+ catch (FaultException ex) when (ex.Detail.ErrorCode == Constants.ErrorCodes.SolutionConcurrencyFailure)
+ {
+ // Policy will handle this exception.
+ throw new SolutionConcurrencyException($"Request failed due to solution concurrency errors.");
+ }
+ catch (FaultException ex) when (CustomizationLockErrorCodes.Contains(ex.Detail.ErrorCode))
+ {
+ // Policy will handle this exception.
+ throw new CustomizationLockException($"Request failed due to customization lock errors.");
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ onError(r, ex);
+ }
+
+ return null;
+ });
+
+ TraceControlSettings.TraceLevel = previousTraceLevel;
+
+ return results;
+ }
+
///
public bool UpdateStateAndStatusForEntity(string entityLogicalName, Guid entityId, int statecode, int status) => this.crmSvc.UpdateStateAndStatusForEntity(entityLogicalName, entityId, statecode, status);
diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs
index 0b9b1df..9b0320b 100644
--- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs
+++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs
@@ -90,10 +90,11 @@ public interface ICrmServiceAdapter : IOrganizationService
/// The request.
/// The user to impersonate.
/// Whether to fallback to the authenticated user if the action fails as the specified user.
+ /// Whether to log errors.
/// The type of response.
/// The response.
/// Thrown when the specified user doesn't exist and fallback is disabled.
- public TResponse Execute(OrganizationRequest request, string username, bool fallbackToExistingUser = true)
+ public TResponse Execute(OrganizationRequest request, string username, bool fallbackToExistingUser = true, bool logErrors = true)
where TResponse : OrganizationResponse;
///
@@ -134,5 +135,14 @@ public TResponse Execute(OrganizationRequest request, string username
/// Timeout in seconds.
/// Returns an .
IEnumerable ExecuteMultipleSolutionHistoryOperation(IEnumerable requests, string username, int? timeout = null);
+
+ ///
+ /// Executes multiple requests individually and performs a check on the Solution History during the operation.
+ ///
+ /// The collection of to execute.
+ /// The user to impersonate.
+ /// An action to be called for each errored request.
+ /// A dictionary of responses keyed by request.
+ IDictionary ExecuteManySolutionHistoryOperation(IEnumerable requests, string username, Action onError = null);
}
}
diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs
index 9fffdbe..cc208e0 100644
--- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs
+++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs
@@ -107,9 +107,14 @@ public static class Fields
public const string Name = "name";
///
- /// The name of the process.
+ /// The state code.
///
public const string StateCode = "statecode";
+
+ ///
+ /// The status code.
+ ///
+ public const string StatusCode = "statuscode";
}
}
diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/ProcessDeploymentService.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/ProcessDeploymentService.cs
index 24254a1..6651532 100644
--- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/ProcessDeploymentService.cs
+++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/ProcessDeploymentService.cs
@@ -3,10 +3,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.ServiceModel;
using Capgemini.PowerApps.PackageDeployerTemplate.Adapters;
- using Microsoft.Crm.Sdk.Messages;
using Microsoft.Extensions.Logging;
using Microsoft.Xrm.Sdk;
+ using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
///
@@ -103,78 +104,95 @@ private void SetStates(IEnumerable processes, IEnumerable proces
this.logger.LogInformation($"Activating processes as {user}.");
}
- var requests = this.GetSetStateRequests(processes, processesToDeactivate);
+ var requests = this.GetRequestByProcess(processes, processesToDeactivate);
if (!requests.Any())
{
return;
}
- this.ExecuteSetStateRequests(requests, user);
+ this.ExecuteUpdateRequests(requests, user);
}
- private void ExecuteSetStateRequests(IEnumerable requests, string user = null)
+ private void ExecuteUpdateRequests(IDictionary requestsByProcess, string user = null)
{
- // Due to unpredictable process dependencies we should retry failed requests until there are zero successful responses.
- var remainingRequests = new List(requests);
- IEnumerable successfulResponses, failedResponses;
+ var nameMap = requestsByProcess.Keys
+ .ToDictionary(e => e.Id, e => e.GetAttributeValue(Constants.Workflow.Fields.Name));
+ var remainingRequests = requestsByProcess.Values.Where(r => r != null);
+ if (!remainingRequests.Any())
+ {
+ return;
+ }
+
+ this.logger.LogInformation($"Updating the states of {remainingRequests.Count()} processes.");
+
+ var iteration = 1;
+ var iterationSuccessfulRequestCount = 0;
+ var errorMessages = new List();
do
{
- var timeout = 120 + (remainingRequests.Count * 10);
- var executeMultipleResponses = this.crmSvc
- .ExecuteMultipleSolutionHistoryOperation(remainingRequests, user, timeout);
+ errorMessages = new List();
+ var responses = this.crmSvc.ExecuteManySolutionHistoryOperation(
+ remainingRequests,
+ user,
+ (r, ex) =>
+ {
+ errorMessages.Add($"Failed to update status of process {nameMap[((UpdateRequest)r).Target.Id]} with the following error: {((FaultException)ex).Detail.Message}");
+ });
+
+ remainingRequests = responses
+ .Where(kvp => kvp.Value is null)
+ .Select(kvp => kvp.Key)
+ .Cast();
- successfulResponses = executeMultipleResponses.Where(r => r.Fault == null);
- failedResponses = executeMultipleResponses.Except(successfulResponses);
- remainingRequests = failedResponses.Select(r => remainingRequests[r.RequestIndex]).ToList();
+ iterationSuccessfulRequestCount = responses.Values.Where(v => v != null).Count();
+ this.logger.LogInformation($"Successfully updated the state of {iterationSuccessfulRequestCount} processes in iteration {iteration}.");
+ iteration++;
}
- while (successfulResponses.Any() && remainingRequests.Any());
+ while (remainingRequests.Any() && iterationSuccessfulRequestCount > 0);
- if (remainingRequests.Any())
+ foreach (var errorMessage in errorMessages)
{
- foreach (var failedResponse in failedResponses)
- {
- var failedRequest = (SetStateRequest)remainingRequests[failedResponse.RequestIndex];
- this.logger.LogError($"Failed to set state for process {failedRequest.EntityMoniker.Name} with the following error: {failedResponse.Fault.Message}.");
- }
+ this.logger.LogError(errorMessage);
}
}
- private List GetSetStateRequests(IEnumerable processes, IEnumerable processesToDeactivate)
+ private IDictionary GetRequestByProcess(IEnumerable processes, IEnumerable processesToDeactivate)
{
- var requests = new List();
-
- foreach (var deployedProcess in processes)
- {
- var stateCode = new OptionSetValue(Constants.Workflow.StateCodeActive);
- var statusCode = new OptionSetValue(Constants.Workflow.StatusCodeActive);
-
- if (processesToDeactivate != null && processesToDeactivate.Contains(deployedProcess[Constants.Workflow.Fields.Name]))
+ return processes.ToDictionary(
+ p => p,
+ p =>
{
- stateCode.Value = Constants.Workflow.StateCodeInactive;
- statusCode.Value = Constants.Workflow.StatusCodeInactive;
- }
+ var stateCode = new OptionSetValue(Constants.Workflow.StateCodeActive);
+ var statusCode = new OptionSetValue(Constants.Workflow.StatusCodeActive);
- if (stateCode.Value == deployedProcess.GetAttributeValue(Constants.Workflow.Fields.StateCode).Value)
- {
- this.logger.LogInformation($"Process {deployedProcess[Constants.Workflow.Fields.Name]} already has desired state. Skipping.");
- continue;
- }
-
- this.logger.LogInformation($"Setting process status for {deployedProcess[Constants.Workflow.Fields.Name]} with statecode {stateCode.Value} and statuscode {statusCode.Value}");
+ if (processesToDeactivate != null && processesToDeactivate.Contains(p[Constants.Workflow.Fields.Name]))
+ {
+ stateCode.Value = Constants.Workflow.StateCodeInactive;
+ statusCode.Value = Constants.Workflow.StatusCodeInactive;
+ }
- // SetStateRequest is supposedly deprecated but UpdateRequest doesn't work for deactivating active flows
- requests.Add(
- new SetStateRequest
+ if (stateCode.Value == p.GetAttributeValue(Constants.Workflow.Fields.StateCode).Value)
{
- EntityMoniker = deployedProcess.ToEntityReference(),
- State = stateCode,
- Status = statusCode,
- });
- }
+ this.logger.LogInformation($"Process {p[Constants.Workflow.Fields.Name]} will be skipped. Already has desired state.");
+ return null;
+ }
- return requests;
+ this.logger.LogInformation($"Process {p[Constants.Workflow.Fields.Name]} will be {(stateCode.Value == Constants.Workflow.StateCodeActive ? "activated" : "deactivated")}.");
+
+ return new UpdateRequest
+ {
+ Target = new Entity(Constants.Workflow.LogicalName, p.Id)
+ {
+ Attributes =
+ {
+ [Constants.Workflow.Fields.StateCode] = stateCode,
+ [Constants.Workflow.Fields.StatusCode] = statusCode,
+ },
+ },
+ };
+ });
}
private EntityCollection RetrieveProcesses(IEnumerable names)
diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs
index 495c302..5872fd3 100644
--- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs
+++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs
@@ -79,7 +79,7 @@ public void ConnectConnectionReferences_WithConnectionOwner_UpdatesAsConnectionO
this.connectionReferenceSvc.ConnectConnectionReferences(connectionMap, connectionOwner);
- this.crmSvc.Verify(svc => svc.Execute(It.IsAny(), connectionOwner, true));
+ this.crmSvc.Verify(svc => svc.Execute(It.IsAny(), connectionOwner, It.IsAny(), It.IsAny()));
}
[Fact]
@@ -119,8 +119,12 @@ public void ConnectConnectionReferences_WithErrorUpdating_Continues()
private void MockUpdateConnectionReferencesResponse(ExecuteMultipleResponse response)
{
- this.crmSvc.Setup(svc => svc.Execute(It.IsAny())).Returns(response);
- this.crmSvc.Setup(svc => svc.Execute(It.IsAny(), It.IsAny(), true)).Returns(response);
+ this.crmSvc
+ .Setup(svc => svc.Execute(It.IsAny()))
+ .Returns(response);
+ this.crmSvc
+ .Setup(svc => svc.Execute(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(response);
}
private EntityCollection MockConnectionReferencesForConnectionMap(Dictionary connectionMap)
diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ProcessDeploymentServiceTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ProcessDeploymentServiceTests.cs
index d7c22bc..ac8a070 100644
--- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ProcessDeploymentServiceTests.cs
+++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ProcessDeploymentServiceTests.cs
@@ -4,10 +4,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+ using System.ServiceModel;
using Capgemini.PowerApps.PackageDeployerTemplate.Adapters;
using Capgemini.PowerApps.PackageDeployerTemplate.Services;
using FluentAssertions;
- using Microsoft.Crm.Sdk.Messages;
using Microsoft.Extensions.Logging;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
@@ -61,18 +61,18 @@ public void SetStatesBySolution_ProcessInComponentsToDeactivateList_DeactivatesP
{
var solutionProcesses = new List { GetProcess(Constants.Workflow.StateCodeActive) };
this.MockBySolutionProcesses(solutionProcesses);
- this.MockExecuteMultipleSolutionHistoryOperationResponse(
+ this.MockExecuteManySolutionHistoryOperationResponse(
null,
- svc => svc.ExecuteMultipleSolutionHistoryOperation(
+ svc => svc.ExecuteManySolutionHistoryOperation(
It.Is>(
- reqs => reqs.Cast().Any(
+ reqs => reqs.Cast().Any(
req =>
- req.EntityMoniker.LogicalName == Constants.Workflow.LogicalName &&
- req.EntityMoniker.Id == solutionProcesses.First().Id &&
- req.State.Value == Constants.Workflow.StateCodeInactive &&
- req.Status.Value == Constants.Workflow.StatusCodeInactive)),
+ req.Target.LogicalName == Constants.Workflow.LogicalName &&
+ req.Target.Id == solutionProcesses.First().Id &&
+ req.Target.GetAttributeValue(Constants.Workflow.Fields.StateCode).Value == Constants.Workflow.StateCodeInactive &&
+ req.Target.GetAttributeValue(Constants.Workflow.Fields.StatusCode).Value == Constants.Workflow.StatusCodeInactive)),
It.IsAny(),
- It.IsAny()),
+ It.IsAny>()),
true);
this.processDeploymentSvc.SetStatesBySolution(
@@ -94,12 +94,12 @@ public void SetStatesBySolution_WithUserParameter_ExecutesAsUser()
GetProcess(Constants.Workflow.StateCodeInactive),
};
this.MockBySolutionProcesses(solutionProcesses);
- this.MockExecuteMultipleSolutionHistoryOperationResponse(
+ this.MockExecuteManySolutionHistoryOperationResponse(
null,
- svc => svc.ExecuteMultipleSolutionHistoryOperation(
+ svc => svc.ExecuteManySolutionHistoryOperation(
It.IsAny>(),
userToImpersonate,
- It.IsAny()));
+ It.IsAny>()));
this.processDeploymentSvc.SetStatesBySolution(
Solutions, user: userToImpersonate);
@@ -162,7 +162,7 @@ public void SetStates_ProcessInComponentsToActivateFound_ActivatesProcess()
{
var foundProcesses = new List { GetProcess(Constants.Workflow.StateCodeInactive) };
this.MockSetStatesProcesses(foundProcesses);
- this.MockExecuteMultipleSolutionHistoryOperationResponse();
+ this.MockExecuteManySolutionHistoryOperationResponse();
this.processDeploymentSvc.SetStates(new List
{
@@ -177,18 +177,18 @@ public void SetStates_ProcessInComponentsToDeactivateFound_DectivatesProcess()
{
var foundProcesses = new List { GetProcess(Constants.Workflow.StateCodeActive) };
this.MockSetStatesProcesses(foundProcesses);
- this.MockExecuteMultipleSolutionHistoryOperationResponse(
+ this.MockExecuteManySolutionHistoryOperationResponse(
null,
- svc => svc.ExecuteMultipleSolutionHistoryOperation(
+ svc => svc.ExecuteManySolutionHistoryOperation(
It.Is>(
- reqs => reqs.Cast().Any(
+ reqs => reqs.Cast().Any(
req =>
- req.EntityMoniker.LogicalName == Constants.Workflow.LogicalName &&
- req.EntityMoniker.Id == foundProcesses.First().Id &&
- req.State.Value == Constants.Workflow.StateCodeInactive &&
- req.Status.Value == Constants.Workflow.StatusCodeInactive)),
+ req.Target.LogicalName == Constants.Workflow.LogicalName &&
+ req.Target.Id == foundProcesses.First().Id &&
+ req.Target.GetAttributeValue(Constants.Workflow.Fields.StateCode).Value == Constants.Workflow.StateCodeInactive &&
+ req.Target.GetAttributeValue(Constants.Workflow.Fields.StatusCode).Value == Constants.Workflow.StatusCodeInactive)),
It.IsAny(),
- It.IsAny()),
+ It.IsAny>()),
true);
this.processDeploymentSvc.SetStates(Enumerable.Empty(), new List
@@ -205,12 +205,12 @@ public void SetStates_WithUserParameter_ExecutesAsUser()
var foundProcesses = new List { GetProcess(Constants.Workflow.StateCodeInactive) };
this.MockSetStatesProcesses(foundProcesses);
var userToImpersonate = "licenseduser@domaincom";
- this.MockExecuteMultipleSolutionHistoryOperationResponse(
+ this.MockExecuteManySolutionHistoryOperationResponse(
null,
- svc => svc.ExecuteMultipleSolutionHistoryOperation(
+ svc => svc.ExecuteManySolutionHistoryOperation(
It.IsAny>(),
userToImpersonate,
- It.IsAny()),
+ It.IsAny>()),
true);
this.processDeploymentSvc.SetStates(
@@ -230,16 +230,21 @@ public void SetStates_WithError_LogsError()
var foundProcesses = new List { GetProcess(Constants.Workflow.StateCodeInactive) };
this.MockSetStatesProcesses(foundProcesses);
var fault = new OrganizationServiceFault { Message = "Some error." };
- var response = new ExecuteMultipleResponse
- {
- Results = new ParameterCollection
- {
- { "Responses", new ExecuteMultipleResponseItemCollection() },
- { "IsFaulted", true },
- },
- };
- response.Responses.Add(new ExecuteMultipleResponseItem { Fault = fault });
- this.MockExecuteMultipleSolutionHistoryOperationResponse(response.Responses);
+ this.crmServiceAdapterMock
+ .Setup(svc => svc.ExecuteManySolutionHistoryOperation(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny>()))
+ .Callback, string, Action>(
+ (requests, username, onError) =>
+ {
+ foreach (var request in requests)
+ {
+ onError(request, new FaultException(fault));
+ }
+ })
+ .Returns, string, Action>(
+ (requests, username, onError) => requests.ToDictionary(r => r, r => (OrganizationResponse)null));
this.processDeploymentSvc.SetStates(
new List
@@ -292,33 +297,27 @@ private void MockBySolutionProcesses(IList processes)
.Returns(new EntityCollection(processes));
}
- private void MockExecuteMultipleSolutionHistoryOperationResponse(
- IEnumerable responses = null,
- Expression>> expression = null,
+ private void MockExecuteManySolutionHistoryOperationResponse(
+ IDictionary response = null,
+ Expression>> expression = null,
bool verifiable = false)
{
if (expression == null)
{
- expression = svc => svc.ExecuteMultipleSolutionHistoryOperation(
+ expression = svc => svc.ExecuteManySolutionHistoryOperation(
It.IsAny>(),
It.IsAny(),
- It.IsAny());
+ It.IsAny>());
}
- if (responses == null)
+ if (response == null)
{
- var executeMultipleResponse = new ExecuteMultipleResponse();
- executeMultipleResponse.Results["Responses"] = new ExecuteMultipleResponseItemCollection()
- {
- new ExecuteMultipleResponseItem() { RequestIndex = 0 },
- new ExecuteMultipleResponseItem() { RequestIndex = 1 },
- };
- responses = executeMultipleResponse.Responses;
+ response = new Dictionary();
}
var returnResult = this.crmServiceAdapterMock
.Setup(expression)
- .Returns(responses);
+ .Returns(response);
if (verifiable)
{