WebApiService
using Polly;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer
{
internal class WebApiService : IDisposable
{
private bool initialized;
private bool disposed;
public RelativityConnectionInfo ConnectionInfo { get; }
public int MaxRetryAttempts { get; set; }
public double TimeoutSeconds { get; set; }
protected ClientConfiguration Configuration { get; }
protected ITransferLog Log { get; }
protected FileIO ServiceInstance { get; set; }
protected IUserManagerService UserManagerService { get; }
public WebApiService(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, IUserManagerService userManagerService, ITransferLog log)
: this(connectionInfo, configuration, userManagerService, log, configuration.MaxHttpRetryAttempts, configuration.HttpTimeoutSeconds)
{
}
public WebApiService(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, IUserManagerService userManagerService, ITransferLog log, int maxRetryAttempts, double timeoutSeconds)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (userManagerService == null)
throw new ArgumentNullException("userManagerService");
if (log == null)
throw new ArgumentNullException("log");
Configuration = configuration;
ConnectionInfo = connectionInfo;
UserManagerService = userManagerService;
ServiceInstance = null;
Log = log;
initialized = false;
MaxRetryAttempts = maxRetryAttempts;
TimeoutSeconds = timeoutSeconds;
}
~WebApiService()
{
Dispose(false);
}
public static Uri GetWebApiServiceUrl(RelativityConnectionInfo connectionInfo, ITransferLog log)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
if (connectionInfo.WebApiServiceUrl != (Uri)null)
return connectionInfo.WebApiServiceUrl;
connectionInfo.WebApiServiceUrl = UrlHelper.Combine(connectionInfo.Host, "RelativityWebAPI");
log.LogWarning("The WebApiServiceUrl wasn't assigned. Manually creating the {Url} URL to prevent failure.", connectionInfo.WebApiServiceUrl);
return connectionInfo.WebApiServiceUrl;
}
public static IHttpCredential GetWebApiHttpCredential(RelativityConnectionInfo connectionInfo)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
return connectionInfo.WebApiServiceCredential ?? connectionInfo.Credential;
}
public Task<string> (int workspaceId, CancellationToken token)
{
if (workspaceId < 1 && workspaceId != -1)
throw new ArgumentOutOfRangeException("workspaceId");
Initialize();
return RetrySyntaxAsync.WaitAndRetryAsync(Policy.Handle<Exception>(), MaxRetryAttempts, (Func<int, TimeSpan>)((int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt))), (Action<Exception, TimeSpan>)delegate(Exception exception, TimeSpan span) {
Log.LogError(exception, $"""{span}", Array.Empty<object>());
CheckLogin(exception);
}).ExecuteAsync<string>((Func<CancellationToken, Task<string>>)delegate {
Task<string> task = Task.Factory.FromAsync((Func<AsyncCallback, object, IAsyncResult>)((AsyncCallback callback, object stateObject) => ServiceInstance.BeginGetBcpSharePath(workspaceId, callback, stateObject)), (Func<IAsyncResult, string>)ServiceInstance.EndGetBcpSharePath, (object)null);
task.Wait(token);
return task;
}, token);
}
public Task<BcpStorageReport> (int workspaceId, CancellationToken token)
{
if (workspaceId < 1 && workspaceId != -1)
throw new ArgumentOutOfRangeException("workspaceId");
Initialize();
return RetrySyntaxAsync.WaitAndRetryAsync(Policy.Handle<Exception>(), MaxRetryAttempts, (Func<int, TimeSpan>)((int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt))), (Action<Exception, TimeSpan>)delegate(Exception exception, TimeSpan span) {
Log.LogError(exception, $"""{span}", Array.Empty<object>());
CheckLogin(exception);
}).ExecuteAsync<BcpStorageReport>((Func<CancellationToken, Task<BcpStorageReport>>)async delegate {
Dictionary<string, string> dictionary = (await Task.Factory.FromAsync((Func<AsyncCallback, object, IAsyncResult>)((AsyncCallback callback, object stateObject) => ServiceInstance.BeginGetBcpShareSpaceReport(workspaceId, callback, stateObject)), (Func<IAsyncResult, string[][]>)ServiceInstance.EndGetBcpShareSpaceReport, (object)null).ConfigureAwait(false)).ToDictionary((string[] line) => line[0], (string[] line) => line[1]);
BcpStorageReport bcpStorageReport = new BcpStorageReport();
if (dictionary.ContainsKey("Drive Name"))
bcpStorageReport.DriveName = dictionary["Drive Name"];
if (dictionary.ContainsKey("Free Space"))
bcpStorageReport.TotalAvailableBytes = TryGetStorageReportBytes(dictionary["Free Space"]);
if (dictionary.ContainsKey("Used Space"))
bcpStorageReport.TotalUsedBytes = TryGetStorageReportBytes(dictionary["Used Space"]);
if (dictionary.ContainsKey("Total Space"))
bcpStorageReport.TotalBytes = TryGetStorageReportBytes(dictionary["Total Space"]);
return bcpStorageReport;
}, token);
}
public Task<bool> (int workspaceId, CancellationToken token)
{
if (workspaceId < 1 && workspaceId != -1)
throw new ArgumentOutOfRangeException("workspaceId");
Initialize();
return RetrySyntaxAsync.WaitAndRetryAsync(Policy.Handle<Exception>(), MaxRetryAttempts, (Func<int, TimeSpan>)((int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt))), (Action<Exception, TimeSpan>)delegate(Exception exception, TimeSpan span) {
Log.LogError(exception, $"""{span}", Array.Empty<object>());
CheckLogin(exception);
}).ExecuteAsync<bool>((Func<CancellationToken, Task<bool>>)(async (CancellationToken ct) => await Task.Factory.FromAsync((Func<AsyncCallback, object, IAsyncResult>)((AsyncCallback callback, object stateObject) => ServiceInstance.BeginValidateBcpShare(workspaceId, callback, stateObject)), (Func<IAsyncResult, bool>)ServiceInstance.EndValidateBcpShare, (object)null).ConfigureAwait(false)), token);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void CheckLogin(Exception exception)
{
List<string> source = new List<string> {
exception.ToString(),
exception.InnerException?.ToString() ?? string.Empty
};
bool flag = source.Any((string x) => x.IndexOf("kcuraaccessdeniedmarker", StringComparison.OrdinalIgnoreCase) != -1);
bool flag2 = source.Any((string x) => x.IndexOf("NeedToReLoginException", StringComparison.OrdinalIgnoreCase) != -1);
if (flag) {
Log.LogInformation("An access denied exception occurred and requesting a new distributed login token.", Array.Empty<object>());
UserManagerService.Login();
} else if (flag2) {
Log.LogInformation(string.IsNullOrWhiteSpace(UserManagerService.DistributedToken) ? "The distributed login token is null or empty and requesting a distributed login token." : "The distributed login token has expired and requesting a new distributed login token.", Array.Empty<object>());
UserManagerService.Login();
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposed) {
if (disposing && ServiceInstance != null)
((Component)ServiceInstance).Dispose();
disposed = true;
}
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed via dispose pattern.")]
protected virtual void Initialize()
{
if (!initialized) {
FileIO fileIO = new FileIO();
fileIO.set_Credentials(GetWebApiHttpCredential(ConnectionInfo).CreateCredentials());
fileIO.set_CookieContainer(Configuration.CookieContainer);
fileIO.set_Timeout((int)TimeSpan.FromSeconds(TimeoutSeconds).TotalMilliseconds);
fileIO.set_Url(UrlHelper.Combine(GetWebApiServiceUrl(ConnectionInfo, Log), "FileIO.asmx").ToString());
ServiceInstance = fileIO;
ServicePointManager.SecurityProtocol = (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12);
initialized = true;
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is done by design.")]
private long TryGetStorageReportBytes(string value)
{
try {
return Convert.ToInt64(ByteSize.FromMegaBytes(Convert.ToDouble(value.Replace(" MB", string.Empty))).Bytes);
} catch (Exception exception) {
Log.LogError(exception, "Failed to convert the {BcpValue} BCP storage report value to a long data type.", value);
return 0;
}
}
}
}