//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ResourceProvider.Core
{
///
/// Utils class supporting parallel querying of Azure resources
///
public static class AzureUtil
{
///
/// Execute an async action for each input in the a list of input in parallel.
/// If any task fails, adds the exeption message to the response errors
/// If cancellation token is set to cancel, returns empty response.
/// Note: Will not throw if errors occur. Instead the caller should check for errors and either notify
/// or rethrow as needed.
///
/// Resource management session to use to call the resource manager
/// List of inputs
/// optional lookup key to filter the result
/// Cancellation token
/// Async action
/// ServiceResponse including the list of data and errors
public static async Task> ExecuteGetAzureResourceAsParallel(
TConfig config,
IEnumerable inputs,
string lookupKey,
CancellationToken cancellationToken,
Func>> asyncAction
)
{
List mergedResult = new List();
List mergedErrors = new List();
try
{
if (inputs == null)
{
return new ServiceResponse(mergedResult);
}
List inputList = inputs.ToList();
ServiceResponse[] resultList = new ServiceResponse[inputList.Count];
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var tasks = Enumerable.Range(0, inputList.Count())
.Select(async i =>
{
ServiceResponse result = await GetResult(config, inputList[i], lookupKey, cancellationToken,
cancellationTokenSource.Token, asyncAction);
//server name is used to filter the result and if the data is already found not, we need to cancel the other tasks
if (!string.IsNullOrEmpty(lookupKey) && result.Found)
{
cancellationTokenSource.Cancel();
}
resultList[i] = result;
return result;
}
);
await Task.WhenAll(tasks);
if (!cancellationToken.IsCancellationRequested)
{
foreach (ServiceResponse resultForEachInput in resultList)
{
mergedResult.AddRange(resultForEachInput.Data);
mergedErrors.AddRange(resultForEachInput.Errors);
}
}
}
catch (Exception ex)
{
mergedErrors.Add(ex);
return new ServiceResponse(mergedResult, mergedErrors);
}
return new ServiceResponse(mergedResult, mergedErrors);
}
private static async Task> GetResult(
TConfig config,
TInput input,
string lookupKey,
CancellationToken cancellationToken,
CancellationToken internalCancellationToken,
Func>> asyncAction
)
{
if (cancellationToken.IsCancellationRequested || internalCancellationToken.IsCancellationRequested)
{
return new ServiceResponse();
}
try
{
return await asyncAction(config, input, lookupKey, cancellationToken, internalCancellationToken);
}
catch (Exception ex)
{
return new ServiceResponse(ex);
}
}
}
}