Schedule Migration Integration
This commit is contained in:
Родитель
14d92553f6
Коммит
6f3efe6895
|
@ -32,7 +32,10 @@ internal class CustomerProvider : ICustomerProvider
|
|||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
Console.WriteLine("Token generated...");
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
|
@ -52,7 +55,7 @@ internal class CustomerProvider : ICustomerProvider
|
|||
route = $"{Routes.GetCustomers}&seekOperation=next";
|
||||
}
|
||||
|
||||
request.RequestUri = new Uri(route);
|
||||
request.RequestUri = new Uri(route, UriKind.Relative);
|
||||
|
||||
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||
if (response.StatusCode == HttpStatusCode.BadRequest)
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="INewCommerceMigrationScheduleProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace NCEBulkMigrationTool
|
||||
{
|
||||
internal interface INewCommerceMigrationScheduleProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports legacy commerce subscriptions with their migration eligibility to an output CSV file so that
|
||||
/// they can be scheduled as needed.
|
||||
/// </summary>
|
||||
/// <returns>Bool indicating success/ failure.</returns>
|
||||
Task<bool> ValidateAndGetSubscriptionsToScheduleMigrationAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Uploads New Commerce Migration Schedules based on CSV files in the input folders and writes the schedule migration data to a new CSV file.
|
||||
/// </summary>
|
||||
/// <returns>Bool indicating success/ failure.</returns>
|
||||
Task<bool> UploadNewCommerceMigrationSchedulesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Exports all the New Commerce Migration Schedules for the given input list of customers.
|
||||
/// </summary>
|
||||
/// <returns>Bool indicating success/ failure.</returns>
|
||||
Task<bool> ExportNewCommerceMigrationSchedulesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the New Commerce Migration Schedules based on the CSV input files and writes the output with the updated Schedule Migration Status.
|
||||
/// </summary>
|
||||
/// <returns>Bool indicating success/ failure.</returns>
|
||||
Task<bool> CancelNewCommerceMigrationSchedulesAsync();
|
||||
}
|
||||
}
|
|
@ -85,6 +85,8 @@ internal record MigrationRequest
|
|||
|
||||
public int SeatCount { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public bool AddOn { get; init; }
|
||||
|
||||
public string BaseSubscriptionId { get; init; } = string.Empty;
|
||||
|
@ -158,6 +160,8 @@ public record NewCommerceMigration
|
|||
|
||||
public bool PurchaseFullTerm { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public string ExternalReferenceId { get; set; } = string.Empty;
|
||||
|
||||
public IEnumerable<NewCommerceMigration> AddOnMigrations { get; set; } = Enumerable.Empty<NewCommerceMigration>();
|
||||
|
@ -200,6 +204,8 @@ public record MigrationResult
|
|||
|
||||
public int NCESeatCount { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public int? ErrorCode { get; set; } = null;
|
||||
|
||||
public string ErrorReason { get; init; } = string.Empty;
|
||||
|
@ -223,6 +229,8 @@ public record NewCommerceEligibility
|
|||
|
||||
public string CatalogItemId { get; set; } = string.Empty;
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public IEnumerable<NewCommerceEligibilityError> Errors { get; set; } = Enumerable.Empty<NewCommerceEligibilityError>();
|
||||
|
||||
public IEnumerable<NewCommerceEligibility> AddOnMigrations { get; set; } = Enumerable.Empty<NewCommerceEligibility>();
|
||||
|
@ -242,6 +250,145 @@ public record CustomerErrorResponse
|
|||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public record NewCommerceMigrationSchedule
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
public string CurrentSubscriptionId { get; set; } = string.Empty;
|
||||
|
||||
public string Status { get; set; } = string.Empty;
|
||||
|
||||
public string CustomerTenantId { get; set; } = string.Empty;
|
||||
|
||||
public string CatalogItemId { get; set; } = string.Empty;
|
||||
|
||||
public DateTime? SubscriptionEndDate { get; set; }
|
||||
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public string TermDuration { get; set; } = string.Empty;
|
||||
|
||||
public string BillingCycle { get; set; } = string.Empty;
|
||||
|
||||
public bool PurchaseFullTerm { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public DateTime? TargetDate { get; set; }
|
||||
|
||||
public bool? MigrateOnRenewal { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedTime { get; set; }
|
||||
|
||||
public DateTimeOffset LastModifiedTime { get; set; }
|
||||
|
||||
public string ExternalReferenceId { get; set; } = string.Empty;
|
||||
|
||||
public string NewCommerceMigrationId { get; set; } = string.Empty;
|
||||
|
||||
public string ErrorDescription { get; set; } = string.Empty;
|
||||
|
||||
public int? ErrorCode { get; set; }
|
||||
|
||||
public IEnumerable<NewCommerceMigrationSchedule> AddOnMigrationSchedules { get; set; } = Enumerable.Empty<NewCommerceMigrationSchedule>();
|
||||
}
|
||||
|
||||
internal record ScheduleMigrationRequest
|
||||
{
|
||||
public string PartnerTenantId { get; init; } = string.Empty;
|
||||
|
||||
public string IndirectResellerMpnId { get; init; } = string.Empty;
|
||||
|
||||
public string CustomerName { get; init; } = string.Empty;
|
||||
|
||||
public Guid CustomerTenantId { get; init; }
|
||||
|
||||
public string LegacySubscriptionId { get; init; } = string.Empty;
|
||||
|
||||
public string LegacySubscriptionName { get; init; } = string.Empty;
|
||||
|
||||
public string LegacyProductName { get; init; } = string.Empty;
|
||||
|
||||
public DateTime ExpirationDate { get; init; }
|
||||
|
||||
public bool AutoRenewEnabled { get; init; }
|
||||
|
||||
public bool AddOn { get; init; }
|
||||
|
||||
public string BaseSubscriptionId { get; init; } = string.Empty;
|
||||
|
||||
public bool MigrationEligible { get; set; }
|
||||
|
||||
public string NcePsa { get; set; } = string.Empty;
|
||||
|
||||
public string CurrentTerm { get; init; } = string.Empty;
|
||||
|
||||
public string CurrentBillingPlan { get; init; } = string.Empty;
|
||||
|
||||
public int CurrentSeatCount { get; set; }
|
||||
|
||||
public bool StartNewTermInNce { get; init; }
|
||||
|
||||
public string Term { get; init; } = string.Empty;
|
||||
|
||||
public string BillingPlan { get; init; } = string.Empty;
|
||||
|
||||
public int SeatCount { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public DateTime? TargetDate { get; set; }
|
||||
|
||||
public bool? MigrateOnRenewal { get; set; }
|
||||
|
||||
public string MigrationIneligibilityReason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public record ScheduleMigrationResult
|
||||
{
|
||||
public string PartnerTenantId { get; init; } = string.Empty;
|
||||
|
||||
public string IndirectResellerMpnId { get; init; } = string.Empty;
|
||||
|
||||
public string CustomerName { get; init; } = string.Empty;
|
||||
|
||||
public Guid CustomerTenantId { get; init; }
|
||||
|
||||
public string LegacySubscriptionId { get; init; } = string.Empty;
|
||||
|
||||
public string LegacySubscriptionName { get; init; } = string.Empty;
|
||||
|
||||
public string LegacyProductName { get; init; } = string.Empty;
|
||||
|
||||
public DateTime ExpirationDate { get; init; }
|
||||
|
||||
public bool AddOn { get; init; }
|
||||
|
||||
public bool StartedNewTermInNce { get; init; }
|
||||
|
||||
public string NCETermDuration { get; init; } = string.Empty;
|
||||
|
||||
public string NCEBillingPlan { get; init; } = string.Empty;
|
||||
|
||||
public int NCESeatCount { get; set; }
|
||||
|
||||
public DateTime? CustomTermEndDate { get; set; }
|
||||
|
||||
public DateTime? TargetDate { get; set; }
|
||||
|
||||
public bool? MigrateOnRenewal { get; set; }
|
||||
|
||||
public string MigrationScheduleStatus { get; init; } = string.Empty;
|
||||
|
||||
public string MigrationScheduleId { get; init; } = string.Empty;
|
||||
|
||||
public string BatchId { get; init; } = string.Empty;
|
||||
|
||||
public int? ErrorCode { get; set; } = null;
|
||||
|
||||
public string ErrorReason { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public enum NewCommerceEligibilityErrorCode
|
||||
{
|
||||
SubscriptionStatusNotActive = 0,
|
||||
|
@ -261,6 +408,14 @@ public enum NewCommerceEligibilityErrorCode
|
|||
SubscriptionActiveForLessThanOneMonth = 7,
|
||||
|
||||
TradeStatusNotAllowed = 8,
|
||||
|
||||
InvalidPartnerIdOnRecord = 9,
|
||||
|
||||
MigrationsWithinLast24hOfTermOnlyAllowedForFullTerm = 10,
|
||||
|
||||
InvalidCustomTermEndDate = 11,
|
||||
|
||||
CustomTermEndDateMustAlignWithExistingSubscriptionOrCalendarMonth = 12,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
|
@ -277,6 +432,8 @@ internal enum SubscriptionStatus
|
|||
Expired = 4,
|
||||
|
||||
Pending = 5,
|
||||
|
||||
Disabled = 6,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
|
@ -310,28 +467,58 @@ internal record Constants
|
|||
|
||||
internal record Routes
|
||||
{
|
||||
/// <summary>
|
||||
/// Base url for the partner center Apis.
|
||||
/// </summary>
|
||||
public const string BaseUrl = "https://api.partnercenter.microsoft.com";
|
||||
|
||||
/// <summary>
|
||||
/// Get customers route.
|
||||
/// </summary>
|
||||
public const string GetCustomers = "https://api.partnercenter.microsoft.com/v1/customers?size=500";
|
||||
public const string GetCustomers = "/v1/customers?size=500";
|
||||
|
||||
/// <summary>
|
||||
/// Get subscriptions route, 0 represents customerId.
|
||||
/// </summary>
|
||||
public const string GetSubscriptions = "https://api.partnercenter.microsoft.com/v1/customers/{0}/subscriptions";
|
||||
public const string GetSubscriptions = "/v1/customers/{0}/subscriptions";
|
||||
|
||||
/// <summary>
|
||||
/// Get new commerce migrations route, 0 represents customerId, 1 represents newCommerceMigrationId.
|
||||
/// </summary>
|
||||
public const string GetNewCommerceMigration = "https://api.partnercenter.microsoft.com/v1/customers/{0}/migrations/newcommerce/{1}";
|
||||
public const string GetNewCommerceMigration = "/v1/customers/{0}/migrations/newcommerce/{1}";
|
||||
|
||||
/// <summary>
|
||||
/// Validate migration eligibility route, 0 represents customerId.
|
||||
/// </summary>
|
||||
public const string ValidateMigrationEligibility = "https://api.partnercenter.microsoft.com/v1/customers/{0}/migrations/newcommerce/validate";
|
||||
public const string ValidateMigrationEligibility = "/v1/customers/{0}/migrations/newcommerce/validate";
|
||||
|
||||
/// <summary>
|
||||
/// Post new commerce migration, 0 represents customerId.
|
||||
/// </summary>
|
||||
public const string PostNewCommerceMigration = "https://api.partnercenter.microsoft.com/v1/customers/{0}/migrations/newcommerce";
|
||||
public static string PostNewCommerceMigration = "/v1/customers/{0}/migrations/newcommerce";
|
||||
|
||||
/// <summary>
|
||||
/// Get new commerce migrations route, 0 represents customerId, 1 represents newCommerceMigrationScheduleId.
|
||||
/// </summary>
|
||||
public const string GetNewCommerceMigrationSchedule = "/v1/customers/{0}/migrations/newcommerce/schedules/{1}";
|
||||
|
||||
/// <summary>
|
||||
/// Post new commerce migration schedule, 0 represents customerId.
|
||||
/// </summary>
|
||||
public const string PostNewCommerceMigrationSchedule = "/v1/customers/{0}/migrations/newcommerce/schedules";
|
||||
|
||||
/// <summary>
|
||||
/// Update new commerce migration schedule, 0 represents customerId, 1 represents newCommerceMigrationScheduleId.
|
||||
/// </summary>
|
||||
public const string UpdateNewCommerceMigrationSchedule = "/v1/customers/{0}/migrations/newcommerce/schedules/{1}";
|
||||
|
||||
/// <summary>
|
||||
/// Get new commerce migration schedules route.
|
||||
/// </summary>
|
||||
public const string GetNewCommerceMigrationSchedules = "/v1/migrations/newcommerce/schedules";
|
||||
|
||||
/// <summary>
|
||||
/// Cancel new commerce migration schedule, 0 represents customerId, 1 represents newCommerceMigrationScheduleId.
|
||||
/// </summary>
|
||||
public const string CancelNewCommerceMigrationSchedule = "/v1/customers/{0}/migrations/newcommerce/schedules/{1}/cancel";
|
||||
}
|
|
@ -46,7 +46,10 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
|
||||
var migrations = new ConcurrentBag<IEnumerable<MigrationResult>>();
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
|
@ -87,10 +90,10 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
|
||||
Console.WriteLine("Exporting migrations");
|
||||
await csvProvider.ExportCsv(migrations.SelectMany(m => m), $"{Constants.OutputFolderPath}/migrations/{processedFileName}_{batchId}.csv");
|
||||
|
||||
|
||||
File.Move(fileName, $"{Constants.InputFolderPath}/subscriptions/processed/{processedFileName}", true);
|
||||
|
||||
await Task.Delay(1000 * 60);
|
||||
await Task.Delay(1000);
|
||||
|
||||
Console.WriteLine($"Exported migrations at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/migrations/{processedFileName}_{batchId}.csv");
|
||||
}
|
||||
|
@ -115,7 +118,10 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
|
||||
var migrations = new ConcurrentBag<MigrationResult>();
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
|
||||
var options = new ParallelOptions()
|
||||
|
@ -173,7 +179,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
|
||||
File.Move(fileName, $"{Constants.InputFolderPath}/migrations/processed/{processedFileName}", true);
|
||||
|
||||
await Task.Delay(1000 * 60);
|
||||
await Task.Delay(1000);
|
||||
|
||||
Console.WriteLine($"Exported migration status at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/migrationstatus/{processedFileName}.csv");
|
||||
}
|
||||
|
@ -226,7 +232,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
}
|
||||
|
||||
var result = this.PrepareMigrationResult(migrationResult, migrationResult.BatchId, migration, migrationError);
|
||||
return (result!, migration?.AddOnMigrations ?? Enumerable.Empty<NewCommerceMigration>());
|
||||
return (result, migration?.AddOnMigrations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -251,6 +257,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
BillingCycle = migrationRequest.BillingPlan,
|
||||
TermDuration = migrationRequest.Term,
|
||||
ExternalReferenceId = batchId,
|
||||
CustomTermEndDate = migrationRequest.CustomTermEndDate,
|
||||
};
|
||||
|
||||
// If they want to start a new term, then we should take the input from the file.
|
||||
|
@ -317,6 +324,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
BillingCycle = request.BillingPlan,
|
||||
TermDuration = request.Term,
|
||||
PurchaseFullTerm = request.StartNewTermInNce,
|
||||
CustomTermEndDate = request.CustomTermEndDate,
|
||||
});
|
||||
|
||||
allAddOnMigrations.AddRange(addOnNewCommerceMigrations);
|
||||
|
@ -363,18 +371,12 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
/// <returns>The add on migration results.</returns>
|
||||
private List<MigrationResult> PrepareAddOnMigrationResult(IEnumerable<MigrationRequest> addOnMigrationRequests, string batchId, NewCommerceMigration? newCommerceMigration, NewCommerceMigrationError? newCommerceMigrationError, List<MigrationResult> migrationResults)
|
||||
{
|
||||
if(newCommerceMigration != null)
|
||||
foreach (var addOnMigrationResponse in newCommerceMigration.AddOnMigrations)
|
||||
{
|
||||
foreach (var addOnMigrationResponse in newCommerceMigration.AddOnMigrations)
|
||||
{
|
||||
var addOnMigrationRequest = addOnMigrationRequests.SingleOrDefault(n => n.LegacySubscriptionId.Equals(addOnMigrationResponse.CurrentSubscriptionId, StringComparison.OrdinalIgnoreCase));
|
||||
if(addOnMigrationRequest != null)
|
||||
{
|
||||
addOnMigrationResponse.Status = newCommerceMigration.Status;
|
||||
addOnMigrationResponse.Id = newCommerceMigration.Id;
|
||||
PrepareMigrationResult(addOnMigrationRequest, batchId, addOnMigrationResponse, newCommerceMigrationError, migrationResults);
|
||||
}
|
||||
}
|
||||
var addOnMigrationRequest = addOnMigrationRequests.SingleOrDefault(n => n.LegacySubscriptionId.Equals(addOnMigrationResponse.CurrentSubscriptionId, StringComparison.OrdinalIgnoreCase));
|
||||
addOnMigrationResponse.Status = newCommerceMigration.Status;
|
||||
addOnMigrationResponse.Id = newCommerceMigration.Id;
|
||||
PrepareMigrationResult(addOnMigrationRequest, batchId, addOnMigrationResponse, newCommerceMigrationError, migrationResults);
|
||||
}
|
||||
|
||||
return migrationResults;
|
||||
|
@ -407,6 +409,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
NCETermDuration = migrationRequest.Term,
|
||||
NCEBillingPlan = migrationRequest.BillingPlan,
|
||||
NCESeatCount = migrationRequest.SeatCount,
|
||||
CustomTermEndDate = migrationRequest.CustomTermEndDate,
|
||||
ErrorCode = newCommerceMigrationError.Code,
|
||||
ErrorReason = newCommerceMigrationError.Description,
|
||||
};
|
||||
|
@ -432,6 +435,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
NCETermDuration = newCommerceMigration.TermDuration,
|
||||
NCEBillingPlan = newCommerceMigration.BillingCycle,
|
||||
NCESeatCount = newCommerceMigration.Quantity,
|
||||
CustomTermEndDate = newCommerceMigration.CustomTermEndDate,
|
||||
NCESubscriptionId = newCommerceMigration.NewCommerceSubscriptionId,
|
||||
BatchId = batchId,
|
||||
MigrationId = newCommerceMigration.Id,
|
||||
|
@ -469,6 +473,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
NCETermDuration = migrationResult.NCETermDuration,
|
||||
NCEBillingPlan = migrationResult.NCEBillingPlan,
|
||||
NCESeatCount = migrationResult.NCESeatCount,
|
||||
CustomTermEndDate = migrationResult.CustomTermEndDate,
|
||||
ErrorCode = newCommerceMigrationError.Code,
|
||||
ErrorReason = newCommerceMigrationError.Description,
|
||||
};
|
||||
|
@ -491,6 +496,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
|
|||
NCETermDuration = newCommerceMigration.TermDuration,
|
||||
NCEBillingPlan = newCommerceMigration.BillingCycle,
|
||||
NCESeatCount = newCommerceMigration.Quantity,
|
||||
CustomTermEndDate = newCommerceMigration.CustomTermEndDate,
|
||||
NCESubscriptionId = newCommerceMigration.NewCommerceSubscriptionId,
|
||||
BatchId = batchId,
|
||||
MigrationId = newCommerceMigration.Id,
|
||||
|
|
|
@ -0,0 +1,849 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="NewCommerceMigrationScheduleProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace NCEBulkMigrationTool
|
||||
{
|
||||
internal class NewCommerceMigrationScheduleProvider : INewCommerceMigrationScheduleProvider
|
||||
{
|
||||
private static string partnerTenantId = string.Empty;
|
||||
private readonly ITokenProvider tokenProvider;
|
||||
private long subscriptionsCntr = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The NewCommerceMigrationScheduleProvider constructor.
|
||||
/// </summary>
|
||||
/// <param name="tokenProvider">The token provider.</param>
|
||||
public NewCommerceMigrationScheduleProvider(ITokenProvider tokenProvider)
|
||||
{
|
||||
this.tokenProvider = tokenProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> ValidateAndGetSubscriptionsToScheduleMigrationAsync()
|
||||
{
|
||||
subscriptionsCntr = 0;
|
||||
var csvProvider = new CsvProvider();
|
||||
|
||||
using TextReader fileReader = File.OpenText($"{Constants.InputFolderPath}/customers.csv");
|
||||
using var csvReader = new CsvReader(fileReader, CultureInfo.InvariantCulture, leaveOpen: true);
|
||||
var inputCustomers = csvReader.GetRecordsAsync<CompanyProfile>();
|
||||
|
||||
ConcurrentBag<IEnumerable<ScheduleMigrationRequest>> allMigrationRequests = new ConcurrentBag<IEnumerable<ScheduleMigrationRequest>>();
|
||||
var failedCustomersBag = new ConcurrentBag<CompanyProfile>();
|
||||
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
partnerTenantId = authenticationResult.TenantId;
|
||||
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
var options = new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5
|
||||
};
|
||||
|
||||
await Parallel.ForEachAsync(inputCustomers, options, async (customer, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var subscriptions = await GetLegacySubscriptionsAsync(httpClient, customer, cancellationToken);
|
||||
|
||||
Console.WriteLine($"Validating subscriptions eligibility for customer {customer.CompanyName}");
|
||||
var migrationEligibilities = await ValidateMigrationEligibility(customer, httpClient, options, subscriptions);
|
||||
var migrationScheduleDetails = await GetMigrationScheduleAsync(customer, httpClient, options, migrationEligibilities);
|
||||
allMigrationRequests.Add(migrationScheduleDetails.Select(a => a));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to verify migration eligibility for customer {customer.CompanyName} {ex}");
|
||||
failedCustomersBag.Add(customer);
|
||||
}
|
||||
});
|
||||
|
||||
csvReader.Dispose();
|
||||
fileReader.Close();
|
||||
|
||||
Console.WriteLine("Exporting subscriptions");
|
||||
await csvProvider.ExportCsv(allMigrationRequests.SelectMany(m => m), $"{Constants.OutputFolderPath}/subscriptionsforschedule.csv");
|
||||
Console.WriteLine($"Exported subscriptions at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/subscriptionsforschedule.csv");
|
||||
|
||||
if (failedCustomersBag.Count > 0)
|
||||
{
|
||||
Console.WriteLine("Exporting failed customers");
|
||||
await csvProvider.ExportCsv(failedCustomersBag, $"{Constants.OutputFolderPath}/failedCustomers_schedulemigration.csv");
|
||||
Console.WriteLine($"Exported failed customers at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/failedCustomers_schedulemigration.csv");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> UploadNewCommerceMigrationSchedulesAsync()
|
||||
{
|
||||
var csvProvider = new CsvProvider();
|
||||
|
||||
var inputFileNames = Directory.EnumerateFiles($"{Constants.InputFolderPath}/subscriptionsforschedule");
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
|
||||
foreach (var fileName in inputFileNames)
|
||||
{
|
||||
Console.WriteLine($"Processing file {fileName}");
|
||||
|
||||
using TextReader fileReader = File.OpenText(fileName);
|
||||
using var csvReader = new CsvReader(fileReader, CultureInfo.InvariantCulture, leaveOpen: true);
|
||||
var inputMigrationRequests = csvReader.GetRecords<ScheduleMigrationRequest>().ToList();
|
||||
|
||||
if (inputMigrationRequests.Count > 200)
|
||||
{
|
||||
Console.WriteLine($"There are too many migration requests in the file: {fileName}. The maximum limit for migration uploads per file is 200. Please fix the input file to continue...");
|
||||
continue;
|
||||
}
|
||||
|
||||
var migrations = new ConcurrentBag<IEnumerable<ScheduleMigrationResult>>();
|
||||
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
var options = new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5
|
||||
};
|
||||
|
||||
long subscriptionsCntr = 0; //The counter to track
|
||||
var batchId = Guid.NewGuid().ToString();
|
||||
|
||||
var inputBaseMigrationRequests = inputMigrationRequests.Where(m => !m.AddOn && m.MigrationEligible);
|
||||
var inputAddOnMigrationRequests = inputMigrationRequests.Where(m => m.AddOn && m.MigrationEligible);
|
||||
|
||||
await Parallel.ForEachAsync(inputBaseMigrationRequests, options, async (migrationRequest, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ScheduleMigrationResult> migrationResult;
|
||||
migrationResult = await this.PostNewCommerceMigrationScheduleAsync(httpClient, migrationRequest, inputAddOnMigrationRequests, batchId, cancellationToken);
|
||||
migrations.Add(migrationResult);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine($"Migration for subscription: {migrationRequest.LegacySubscriptionId} failed.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Increment(ref subscriptionsCntr);
|
||||
Console.WriteLine($"Processed {subscriptionsCntr} subscription migration requests.", subscriptionsCntr);
|
||||
}
|
||||
});
|
||||
|
||||
csvReader.Dispose();
|
||||
fileReader.Close();
|
||||
|
||||
var index = fileName.LastIndexOf('\\');
|
||||
var processedFileName = fileName[++index..];
|
||||
|
||||
Console.WriteLine("Exporting migrations");
|
||||
await csvProvider.ExportCsv(migrations.SelectMany(m => m), $"{Constants.OutputFolderPath}/schedulemigrations/{processedFileName}_{batchId}.csv");
|
||||
|
||||
File.Move(fileName, $"{Constants.InputFolderPath}/subscriptionsforschedule/processed/{processedFileName}", true);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Console.WriteLine($"Exported migrations at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/schedulemigrations/{processedFileName}_{batchId}.csv");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> ExportNewCommerceMigrationSchedulesAsync()
|
||||
{
|
||||
var csvProvider = new CsvProvider();
|
||||
|
||||
using TextReader fileReader = File.OpenText($"{Constants.InputFolderPath}/customers.csv");
|
||||
using var csvReader = new CsvReader(fileReader, CultureInfo.InvariantCulture, leaveOpen: true);
|
||||
var inputCustomers = csvReader.GetRecordsAsync<CompanyProfile>();
|
||||
|
||||
ConcurrentBag<IEnumerable<NewCommerceMigrationSchedule>> allMigrationSchedules = new();
|
||||
var failedCustomersBag = new ConcurrentBag<CompanyProfile>();
|
||||
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
partnerTenantId = authenticationResult.TenantId;
|
||||
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
var options = new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5
|
||||
};
|
||||
|
||||
await Parallel.ForEachAsync(inputCustomers, options, async (customer, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var getScheduleMigrationsRequest = new HttpRequestMessage(HttpMethod.Get, $"{Routes.GetNewCommerceMigrationSchedules}/?CustomerTenantId={customer.TenantId}");
|
||||
|
||||
getScheduleMigrationsRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var scheduleMigrationsResponse = await httpClient.SendAsync(getScheduleMigrationsRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (scheduleMigrationsResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
getScheduleMigrationsRequest = new HttpRequestMessage(HttpMethod.Get, $"{Routes.GetNewCommerceMigrationSchedules}/?CustomerTenantId={customer.TenantId}");
|
||||
getScheduleMigrationsRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
scheduleMigrationsResponse = await httpClient.SendAsync(getScheduleMigrationsRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
scheduleMigrationsResponse.EnsureSuccessStatusCode();
|
||||
var newCommerceMigrationSchedules = await scheduleMigrationsResponse.Content.ReadFromJsonAsync<IEnumerable<NewCommerceMigrationSchedule>>().ConfigureAwait(false);
|
||||
allMigrationSchedules.Add(newCommerceMigrationSchedules);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to fetch migration schedules for customer {customer.CompanyName} {ex}");
|
||||
failedCustomersBag.Add(customer);
|
||||
}
|
||||
});
|
||||
|
||||
csvReader.Dispose();
|
||||
fileReader.Close();
|
||||
|
||||
Console.WriteLine("Exporting migration schedules");
|
||||
await csvProvider.ExportCsv(allMigrationSchedules.SelectMany(m => m), $"{Constants.OutputFolderPath}/schedulemigrations.csv");
|
||||
Console.WriteLine($"Exported schedule migrations at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/schedulemigrations.csv");
|
||||
|
||||
if (failedCustomersBag.Count > 0)
|
||||
{
|
||||
Console.WriteLine("Exporting failed customers");
|
||||
await csvProvider.ExportCsv(failedCustomersBag, "failedCustomers.csv");
|
||||
Console.WriteLine($"Exported failed customers at {Environment.CurrentDirectory}/failedCustomers.csv");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> CancelNewCommerceMigrationSchedulesAsync()
|
||||
{
|
||||
var csvProvider = new CsvProvider();
|
||||
|
||||
var inputFileNames = Directory.EnumerateFiles($"{Constants.InputFolderPath}/cancelschedulemigrations");
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
|
||||
foreach (var fileName in inputFileNames)
|
||||
{
|
||||
Console.WriteLine($"Processing file {fileName}");
|
||||
|
||||
using TextReader fileReader = File.OpenText(fileName);
|
||||
using var csvReader = new CsvReader(fileReader, CultureInfo.InvariantCulture, leaveOpen: true);
|
||||
var inputMigrationSchedules = csvReader.GetRecords<NewCommerceMigrationSchedule>().ToList();
|
||||
|
||||
if (inputMigrationSchedules.Count > 200)
|
||||
{
|
||||
Console.WriteLine($"There are too many migration schedule requests in the file: {fileName}. The maximum limit for migration uploads per file is 200. Please fix the input file to continue...");
|
||||
continue;
|
||||
}
|
||||
|
||||
var migrationSchedules = new ConcurrentBag<NewCommerceMigrationSchedule>();
|
||||
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
var options = new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5
|
||||
};
|
||||
|
||||
long scheduleMigrationsCounter = 0;
|
||||
var batchId = Guid.NewGuid().ToString();
|
||||
|
||||
var scheduledMigrationsToCancel = inputMigrationSchedules.Where(s => string.Equals(s.Status, "Cancel", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
await Parallel.ForEachAsync(scheduledMigrationsToCancel, options, async (scheduleMigration, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
NewCommerceMigrationSchedule migrationScheduleResult;
|
||||
migrationScheduleResult = await this.CancelNewCommerceMigrationScheduleAsync(httpClient, scheduleMigration, cancellationToken);
|
||||
migrationSchedules.Add(migrationScheduleResult);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine($"Cancel Migration Schedule for subscription: {scheduleMigration.CurrentSubscriptionId} failed.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Increment(ref scheduleMigrationsCounter);
|
||||
Console.WriteLine($"Processed {scheduleMigrationsCounter} cancel schedule migration requests.");
|
||||
}
|
||||
});
|
||||
|
||||
csvReader.Dispose();
|
||||
fileReader.Close();
|
||||
|
||||
var index = fileName.LastIndexOf('\\');
|
||||
var processedFileName = fileName[++index..];
|
||||
|
||||
Console.WriteLine("Exporting cancelled scheduled migrations");
|
||||
await csvProvider.ExportCsv(migrationSchedules.Select(s => s), $"{Constants.OutputFolderPath}/cancelschedulemigrations/{processedFileName}_{batchId}.csv");
|
||||
|
||||
File.Move(fileName, $"{Constants.InputFolderPath}/cancelschedulemigrations/processed/{processedFileName}", true);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Console.WriteLine($"Exported cancelled schedule migrations at {Environment.CurrentDirectory}/{Constants.OutputFolderPath}/cancelschedulemigrations/{processedFileName}_{batchId}.csv");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Subscription>> GetLegacySubscriptionsAsync(HttpClient httpClient, CompanyProfile customer, CancellationToken cancellationToken)
|
||||
{
|
||||
var allSubscriptions = await this.GetSubscriptionsAsync(httpClient, customer, cancellationToken).ConfigureAwait(false);
|
||||
var subscriptions = allSubscriptions.Where(s => Guid.TryParse(s.OfferId, out _));
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Subscription>> GetSubscriptionsAsync(HttpClient httpClient, CompanyProfile customer, CancellationToken cancellationToken)
|
||||
{
|
||||
var subscriptionRequest = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetSubscriptions, customer.TenantId));
|
||||
|
||||
subscriptionRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var subscriptionResponse = await httpClient.SendAsync(subscriptionRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (subscriptionResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
subscriptionRequest = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetSubscriptions, customer.TenantId));
|
||||
subscriptionRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
subscriptionResponse = await httpClient.SendAsync(subscriptionRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
subscriptionResponse.EnsureSuccessStatusCode();
|
||||
var subscriptionCollection = await subscriptionResponse.Content.ReadFromJsonAsync<ResourceCollection<Subscription>>().ConfigureAwait(false);
|
||||
|
||||
return subscriptionCollection!.Items;
|
||||
}
|
||||
|
||||
private async Task<ConcurrentBag<ScheduleMigrationRequest>> ValidateMigrationEligibility(CompanyProfile customer, HttpClient httpClient, ParallelOptions options, IEnumerable<Subscription> subscriptions)
|
||||
{
|
||||
var baseSubscriptions = subscriptions.Where(s => string.IsNullOrWhiteSpace(s.ParentSubscriptionId));
|
||||
var addOns = subscriptions.Where(s => !string.IsNullOrWhiteSpace(s.ParentSubscriptionId));
|
||||
var migrationRequests = new ConcurrentBag<ScheduleMigrationRequest>();
|
||||
var addOnEligibilityList = new ConcurrentBag<IEnumerable<NewCommerceEligibility>>();
|
||||
await Parallel.ForEachAsync(baseSubscriptions, options, async (subscription, cancellationToken) =>
|
||||
{
|
||||
var payload = new NewCommerceMigration
|
||||
{
|
||||
CurrentSubscriptionId = subscription.Id,
|
||||
};
|
||||
|
||||
var migrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.ValidateMigrationEligibility, customer.TenantId))
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
migrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var migrationResponse = await httpClient.SendAsync(migrationRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (migrationResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
migrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.ValidateMigrationEligibility, customer.TenantId))
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
|
||||
};
|
||||
migrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
migrationResponse = await httpClient.SendAsync(migrationRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
migrationResponse.EnsureSuccessStatusCode();
|
||||
var newCommerceEligibility = await migrationResponse.Content.ReadFromJsonAsync<NewCommerceEligibility>().ConfigureAwait(false);
|
||||
if (newCommerceEligibility.AddOnMigrations.Any())
|
||||
{
|
||||
addOnEligibilityList.Add(newCommerceEligibility.AddOnMigrations);
|
||||
}
|
||||
|
||||
migrationRequests.Add(PrepareMigrationRequest(customer, subscription, newCommerceEligibility!));
|
||||
|
||||
Interlocked.Increment(ref subscriptionsCntr);
|
||||
Console.WriteLine($"Validated migration eligibility for {subscriptionsCntr} subscriptions.");
|
||||
});
|
||||
|
||||
foreach (var addOn in addOns)
|
||||
{
|
||||
var addOnEligibility = addOnEligibilityList.SelectMany(a => a).SingleOrDefault(m => m.CurrentSubscriptionId.Equals(addOn.Id, StringComparison.OrdinalIgnoreCase));
|
||||
if (addOnEligibility != null)
|
||||
{
|
||||
migrationRequests.Add(PrepareMigrationRequest(customer, addOn, addOnEligibility));
|
||||
}
|
||||
}
|
||||
|
||||
return migrationRequests;
|
||||
}
|
||||
|
||||
private async Task<ConcurrentBag<ScheduleMigrationRequest>> GetMigrationScheduleAsync(CompanyProfile customer, HttpClient httpClient, ParallelOptions options, IEnumerable<ScheduleMigrationRequest> scheduleMigrationRequests)
|
||||
{
|
||||
var baseSubscriptions = scheduleMigrationRequests.Where(s => string.IsNullOrWhiteSpace(s.BaseSubscriptionId));
|
||||
var addOns = scheduleMigrationRequests.Where(s => !string.IsNullOrWhiteSpace(s.BaseSubscriptionId));
|
||||
var migrationRequests = new ConcurrentBag<ScheduleMigrationRequest>();
|
||||
var addOnEligibilityList = new ConcurrentBag<IEnumerable<NewCommerceMigrationSchedule>>();
|
||||
await Parallel.ForEachAsync(baseSubscriptions, options, async (subscription, cancellationToken) =>
|
||||
{
|
||||
var getScheduleMigrationRequest = new HttpRequestMessage(HttpMethod.Get, $"{Routes.GetNewCommerceMigrationSchedules}?CustomerTenantId={customer.TenantId}&CurrentSubscriptionId={subscription.LegacySubscriptionId}");
|
||||
|
||||
getScheduleMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var migrationResponse = await httpClient.SendAsync(getScheduleMigrationRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (migrationResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
getScheduleMigrationRequest = new HttpRequestMessage(HttpMethod.Get, $"{Routes.GetNewCommerceMigrationSchedules}?CustomerTenantId={customer.TenantId}&CurrentSubscriptionId={subscription.LegacySubscriptionId}");
|
||||
getScheduleMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
migrationResponse = await httpClient.SendAsync(getScheduleMigrationRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
migrationResponse.EnsureSuccessStatusCode();
|
||||
var newCommerceMigrationSchedule = (await migrationResponse.Content.ReadFromJsonAsync<IEnumerable<NewCommerceMigrationSchedule>>().ConfigureAwait(false)).SingleOrDefault();
|
||||
|
||||
migrationRequests.Add(PrepareMigrationRequest(subscription, newCommerceMigrationSchedule));
|
||||
|
||||
if (newCommerceMigrationSchedule?.AddOnMigrationSchedules.Any() ?? false)
|
||||
{
|
||||
addOnEligibilityList.Add(newCommerceMigrationSchedule.AddOnMigrationSchedules);
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var addOn in addOns)
|
||||
{
|
||||
var addOnEligibility = addOnEligibilityList.SelectMany(a => a).SingleOrDefault(m => m.CurrentSubscriptionId.Equals(addOn.LegacySubscriptionId, StringComparison.OrdinalIgnoreCase));
|
||||
migrationRequests.Add(PrepareMigrationRequest(addOn, addOnEligibility));
|
||||
}
|
||||
|
||||
return migrationRequests;
|
||||
}
|
||||
|
||||
private static ScheduleMigrationRequest PrepareMigrationRequest(CompanyProfile companyProfile, Subscription subscription, NewCommerceEligibility newCommerceEligibility)
|
||||
{
|
||||
return new ScheduleMigrationRequest
|
||||
{
|
||||
PartnerTenantId = partnerTenantId,
|
||||
IndirectResellerMpnId = subscription.PartnerId,
|
||||
CustomerName = companyProfile.CompanyName,
|
||||
CustomerTenantId = companyProfile.TenantId,
|
||||
LegacySubscriptionId = subscription.Id,
|
||||
LegacySubscriptionName = subscription.FriendlyName,
|
||||
LegacyProductName = subscription.OfferName,
|
||||
ExpirationDate = subscription.CommitmentEndDate,
|
||||
AutoRenewEnabled = subscription.AutoRenewEnabled,
|
||||
MigrationEligible = newCommerceEligibility.IsEligible,
|
||||
NcePsa = newCommerceEligibility.CatalogItemId,
|
||||
CurrentTerm = subscription.TermDuration,
|
||||
CurrentBillingPlan = subscription.BillingCycle.ToString(),
|
||||
CurrentSeatCount = subscription.Quantity,
|
||||
StartNewTermInNce = false,
|
||||
Term = subscription.TermDuration,
|
||||
BillingPlan = subscription.BillingCycle.ToString(),
|
||||
SeatCount = subscription.Quantity,
|
||||
CustomTermEndDate = newCommerceEligibility.CustomTermEndDate,
|
||||
AddOn = !string.IsNullOrWhiteSpace(subscription.ParentSubscriptionId),
|
||||
BaseSubscriptionId = subscription.ParentSubscriptionId,
|
||||
MigrationIneligibilityReason = newCommerceEligibility.Errors.Any() ?
|
||||
string.Join(";", newCommerceEligibility.Errors.Select(e => e.Description)) :
|
||||
string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
private static ScheduleMigrationRequest PrepareMigrationRequest(ScheduleMigrationRequest scheduleMigrationRequest, NewCommerceMigrationSchedule? newCommerceMigrationSchedule)
|
||||
{
|
||||
if (newCommerceMigrationSchedule is not null)
|
||||
{
|
||||
return scheduleMigrationRequest with
|
||||
{
|
||||
StartNewTermInNce = newCommerceMigrationSchedule.PurchaseFullTerm,
|
||||
Term = newCommerceMigrationSchedule.TermDuration,
|
||||
BillingPlan = newCommerceMigrationSchedule.BillingCycle,
|
||||
SeatCount = newCommerceMigrationSchedule.Quantity,
|
||||
CustomTermEndDate = newCommerceMigrationSchedule.CustomTermEndDate,
|
||||
TargetDate = newCommerceMigrationSchedule.TargetDate,
|
||||
MigrateOnRenewal = newCommerceMigrationSchedule.MigrateOnRenewal,
|
||||
};
|
||||
}
|
||||
|
||||
return scheduleMigrationRequest;
|
||||
}
|
||||
|
||||
private async Task<List<ScheduleMigrationResult>> PostNewCommerceMigrationScheduleAsync(HttpClient httpClient, ScheduleMigrationRequest migrationRequest, IEnumerable<ScheduleMigrationRequest> addOnMigrationRequests, string batchId, CancellationToken cancellationToken)
|
||||
{
|
||||
var newCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.PostNewCommerceMigrationSchedule, migrationRequest.CustomerTenantId));
|
||||
|
||||
var getSchedulesRoute = $"{Routes.GetNewCommerceMigrationSchedules}?CurrentSubscriptionId={migrationRequest.LegacySubscriptionId}";
|
||||
var getSchedulesRequest = new HttpRequestMessage(HttpMethod.Get, getSchedulesRoute);
|
||||
getSchedulesRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
var existingNewCommerceMigrationScheduleResponse = await httpClient.SendAsync(getSchedulesRequest).ConfigureAwait(false);
|
||||
NewCommerceMigrationSchedule? existingSchedule = null;
|
||||
|
||||
if (existingNewCommerceMigrationScheduleResponse.IsSuccessStatusCode)
|
||||
{
|
||||
existingSchedule = (await existingNewCommerceMigrationScheduleResponse.Content.ReadFromJsonAsync<IEnumerable<NewCommerceMigrationSchedule>>().ConfigureAwait(false))?.FirstOrDefault();
|
||||
}
|
||||
|
||||
newCommerceMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var newCommerceMigrationSchedule = new NewCommerceMigrationSchedule
|
||||
{
|
||||
CurrentSubscriptionId = migrationRequest.LegacySubscriptionId,
|
||||
Quantity = migrationRequest.SeatCount,
|
||||
BillingCycle = migrationRequest.BillingPlan,
|
||||
TermDuration = migrationRequest.Term,
|
||||
CustomTermEndDate = migrationRequest.CustomTermEndDate,
|
||||
TargetDate = migrationRequest.TargetDate,
|
||||
MigrateOnRenewal = migrationRequest.MigrateOnRenewal,
|
||||
ExternalReferenceId = batchId,
|
||||
};
|
||||
|
||||
// If they want to start a new term, then we should take the input from the file.
|
||||
if (migrationRequest.StartNewTermInNce)
|
||||
{
|
||||
newCommerceMigrationSchedule.PurchaseFullTerm = true;
|
||||
}
|
||||
|
||||
if (existingSchedule is not null)
|
||||
{
|
||||
newCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Put, string.Format(Routes.UpdateNewCommerceMigrationSchedule, migrationRequest.CustomerTenantId, existingSchedule.Id));
|
||||
newCommerceMigrationSchedule = newCommerceMigrationSchedule with
|
||||
{
|
||||
Id = existingSchedule.Id,
|
||||
};
|
||||
}
|
||||
|
||||
newCommerceMigrationSchedule.AddOnMigrationSchedules = GetAddOnMigrationSchedules(migrationRequest.LegacySubscriptionId, addOnMigrationRequests);
|
||||
|
||||
newCommerceMigrationRequest.Content = new StringContent(JsonSerializer.Serialize(newCommerceMigrationSchedule), Encoding.UTF8, "application/json");
|
||||
|
||||
var migrationResponse = await httpClient.SendAsync(newCommerceMigrationRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (migrationResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
if (existingSchedule is not null)
|
||||
{
|
||||
newCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Put, string.Format(Routes.UpdateNewCommerceMigrationSchedule, migrationRequest.CustomerTenantId, existingSchedule.Id));
|
||||
newCommerceMigrationSchedule = newCommerceMigrationSchedule with
|
||||
{
|
||||
Id = existingSchedule.Id,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
newCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.PostNewCommerceMigrationSchedule, migrationRequest.CustomerTenantId));
|
||||
}
|
||||
|
||||
newCommerceMigrationRequest.Content = new StringContent(JsonSerializer.Serialize(newCommerceMigrationSchedule), Encoding.UTF8, "application/json");
|
||||
|
||||
newCommerceMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
migrationResponse = await httpClient.SendAsync(newCommerceMigrationRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
NewCommerceMigrationError? migrationError = null;
|
||||
NewCommerceMigrationSchedule? migration = null;
|
||||
|
||||
if (migrationResponse.IsSuccessStatusCode)
|
||||
{
|
||||
migration = await migrationResponse.Content.ReadFromJsonAsync<NewCommerceMigrationSchedule>().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
migrationError = await migrationResponse.Content.ReadFromJsonAsync<NewCommerceMigrationError>().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return this.PrepareMigrationResult(migrationRequest, addOnMigrationRequests, batchId, migration, migrationError);
|
||||
}
|
||||
|
||||
private async Task<NewCommerceMigrationSchedule> CancelNewCommerceMigrationScheduleAsync(HttpClient httpClient, NewCommerceMigrationSchedule newCommerceMigrationSchedule, CancellationToken cancellationToken)
|
||||
{
|
||||
var cancelNewCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.CancelNewCommerceMigrationSchedule, newCommerceMigrationSchedule.CustomerTenantId, newCommerceMigrationSchedule.Id));
|
||||
cancelNewCommerceMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var migrationScheduleResponse = await httpClient.SendAsync(cancelNewCommerceMigrationRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (migrationScheduleResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
cancelNewCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.CancelNewCommerceMigrationSchedule, newCommerceMigrationSchedule.CustomerTenantId, newCommerceMigrationSchedule.Id));
|
||||
cancelNewCommerceMigrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
cancelNewCommerceMigrationRequest.Content = new StringContent(JsonSerializer.Serialize(newCommerceMigrationSchedule), Encoding.UTF8, "application/json");
|
||||
migrationScheduleResponse = await httpClient.SendAsync(cancelNewCommerceMigrationRequest).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (migrationScheduleResponse.IsSuccessStatusCode)
|
||||
{
|
||||
newCommerceMigrationSchedule = await migrationScheduleResponse.Content.ReadFromJsonAsync<NewCommerceMigrationSchedule>().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var migrationScheduleError = await migrationScheduleResponse.Content.ReadFromJsonAsync<NewCommerceMigrationError>().ConfigureAwait(false);
|
||||
newCommerceMigrationSchedule.ErrorCode = migrationScheduleError.Code;
|
||||
newCommerceMigrationSchedule.ErrorDescription = migrationScheduleError.Description;
|
||||
}
|
||||
|
||||
return newCommerceMigrationSchedule;
|
||||
}
|
||||
|
||||
private static IEnumerable<NewCommerceMigrationSchedule> GetAddOnMigrationSchedules(string currentSubscriptionId, IEnumerable<ScheduleMigrationRequest> addOnMigrationRequests)
|
||||
{
|
||||
if (!addOnMigrationRequests.Any())
|
||||
{
|
||||
return Enumerable.Empty<NewCommerceMigrationSchedule>();
|
||||
}
|
||||
|
||||
var allAddOnMigrations = new List<NewCommerceMigrationSchedule>();
|
||||
|
||||
var childAddOns = addOnMigrationRequests.Where(a => a.BaseSubscriptionId.Equals(currentSubscriptionId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (childAddOns.Any())
|
||||
{
|
||||
var addOnNewCommerceMigrations = childAddOns.Select(request => new NewCommerceMigrationSchedule
|
||||
{
|
||||
CurrentSubscriptionId = request.LegacySubscriptionId,
|
||||
Quantity = request.SeatCount,
|
||||
BillingCycle = request.BillingPlan,
|
||||
TermDuration = request.Term,
|
||||
PurchaseFullTerm = request.StartNewTermInNce,
|
||||
CustomTermEndDate = request.CustomTermEndDate,
|
||||
TargetDate = request.TargetDate,
|
||||
MigrateOnRenewal = request.MigrateOnRenewal,
|
||||
});
|
||||
|
||||
allAddOnMigrations.AddRange(addOnNewCommerceMigrations);
|
||||
|
||||
foreach (var item in childAddOns)
|
||||
{
|
||||
var multiLevelAddons = GetAddOnMigrationSchedules(item.LegacySubscriptionId, addOnMigrationRequests);
|
||||
allAddOnMigrations.AddRange(multiLevelAddons);
|
||||
}
|
||||
}
|
||||
|
||||
return allAddOnMigrations;
|
||||
}
|
||||
|
||||
private List<ScheduleMigrationResult> PrepareMigrationResult(ScheduleMigrationRequest migrationRequest, IEnumerable<ScheduleMigrationRequest> addOnMigrationRequests, string batchId, NewCommerceMigrationSchedule? newCommerceMigrationSchedule = null, NewCommerceMigrationError? newCommerceMigrationError = null)
|
||||
{
|
||||
var migrationResults = new List<ScheduleMigrationResult>();
|
||||
PrepareMigrationResult(migrationRequest, batchId, newCommerceMigrationSchedule, newCommerceMigrationError, migrationResults);
|
||||
|
||||
if (newCommerceMigrationSchedule?.AddOnMigrationSchedules.Any() == true)
|
||||
{
|
||||
PrepareAddOnMigrationResult(addOnMigrationRequests, batchId, newCommerceMigrationSchedule, newCommerceMigrationError, migrationResults);
|
||||
}
|
||||
|
||||
return migrationResults;
|
||||
}
|
||||
|
||||
private static void PrepareMigrationResult(ScheduleMigrationRequest migrationRequest, string batchId, NewCommerceMigrationSchedule? newCommerceMigrationSchedule, NewCommerceMigrationError? newCommerceMigrationError, List<ScheduleMigrationResult> migrationResults)
|
||||
{
|
||||
if (newCommerceMigrationError != null)
|
||||
{
|
||||
var migrationResult = new ScheduleMigrationResult
|
||||
{
|
||||
PartnerTenantId = migrationRequest.PartnerTenantId,
|
||||
IndirectResellerMpnId = migrationRequest.IndirectResellerMpnId,
|
||||
CustomerName = migrationRequest.CustomerName,
|
||||
CustomerTenantId = migrationRequest.CustomerTenantId,
|
||||
LegacySubscriptionId = migrationRequest.LegacySubscriptionId,
|
||||
LegacySubscriptionName = migrationRequest.LegacySubscriptionName,
|
||||
LegacyProductName = migrationRequest.LegacyProductName,
|
||||
ExpirationDate = migrationRequest.ExpirationDate,
|
||||
AddOn = migrationRequest.AddOn,
|
||||
StartedNewTermInNce = migrationRequest.StartNewTermInNce,
|
||||
NCETermDuration = migrationRequest.Term,
|
||||
NCEBillingPlan = migrationRequest.BillingPlan,
|
||||
NCESeatCount = migrationRequest.SeatCount,
|
||||
CustomTermEndDate = migrationRequest.CustomTermEndDate,
|
||||
TargetDate = migrationRequest.TargetDate,
|
||||
MigrateOnRenewal = migrationRequest.MigrateOnRenewal,
|
||||
ErrorCode = newCommerceMigrationError.Code,
|
||||
ErrorReason = newCommerceMigrationError.Description,
|
||||
};
|
||||
|
||||
migrationResults.Add(migrationResult);
|
||||
}
|
||||
|
||||
if (newCommerceMigrationSchedule != null)
|
||||
{
|
||||
var migrationResult = new ScheduleMigrationResult
|
||||
{
|
||||
PartnerTenantId = migrationRequest.PartnerTenantId,
|
||||
IndirectResellerMpnId = migrationRequest.IndirectResellerMpnId,
|
||||
CustomerName = migrationRequest.CustomerName,
|
||||
CustomerTenantId = migrationRequest.CustomerTenantId,
|
||||
LegacySubscriptionId = migrationRequest.LegacySubscriptionId,
|
||||
LegacySubscriptionName = migrationRequest.LegacySubscriptionName,
|
||||
LegacyProductName = migrationRequest.LegacyProductName,
|
||||
ExpirationDate = migrationRequest.ExpirationDate,
|
||||
AddOn = migrationRequest.AddOn,
|
||||
StartedNewTermInNce = migrationRequest.StartNewTermInNce,
|
||||
NCETermDuration = newCommerceMigrationSchedule.TermDuration,
|
||||
NCEBillingPlan = newCommerceMigrationSchedule.BillingCycle,
|
||||
NCESeatCount = newCommerceMigrationSchedule.Quantity,
|
||||
CustomTermEndDate = migrationRequest.CustomTermEndDate,
|
||||
TargetDate = migrationRequest.TargetDate,
|
||||
MigrateOnRenewal = migrationRequest.MigrateOnRenewal,
|
||||
BatchId = batchId,
|
||||
MigrationScheduleId = newCommerceMigrationSchedule.Id,
|
||||
MigrationScheduleStatus = newCommerceMigrationSchedule.Status,
|
||||
};
|
||||
|
||||
migrationResults.Add(migrationResult);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ScheduleMigrationResult> PrepareAddOnMigrationResult(IEnumerable<ScheduleMigrationRequest> addOnMigrationRequests, string batchId, NewCommerceMigrationSchedule? newCommerceMigrationSchedule, NewCommerceMigrationError? newCommerceMigrationError, List<ScheduleMigrationResult> migrationResults)
|
||||
{
|
||||
if (newCommerceMigrationSchedule != null && addOnMigrationRequests?.Any() == true)
|
||||
{
|
||||
foreach (var addOnMigrationResponse in newCommerceMigrationSchedule.AddOnMigrationSchedules)
|
||||
{
|
||||
var addOnMigrationRequest = addOnMigrationRequests.SingleOrDefault(n => n.LegacySubscriptionId.Equals(addOnMigrationResponse.CurrentSubscriptionId, StringComparison.OrdinalIgnoreCase));
|
||||
addOnMigrationResponse.Status = newCommerceMigrationSchedule.Status;
|
||||
addOnMigrationResponse.Id = newCommerceMigrationSchedule.Id;
|
||||
PrepareMigrationResult(addOnMigrationRequest, batchId, addOnMigrationResponse, newCommerceMigrationError, migrationResults);
|
||||
}
|
||||
}
|
||||
|
||||
return migrationResults;
|
||||
}
|
||||
|
||||
private async Task<(ScheduleMigrationResult BaseMigrationResult, IEnumerable<NewCommerceMigrationSchedule> AddOnMigrationsResult)> GetNewCommerceMigrationScheduleByScheduleIdAsync(HttpClient httpClient, ScheduleMigrationResult migrationResult, CancellationToken cancellationToken)
|
||||
{
|
||||
// Validate that the migration result has a migrationId, if a migration didn't initiate the migrationId will be empty.
|
||||
if (string.IsNullOrWhiteSpace(migrationResult.MigrationScheduleId))
|
||||
{
|
||||
// We cannot determine the status, we should return this migration result.
|
||||
return (migrationResult, Enumerable.Empty<NewCommerceMigrationSchedule>());
|
||||
}
|
||||
|
||||
var getNewCommerceMigrationSchedule = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetNewCommerceMigrationSchedule, migrationResult.CustomerTenantId, migrationResult.MigrationScheduleId));
|
||||
|
||||
getNewCommerceMigrationSchedule.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
var migrationResponse = await httpClient.SendAsync(getNewCommerceMigrationSchedule, cancellationToken).ConfigureAwait(false);
|
||||
if (migrationResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
getNewCommerceMigrationSchedule = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetNewCommerceMigrationSchedule, migrationResult.CustomerTenantId, migrationResult.MigrationScheduleId));
|
||||
getNewCommerceMigrationSchedule.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
migrationResponse = await httpClient.SendAsync(getNewCommerceMigrationSchedule).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
NewCommerceMigrationError? migrationError = null;
|
||||
NewCommerceMigrationSchedule? migration = null;
|
||||
|
||||
if (migrationResponse.IsSuccessStatusCode)
|
||||
{
|
||||
migration = await migrationResponse.Content.ReadFromJsonAsync<NewCommerceMigrationSchedule>().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var result = this.PrepareMigrationResult(migrationResult, migrationResult.BatchId, migration, migrationError);
|
||||
return (result, migration?.AddOnMigrationSchedules);
|
||||
}
|
||||
|
||||
private ScheduleMigrationResult PrepareMigrationResult(ScheduleMigrationResult migrationResult, string batchId, NewCommerceMigrationSchedule? newCommerceMigrationSchedule = null, NewCommerceMigrationError? newCommerceMigrationError = null)
|
||||
{
|
||||
ScheduleMigrationResult result = new ScheduleMigrationResult();
|
||||
|
||||
if (newCommerceMigrationError != null)
|
||||
{
|
||||
result = new ScheduleMigrationResult
|
||||
{
|
||||
PartnerTenantId = migrationResult.PartnerTenantId,
|
||||
IndirectResellerMpnId = migrationResult.IndirectResellerMpnId,
|
||||
CustomerName = migrationResult.CustomerName,
|
||||
CustomerTenantId = migrationResult.CustomerTenantId,
|
||||
LegacySubscriptionId = migrationResult.LegacySubscriptionId,
|
||||
LegacySubscriptionName = migrationResult.LegacySubscriptionName,
|
||||
LegacyProductName = migrationResult.LegacyProductName,
|
||||
ExpirationDate = migrationResult.ExpirationDate,
|
||||
StartedNewTermInNce = migrationResult.StartedNewTermInNce,
|
||||
NCETermDuration = migrationResult.NCETermDuration,
|
||||
NCEBillingPlan = migrationResult.NCEBillingPlan,
|
||||
NCESeatCount = migrationResult.NCESeatCount,
|
||||
CustomTermEndDate = migrationResult.CustomTermEndDate,
|
||||
ErrorCode = newCommerceMigrationError.Code,
|
||||
ErrorReason = newCommerceMigrationError.Description,
|
||||
};
|
||||
}
|
||||
|
||||
if (newCommerceMigrationSchedule != null)
|
||||
{
|
||||
result = new ScheduleMigrationResult
|
||||
{
|
||||
PartnerTenantId = migrationResult.PartnerTenantId,
|
||||
IndirectResellerMpnId = migrationResult.IndirectResellerMpnId,
|
||||
CustomerName = migrationResult.CustomerName,
|
||||
CustomerTenantId = migrationResult.CustomerTenantId,
|
||||
LegacySubscriptionId = migrationResult.LegacySubscriptionId,
|
||||
LegacySubscriptionName = migrationResult.LegacySubscriptionName,
|
||||
LegacyProductName = migrationResult.LegacyProductName,
|
||||
ExpirationDate = migrationResult.ExpirationDate,
|
||||
StartedNewTermInNce = migrationResult.StartedNewTermInNce,
|
||||
NCETermDuration = newCommerceMigrationSchedule.TermDuration,
|
||||
NCEBillingPlan = newCommerceMigrationSchedule.BillingCycle,
|
||||
NCESeatCount = newCommerceMigrationSchedule.Quantity,
|
||||
CustomTermEndDate = newCommerceMigrationSchedule.CustomTermEndDate,
|
||||
TargetDate = newCommerceMigrationSchedule.TargetDate,
|
||||
MigrateOnRenewal = newCommerceMigrationSchedule.MigrateOnRenewal,
|
||||
BatchId = batchId,
|
||||
MigrationScheduleId = newCommerceMigrationSchedule.Id,
|
||||
MigrationScheduleStatus = newCommerceMigrationSchedule.Status,
|
||||
ErrorCode = newCommerceMigrationSchedule.ErrorCode,
|
||||
ErrorReason = newCommerceMigrationSchedule.ErrorDescription,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ if (args.Length == 2)
|
|||
}
|
||||
else
|
||||
{
|
||||
AppId:
|
||||
AppId:
|
||||
Console.WriteLine("Enter AppId");
|
||||
appId = Console.ReadLine();
|
||||
|
||||
|
@ -25,7 +25,7 @@ else
|
|||
goto AppId;
|
||||
}
|
||||
|
||||
Upn:
|
||||
Upn:
|
||||
Console.WriteLine("Enter Upn");
|
||||
upn = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(upn))
|
||||
|
@ -49,6 +49,7 @@ using IHost host = Host.CreateDefaultBuilder(args)
|
|||
services.AddSingleton<ICustomerProvider, CustomerProvider>();
|
||||
services.AddSingleton<ISubscriptionProvider, SubscriptionProvider>();
|
||||
services.AddSingleton<INewCommerceMigrationProvider, NewCommerceMigrationProvider>();
|
||||
services.AddSingleton<INewCommerceMigrationScheduleProvider, NewCommerceMigrationScheduleProvider>();
|
||||
}).Build();
|
||||
|
||||
await RunAsync(host.Services);
|
||||
|
@ -57,11 +58,7 @@ await host.RunAsync();
|
|||
|
||||
static async Task RunAsync(IServiceProvider serviceProvider)
|
||||
{
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/subscriptions/processed");
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/migrations/processed");
|
||||
Directory.CreateDirectory(Constants.OutputFolderPath);
|
||||
|
||||
ShowOptions:
|
||||
ShowOptions:
|
||||
Console.WriteLine("Please choose an option");
|
||||
|
||||
Console.WriteLine("1. Export customers");
|
||||
|
@ -69,23 +66,33 @@ static async Task RunAsync(IServiceProvider serviceProvider)
|
|||
Console.WriteLine("3. Upload migrations");
|
||||
Console.WriteLine("4. Export migration status");
|
||||
Console.WriteLine("5. Export NCE subscriptions");
|
||||
Console.WriteLine("6. Exit");
|
||||
Console.WriteLine("6. Export subscriptions with migration eligibility to schedule migrations");
|
||||
Console.WriteLine("7. Upload migration schedules");
|
||||
Console.WriteLine("8. Export schedule migrations");
|
||||
Console.WriteLine("9. Cancel schedule migrations");
|
||||
Console.WriteLine("10. Exit");
|
||||
|
||||
SelectOption:
|
||||
var option = Console.ReadLine();
|
||||
|
||||
if (!short.TryParse(option, out short input) || !(input >= 1 && input <= 6))
|
||||
if (!short.TryParse(option, out short input) || !(input >= 1 && input <= 10))
|
||||
{
|
||||
Console.WriteLine("Invalid input, Please try again! Possible values are {1, 2, 3, 4, 5, 6}");
|
||||
Console.WriteLine("Invalid input, Please try again! Possible values are {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}");
|
||||
goto SelectOption;
|
||||
}
|
||||
|
||||
if(input == 6)
|
||||
if (input == 10)
|
||||
{
|
||||
Console.WriteLine("Exiting the app!");
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/subscriptions/processed");
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/migrations/processed");
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/subscriptionsforschedule/processed");
|
||||
Directory.CreateDirectory($"{Constants.InputFolderPath}/cancelschedulemigrations/processed");
|
||||
Directory.CreateDirectory(Constants.OutputFolderPath);
|
||||
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var result = input switch
|
||||
|
@ -95,6 +102,10 @@ SelectOption:
|
|||
3 => await serviceProvider.GetRequiredService<INewCommerceMigrationProvider>().UploadNewCommerceMigrationsAsync(),
|
||||
4 => await serviceProvider.GetRequiredService<INewCommerceMigrationProvider>().ExportNewCommerceMigrationStatusAsync(),
|
||||
5 => await serviceProvider.GetRequiredService<ISubscriptionProvider>().ExportModernSubscriptionsAsync(),
|
||||
6 => await serviceProvider.GetRequiredService<INewCommerceMigrationScheduleProvider>().ValidateAndGetSubscriptionsToScheduleMigrationAsync(),
|
||||
7 => await serviceProvider.GetRequiredService<INewCommerceMigrationScheduleProvider>().UploadNewCommerceMigrationSchedulesAsync(),
|
||||
8 => await serviceProvider.GetRequiredService<INewCommerceMigrationScheduleProvider>().ExportNewCommerceMigrationSchedulesAsync(),
|
||||
9 => await serviceProvider.GetRequiredService<INewCommerceMigrationScheduleProvider>().CancelNewCommerceMigrationSchedulesAsync(),
|
||||
_ => throw new InvalidOperationException("Invalid input")
|
||||
};
|
||||
|
||||
|
|
|
@ -40,7 +40,10 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
partnerTenantId = authenticationResult.TenantId;
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
|
@ -98,7 +101,10 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
var authenticationResult = await this.tokenProvider.GetTokenAsync();
|
||||
partnerTenantId = authenticationResult.TenantId;
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(Routes.BaseUrl)
|
||||
};
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
|
||||
|
@ -182,7 +188,7 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
subscriptionRequest = new HttpRequestMessage(HttpMethod.Get, Routes.GetCustomers);
|
||||
subscriptionRequest = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetSubscriptions, customer.TenantId));
|
||||
subscriptionRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
subscriptionResponse = await httpClient.SendAsync(subscriptionRequest).ConfigureAwait(false);
|
||||
|
@ -229,7 +235,10 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
|
||||
httpClient.DefaultRequestHeaders.Add(Constants.PartnerCenterClientHeader, Constants.ClientName);
|
||||
migrationRequest = new HttpRequestMessage(HttpMethod.Get, Routes.GetCustomers);
|
||||
migrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.ValidateMigrationEligibility, customer.TenantId))
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
|
||||
};
|
||||
migrationRequest.Headers.Add("MS-CorrelationId", Guid.NewGuid().ToString());
|
||||
|
||||
migrationResponse = await httpClient.SendAsync(migrationRequest).ConfigureAwait(false);
|
||||
|
@ -237,7 +246,7 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
|
||||
migrationResponse.EnsureSuccessStatusCode();
|
||||
var newCommerceEligibility = await migrationResponse.Content.ReadFromJsonAsync<NewCommerceEligibility>().ConfigureAwait(false);
|
||||
if (newCommerceEligibility!.AddOnMigrations.Any())
|
||||
if (newCommerceEligibility.AddOnMigrations.Any())
|
||||
{
|
||||
addOnEligibilityList.Add(newCommerceEligibility.AddOnMigrations);
|
||||
}
|
||||
|
@ -289,6 +298,7 @@ internal class SubscriptionProvider : ISubscriptionProvider
|
|||
Term = subscription.TermDuration,
|
||||
BillingPlan = subscription.BillingCycle.ToString(),
|
||||
SeatCount = subscription.Quantity,
|
||||
CustomTermEndDate = newCommerceEligibility.CustomTermEndDate,
|
||||
AddOn = !string.IsNullOrWhiteSpace(subscription.ParentSubscriptionId),
|
||||
BaseSubscriptionId = subscription.ParentSubscriptionId,
|
||||
MigrationIneligibilityReason = newCommerceEligibility.Errors.Any() ?
|
||||
|
|
Загрузка…
Ссылка в новой задаче