Skip to content

Commit

Permalink
feat: extend interface with solution history check
Browse files Browse the repository at this point in the history
- contains retry logic to defer the processing of requests until all solution history records are completed

#89
  • Loading branch information
iainlomax81 committed Oct 23, 2024
1 parent c9cf157 commit d11fee6
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// An extended <see cref="IOrganizationService"/> built on <see cref="CrmServiceClient"/>.
Expand Down Expand Up @@ -313,6 +315,72 @@ public TResponse Execute<TResponse>(OrganizationRequest request, string username
return (TResponse)this.Execute(request);
}

/// <inheritdoc/>
public bool HasStartedSolutionHistoryRecords(ILogger logger)
{
var retryPolicy = Policy
.HandleResult<bool>(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;
}

/// <inheritdoc/>
public ExecuteMultipleResponse ExecuteMultipleSolutionHistoryOperation(IEnumerable<OrganizationRequest> requests, string username, ILogger logger, int? timeout = null)
{
ExecuteMultipleResponse executeMultipleRes = null;

var retryPolicy = Policy
.Handle<SolutionHistoryOperationException>()
.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;
}

/// <inheritdoc/>
public bool UpdateStateAndStatusForEntity(string entityLogicalName, Guid entityId, int statecode, int status) => this.crmSvc.UpdateStateAndStatusForEntity(entityLogicalName, entityId, statecode, status);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -120,5 +121,22 @@ public TResponse Execute<TResponse>(OrganizationRequest request, string username
/// <param name="entityLogicalName">The unique name of the solution.</param>
/// <returns> EntityTypeCode for a given entity type.</returns>
string GetEntityTypeCode(string entityLogicalName);

/// <summary>
/// Checks whether there are any Solution History records in a <b>Started</b> state.
/// </summary>
/// <param name="logger">Instane of ILogger.</param>
/// <returns>Returns <c>true</c> if active records are present in the Solution History; otherwise, returns <c>false</c>.</returns>
bool HasStartedSolutionHistoryRecords(ILogger logger);

/// <summary>
/// Executes multiple requests and performs a check on the Solution History during the operation.
/// </summary>
/// <param name="requests">The collection of <see cref="OrganizationRequest"/> to execute.</param>
/// <param name="username">The user to impersonate.</param>
/// <param name="logger">The <see cref="ILogger"/> instance.</param>
/// <param name="timeout">Timeout in seconds.</param>
/// <returns>Returns an <see cref="ExecuteMultipleResponse"/>. </returns>
ExecuteMultipleResponse ExecuteMultipleSolutionHistoryOperation(IEnumerable<OrganizationRequest> requests, string username, ILogger logger, int? timeout = null);
}
}

0 comments on commit d11fee6

Please sign in to comment.