diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs index 53ba943..53738b3 100644 --- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/CrmServiceAdapter.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; + using Capgemini.PowerApps.PackageDeployerTemplate.Exceptions; using Microsoft.Extensions.Logging; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Messages; using Microsoft.Xrm.Sdk.Query; using Microsoft.Xrm.Tooling.Connector; + using Polly; /// /// An extended built on . @@ -313,6 +315,72 @@ public TResponse Execute(OrganizationRequest request, string username return (TResponse)this.Execute(request); } + /// + public bool HasStartedSolutionHistoryRecords(ILogger logger) + { + var retryPolicy = Policy + .HandleResult(hasActiveRecords => !hasActiveRecords) + .WaitAndRetryForever( + sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(20), + onRetry: (result, timeSpan) => + { + logger.LogInformation($"Active records still present within Solution History. Waiting for {timeSpan.TotalSeconds} seconds before retrying..."); + }); + + var result = retryPolicy.Execute(() => + { + var entityCollection = this.crmSvc.RetrieveMultiple(new QueryExpression() + { + EntityName = Constants.SolutionHistory.LogicalName, + ColumnSet = new ColumnSet( + Constants.SolutionHistory.Fields.SolutionHistoryId, + Constants.SolutionHistory.Fields.Name, + Constants.SolutionHistory.Fields.Status), + Criteria = + { + Conditions = + { + new ConditionExpression(Constants.SolutionHistory.Fields.Status, ConditionOperator.Equal, Constants.SolutionHistory.Statuses.Started), + }, + }, + }); + return entityCollection.TotalRecordCount > 0; + }); + + return result; + } + + /// + public ExecuteMultipleResponse ExecuteMultipleSolutionHistoryOperation(IEnumerable requests, string username, ILogger logger, int? timeout = null) + { + ExecuteMultipleResponse executeMultipleRes = null; + + var retryPolicy = Policy + .Handle() + .WaitAndRetryForever( + sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(10), + onRetry: (ex, timeSpan) => + { + this.HasStartedSolutionHistoryRecords(logger); + }); + + retryPolicy.Execute(() => + { + executeMultipleRes = string.IsNullOrEmpty(username) ? + this.ExecuteMultiple(requests, true, true, timeout) : this.ExecuteMultiple(requests, username, true, true, timeout); + var failedResponses = executeMultipleRes.Responses + .Where(r => r.Fault.ErrorCode == Constants.ErrorCodes.CustomizationLockExBlockedUnknown) + .ToList(); + + if (failedResponses.Any()) + { + throw new SolutionHistoryOperationException($"{failedResponses.Count} requests failed due to the error code {Constants.ErrorCodes.CustomizationLockExBlockedUnknown}"); + } + }); + + return executeMultipleRes; + } + /// 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 9177aea..2a634c7 100644 --- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Adapters/ICrmServiceAdapter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; + using Microsoft.Extensions.Logging; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Messages; using Microsoft.Xrm.Sdk.Query; @@ -120,5 +121,22 @@ public TResponse Execute(OrganizationRequest request, string username /// The unique name of the solution. /// EntityTypeCode for a given entity type. string GetEntityTypeCode(string entityLogicalName); + + /// + /// Checks whether there are any Solution History records in a Started state. + /// + /// Instane of ILogger. + /// Returns true if active records are present in the Solution History; otherwise, returns false. + bool HasStartedSolutionHistoryRecords(ILogger logger); + + /// + /// Executes multiple requests and performs a check on the Solution History during the operation. + /// + /// The collection of to execute. + /// The user to impersonate. + /// The instance. + /// Timeout in seconds. + /// Returns an . + ExecuteMultipleResponse ExecuteMultipleSolutionHistoryOperation(IEnumerable requests, string username, ILogger logger, int? timeout = null); } }