This commit is contained in:
Kyle Swartz 2022-03-22 12:46:06 -04:00
Родитель 6e78851b7e
Коммит 6d7ad91e43
13 изменённых файлов: 258 добавлений и 14 удалений

Просмотреть файл

@ -1,11 +1,29 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="AppSettings.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The app settings record.
/// </summary>
internal record AppSettings
{
/// <summary>
/// Gets or sets the application id.
/// </summary>
public string AppId { get; init; } = string.Empty;
/// <summary>
/// Gets or sets the user principal name.
/// </summary>
public string Upn { get; init; } = string.Empty;
/// <summary>
/// Gets the domain from the user principal name.
/// </summary>
public string Domain
{
get

Просмотреть файл

@ -1,7 +1,23 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="CsvProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The CsvProvider class.
/// </summary>
internal class CsvProvider
{
/// <summary>
/// Exports provided data to CSV format.
/// </summary>
/// <typeparam name="T">The type of data to be translated to CSV format.</typeparam>
/// <param name="data">The list of data to export to CSV.</param>
/// <param name="fileName">The filename to write CSV data to.</param>
/// <returns>No return.</returns>
public async Task ExportCsv<T>(IEnumerable<T> data, string fileName)
{
int index = fileName.LastIndexOf('/');

Просмотреть файл

@ -1,14 +1,31 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="CustomerProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The CustomerProvider class.
/// </summary>
internal class CustomerProvider : ICustomerProvider
{
/// <summary>
/// The token provider.
/// </summary>
private readonly ITokenProvider tokenProvider;
/// <summary>
/// CustomerProvider constructor.
/// </summary>
/// <param name="tokenProvider">The token provider.</param>
public CustomerProvider(ITokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}
/// <inheritdoc/>
public async Task<bool> ExportCustomersAsync()
{
Console.WriteLine("Generating token...");

Просмотреть файл

@ -1,4 +1,10 @@
global using System.Net.Http.Headers;
// -----------------------------------------------------------------------
// <copyright file="GlobalUsing.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
global using System.Net.Http.Headers;
global using System.Net.Http.Json;
global using System.Text;
global using System.Text.Json;

Просмотреть файл

@ -1,6 +1,19 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="ICustomerProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The ICustomerProvider interface.
/// </summary>
internal interface ICustomerProvider
{
/// <summary>
/// Export all customers into CSV file.
/// </summary>
/// <returns>Bool for success/ failure.</returns>
Task<bool> ExportCustomersAsync();
}

Просмотреть файл

@ -1,8 +1,25 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="INewCommerceMigrationProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The INewCommerceMigrationProvider interface.
/// </summary>
internal interface INewCommerceMigrationProvider
{
/// <summary>
/// Uploads New Commerce Migrations based on CSV files in the input folders and writes the migration data to a new CSV file.
/// </summary>
/// <returns>Bool indicating success/ failure.</returns>
Task<bool> UploadNewCommerceMigrationsAsync();
/// <summary>
/// Exports the latest New Commerce Migration Status for given input migrations into a new CSV file.
/// </summary>
/// <returns>Bool indicating success/ failure.</returns>
Task<bool> ExportNewCommerceMigrationStatusAsync();
}
}

Просмотреть файл

@ -1,8 +1,25 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="ISubscriptionProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The ISubscriptionProvider interface.
/// </summary>
internal interface ISubscriptionProvider
{
/// <summary>
/// Exports legacy commerce subscriptions with their migration eligibility to an output CSV file.
/// </summary>
/// <returns>Bool indicating success/ failure.</returns>
Task<bool> ExportLegacySubscriptionsAsync();
/// <summary>
/// Exports NCE subscriptions to an output CSV file.
/// </summary>
/// <returns>Bool indicating success/ failure.</returns>
Task<bool> ExportModernSubscriptionsAsync();
}

Просмотреть файл

@ -1,6 +1,19 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="ITokenProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The ITokenProvider interface.
/// </summary>
internal interface ITokenProvider
{
/// <summary>
/// Gets a Partner token to authenticate with Partner Center APIs.
/// </summary>
/// <returns>AuthenticationResult containing the token.</returns>
Task<AuthenticationResult> GetTokenAsync();
}

Просмотреть файл

@ -1,4 +1,10 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="Models.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
internal record ResourceCollection<T>(IEnumerable<T> Items, int TotalCount, string ContinuationToken);

Просмотреть файл

@ -1,16 +1,28 @@
using System.Threading;
// -----------------------------------------------------------------------
// <copyright file="NewCommerceMigrationProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
{
/// <summary>
/// The token provider.
/// </summary>
private readonly ITokenProvider tokenProvider;
/// <summary>
/// The NewCommerceMigrationProvider constructor.
/// </summary>
/// <param name="tokenProvider">The token provider.</param>
public NewCommerceMigrationProvider(ITokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}
/// <inheritdoc/>
public async Task<bool> UploadNewCommerceMigrationsAsync()
{
var csvProvider = new CsvProvider();
@ -86,6 +98,7 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return true;
}
/// <inheritdoc/>
public async Task<bool> ExportNewCommerceMigrationStatusAsync()
{
var csvProvider = new CsvProvider();
@ -168,6 +181,13 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return true;
}
/// <summary>
/// Gets the New Commerce Migration by MigrationId.
/// </summary>
/// <param name="httpClient">The httpClient.</param>
/// <param name="migrationResult">The migrationResult.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The migration result.</returns>
private async Task<(MigrationResult BaseMigrationResult, IEnumerable<NewCommerceMigration> AddOnMigrationsResult)> GetNewCommerceMigrationByMigrationIdAsync(HttpClient httpClient, MigrationResult migrationResult, CancellationToken cancellationToken)
{
// Validate that the migration result has a migrationId, if a migration didn't initiate the migrationId will be empty.
@ -209,6 +229,15 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return (result, migration?.AddOnMigrations);
}
/// <summary>
/// Posts a New Commerce Migration via Partner Center API.
/// </summary>
/// <param name="httpClient">The httpClient.</param>
/// <param name="migrationRequest">The migration request.</param>
/// <param name="addOnMigrationRequests">The add on migration requests.</param>
/// <param name="batchId">The batchId.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The migration result.</returns>
private async Task<List<MigrationResult>> PostNewCommerceMigrationAsync(HttpClient httpClient, MigrationRequest migrationRequest, IEnumerable<MigrationRequest> addOnMigrationRequests, string batchId, CancellationToken cancellationToken)
{
var newCommerceMigrationRequest = new HttpRequestMessage(HttpMethod.Post, string.Format(Routes.PostNewCommerceMigration, migrationRequest.CustomerTenantId));
@ -262,6 +291,12 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return this.PrepareMigrationResult(migrationRequest, addOnMigrationRequests, batchId, migration, migrationError);
}
/// <summary>
/// Gets the add on migrations.
/// </summary>
/// <param name="currentSubscriptionId">The current subscriptionId.</param>
/// <param name="addOnMigrationRequests">The add on migration requests.</param>
/// <returns>The add on migrations.</returns>
private static IEnumerable<NewCommerceMigration> GetAddOnMigrations(string currentSubscriptionId, IEnumerable<MigrationRequest> addOnMigrationRequests)
{
if (!addOnMigrationRequests.Any())
@ -317,6 +352,15 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return migrationResults;
}
/// <summary>
/// Prepares the add on migration result.
/// </summary>
/// <param name="addOnMigrationRequests">The add on migration requests.</param>
/// <param name="batchId">The batchId.</param>
/// <param name="newCommerceMigration">The new commerce migration.</param>
/// <param name="newCommerceMigrationError">The new commerce migration error.</param>
/// <param name="migrationResults">The migration results.</param>
/// <returns>The add on migration results.</returns>
private List<MigrationResult> PrepareAddOnMigrationResult(IEnumerable<MigrationRequest> addOnMigrationRequests, string batchId, NewCommerceMigration? newCommerceMigration, NewCommerceMigrationError? newCommerceMigrationError, List<MigrationResult> migrationResults)
{
foreach (var addOnMigrationResponse in newCommerceMigration.AddOnMigrations)
@ -330,6 +374,14 @@ internal class NewCommerceMigrationProvider : INewCommerceMigrationProvider
return migrationResults;
}
/// <summary>
/// Prepares the migration result.
/// </summary>
/// <param name="migrationRequest">The migration request.</param>
/// <param name="batchId">The batchId.</param>
/// <param name="newCommerceMigration">The new commerce migration.</param>
/// <param name="newCommerceMigrationError">The new commerce migration error.</param>
/// <param name="migrationResults">The migration results.</param>
private static void PrepareMigrationResult(MigrationRequest migrationRequest, string batchId, NewCommerceMigration? newCommerceMigration, NewCommerceMigrationError? newCommerceMigrationError, List<MigrationResult> migrationResults)
{
if (newCommerceMigrationError != null)

Просмотреть файл

@ -1,4 +1,9 @@

// -----------------------------------------------------------------------
// <copyright file="Program.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
Console.WriteLine("Welcome to NCE Bulk Migration Tool!");
string? appId;
string? upn;

Просмотреть файл

@ -1,5 +1,14 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="SubscriptionProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The SubscriptionProvider class.
/// </summary>
internal class SubscriptionProvider : ISubscriptionProvider
{
@ -7,11 +16,16 @@ internal class SubscriptionProvider : ISubscriptionProvider
private readonly ITokenProvider tokenProvider;
private long subscriptionsCntr = 0;
/// <summary>
/// SubscriptionProvider constructor.
/// </summary>
/// <param name="tokenProvider"></param>
public SubscriptionProvider(ITokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}
/// <inheritdoc/>
public async Task<bool> ExportLegacySubscriptionsAsync()
{
var csvProvider = new CsvProvider();
@ -69,6 +83,7 @@ internal class SubscriptionProvider : ISubscriptionProvider
return true;
}
/// <inheritdoc/>
public async Task<bool> ExportModernSubscriptionsAsync()
{
var csvProvider = new CsvProvider();
@ -123,6 +138,13 @@ internal class SubscriptionProvider : ISubscriptionProvider
return true;
}
/// <summary>
/// Gets legacy subscriptions for a given customer.
/// </summary>
/// <param name="httpClient">The httpClient.</param>
/// <param name="customer">The customer.</param>
/// <param name="cancellationToken">The cancellationToken.</param>
/// <returns>Legacy subscriptions.</returns>
private async Task<IEnumerable<Subscription>> GetLegacySubscriptionsAsync(HttpClient httpClient, CompanyProfile customer, CancellationToken cancellationToken)
{
var allSubscriptions = await this.GetSubscriptionsAsync(httpClient, customer, cancellationToken).ConfigureAwait(false);
@ -140,6 +162,13 @@ internal class SubscriptionProvider : ISubscriptionProvider
return modernSubscriptions;
}
/// <summary>
/// Gets subscriptions for a given customer.
/// </summary>
/// <param name="httpClient">The htppClient.</param>
/// <param name="customer">The customer.</param>
/// <param name="cancellationToken">The cancellationToken.</param>
/// <returns>Subscriptions associated with the given customer.</returns>
private async Task<IEnumerable<Subscription>> GetSubscriptionsAsync(HttpClient httpClient, CompanyProfile customer, CancellationToken cancellationToken)
{
var subscriptionRequest = new HttpRequestMessage(HttpMethod.Get, string.Format(Routes.GetSubscriptions, customer.TenantId));
@ -165,6 +194,14 @@ internal class SubscriptionProvider : ISubscriptionProvider
return subscriptionCollection!.Items;
}
/// <summary>
/// Validates migration eligibility for a given customer and subscriptions.
/// </summary>
/// <param name="customer">The customer.</param>
/// <param name="httpClient">The httpClient.</param>
/// <param name="options">The ParallelOptions.</param>
/// <param name="subscriptions">The subscriptions.</param>
/// <returns>The migration eligibility.</returns>
private async Task<ConcurrentBag<MigrationRequest>> ValidateMigrationEligibility(CompanyProfile customer, HttpClient httpClient, ParallelOptions options, IEnumerable<Subscription> subscriptions)
{
var baseSubscriptions = subscriptions.Where(s => string.IsNullOrWhiteSpace(s.ParentSubscriptionId));
@ -223,6 +260,13 @@ internal class SubscriptionProvider : ISubscriptionProvider
return migrationRequests;
}
/// <summary>
/// Prepares the migration request for CSV output.
/// </summary>
/// <param name="companyProfile">The company profile.</param>
/// <param name="subscription">The subscription.</param>
/// <param name="newCommerceEligibility">The new commerce eligibility.</param>
/// <returns>The migration request.</returns>
private static MigrationRequest PrepareMigrationRequest(CompanyProfile companyProfile, Subscription subscription, NewCommerceEligibility newCommerceEligibility)
{
return new MigrationRequest
@ -252,6 +296,12 @@ internal class SubscriptionProvider : ISubscriptionProvider
};
}
/// <summary>
/// Prepares the modern subscription for CSV output.
/// </summary>
/// <param name="companyProfile">The company profile.</param>
/// <param name="subscription">The subscription.</param>
/// <returns>The Modern Subscription.</returns>
private static ModernSubscription PrepareModernSubscription(CompanyProfile companyProfile, Subscription subscription)
{
return new ModernSubscription

Просмотреть файл

@ -1,7 +1,20 @@
namespace NCEBulkMigrationTool;
// -----------------------------------------------------------------------
// <copyright file="TokenProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace NCEBulkMigrationTool;
/// <summary>
/// The TokenProvider class.
/// </summary>
internal class TokenProvider : ITokenProvider
{
/// <summary>
/// TokenProvider constructor.
/// </summary>
/// <param name="appSettings">The app settings.</param>
public TokenProvider(AppSettings appSettings)
{
this.appSettings = appSettings;
@ -10,6 +23,7 @@ internal class TokenProvider : ITokenProvider
private readonly AppSettings appSettings;
private AuthenticationResult? authenticationResult;
/// <inheritdoc/>
public async Task<AuthenticationResult> GetTokenAsync()
{
if (authenticationResult != null && authenticationResult.ExpiresOn > DateTimeOffset.UtcNow.AddMinutes(5))