Skip to content

Commit

Permalink
Added basic support for package flights (list only).
Browse files Browse the repository at this point in the history
  • Loading branch information
azchohfi committed Feb 23, 2024
1 parent 04c41aa commit ad09d7d
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 0 deletions.
1 change: 1 addition & 0 deletions MSStore.API/Packaged/IStorePackagedAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ public interface IStorePackagedAPI
Task<DevCenterSubmission> UpdateSubmissionAsync(string productId, string submissionId, DevCenterSubmission updatedSubmission, CancellationToken ct = default);
Task<DevCenterCommitResponse?> CommitSubmissionAsync(string productId, string submissionId, CancellationToken ct = default);
Task<DevCenterSubmissionStatusResponse> GetSubmissionStatusAsync(string productId, string submissionId, CancellationToken ct = default);
Task<List<DevCenterFlight>> GetFlightsAsync(string productId, CancellationToken ct = default);
}
}
17 changes: 17 additions & 0 deletions MSStore.API/Packaged/Models/DevCenterFlight.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;

namespace MSStore.API.Packaged.Models
{
public class DevCenterFlight
{
public string? FlightId { get; set; }
public string? FriendlyName { get; set; }
public ApplicationSubmissionInfo? LastPublishedFlightSubmission { get; set; }
public ApplicationSubmissionInfo? PendingFlightSubmission { get; set; }
public List<string>? GroupIds { get; set; }
public string? RankHigherThan { get; set; }
}
}
31 changes: 31 additions & 0 deletions MSStore.API/Packaged/StorePackagedAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class StorePackagedAPI : IStorePackagedAPI, IDisposable
private static readonly CompositeFormat DevCenterDeleteSubmissionTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/submissions/{2}");
private static readonly CompositeFormat DevCenterCommitSubmissionTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/submissions/{2}/Commit");
private static readonly CompositeFormat DevCenterSubmissionStatusTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/submissions/{2}/status");
private static readonly CompositeFormat DevCenterListFlightsTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/listflights?skip={2}&top={3}");

private SubmissionClient? _devCenterClient;

Expand Down Expand Up @@ -270,5 +271,35 @@ public async Task<DevCenterSubmissionStatusResponse> GetSubmissionStatusAsync(st
null,
ct);
}

public async Task<List<DevCenterFlight>> GetFlightsAsync(string productId, CancellationToken ct = default)
{
try
{
var devCenterFlightsResponse = await GetFlightsAsync(productId, 0, 100, ct); // TODO: pagination
return devCenterFlightsResponse.Value ?? new();
}
catch (Exception error)
{
throw new MSStoreException($"Failed to get the flights - {error.Message}", error);
}
}

private async Task<PagedResponse<DevCenterFlight>> GetFlightsAsync(string productId, int skip = 0, int top = 10, CancellationToken ct = default)
{
AssertClientInitialized();

return await _devCenterClient.InvokeAsync<PagedResponse<DevCenterFlight>>(
HttpMethod.Get,
string.Format(
CultureInfo.InvariantCulture,
DevCenterListFlightsTemplate,
DevCenterVersion,
productId,
skip,
top),
null,
ct);
}
}
}
1 change: 1 addition & 0 deletions MSStore.API/SourceGenerationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace MSStore.API.Models
[JsonSerializable(typeof(DevCenterError))]
[JsonSerializable(typeof(DevCenterCommitResponse))]
[JsonSerializable(typeof(DevCenterSubmissionStatusResponse))]
[JsonSerializable(typeof(PagedResponse<DevCenterFlight>))]
public partial class SourceGenerationContext : JsonSerializerContext
{
private static SourceGenerationContext? _default;
Expand Down
21 changes: 21 additions & 0 deletions MSStore.CLI.UnitTests/BaseCommandLineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ public class BaseCommandLineTest
}
};

protected List<DevCenterFlight> FakeFlights { get; } = new List<DevCenterFlight>
{
new DevCenterFlight
{
FlightId = "632B6A77-0E18-4B41-9033-3614D2174F2C",
FriendlyName = "FakeFlight1"
},
new DevCenterFlight
{
FlightId = "632B6A77-0E18-4B41-9033-3614D2174F2D",
FriendlyName = "FakeFlight2"
}
};

internal static Organization DefaultOrganization { get; } = new Organization
{
Id = new Guid("F3C1CCB6-09C0-4BAB-BABA-C034BFB60EF9")
Expand Down Expand Up @@ -444,6 +458,13 @@ protected void AddFakeApps()
.ReturnsAsync((string productId, CancellationToken ct) => FakeApps.First(a => a.Id == productId));
}

protected void AddFakeFlights()
{
FakeStorePackagedAPI
.Setup(x => x.GetFlightsAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(FakeFlights);
}

internal void InitDefaultSubmissionStatusResponseQueue()
{
FakeStorePackagedAPI
Expand Down
33 changes: 33 additions & 0 deletions MSStore.CLI.UnitTests/FlightsCommandUnitTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace MSStore.CLI.UnitTests
{
[TestClass]
public class FlightsCommandUnitTests : BaseCommandLineTest
{
[TestInitialize]
public void Init()
{
FakeLogin();
AddDefaultFakeAccount();
AddFakeApps();
AddFakeFlights();
}

[TestMethod]
public async Task FlightsListCommandShouldReturnZero()
{
var result = await ParseAndInvokeAsync(
new string[]
{
"flights",
"list",
FakeApps[0].Id!
});

result.Should().ContainAll(FakeFlights.Select(a => a.FlightId));
result.Should().ContainAll(FakeFlights.Select(a => a.FriendlyName));
}
}
}
106 changes: 106 additions & 0 deletions MSStore.CLI/Commands/Flights/ListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.Extensions.Logging;
using MSStore.CLI.Helpers;
using MSStore.CLI.Services;
using Spectre.Console;

namespace MSStore.CLI.Commands.Flights
{
internal class ListCommand : Command
{
public ListCommand()
: base("list", "Retrieves all the Flights for the specified Application.")
{
AddArgument(SubmissionCommand.ProductIdArgument);
}

public new class Handler : ICommandHandler
{
private readonly ILogger _logger;
private readonly IStoreAPIFactory _storeAPIFactory;
private readonly TelemetryClient _telemetryClient;

public string ProductId { get; set; } = null!;

public Handler(ILogger<Handler> logger, IStoreAPIFactory storeAPIFactory, TelemetryClient telemetryClient)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_storeAPIFactory = storeAPIFactory ?? throw new ArgumentNullException(nameof(storeAPIFactory));
_telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
}

public int Invoke(InvocationContext context)
{
return -1001;
}

public async Task<int> InvokeAsync(InvocationContext context)
{
var ct = context.GetCancellationToken();

if (ProductTypeHelper.Solve(ProductId) == ProductType.Unpackaged)
{
AnsiConsole.WriteLine("This command is not supported for unpackaged applications.");
return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, -1, ct);
}

return await _telemetryClient.TrackCommandEventAsync<Handler>(
await AnsiConsole.Status().StartAsync("Retrieving Flights", async ctx =>
{
try
{
var storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: ct);
var flightsList = await storePackagedAPI.GetFlightsAsync(ProductId, ct);
ctx.SuccessStatus("[bold green]Retrieved Flights[/]");
if (flightsList?.Count > 0)
{
var table = new Table();
table.AddColumns(string.Empty, "FlightId", "FriendlyName", "LastPublishedFlightSubmission.Id", "PendingFlightSubmission.Id", "GroupIds", "RankHigherThan");
int i = 1;
foreach (var f in flightsList)
{
table.AddRow(
i.ToString(CultureInfo.InvariantCulture),
$"[bold u]{f.FlightId}[/]",
$"[bold u]{f.FriendlyName}[/]",
$"[bold u]{f.LastPublishedFlightSubmission?.Id}[/]",
$"[bold u]{f.PendingFlightSubmission?.Id}[/]",
$"[bold u]{string.Join(", ", f.GroupIds ?? new())}[/]",
$"[bold u]{f.RankHigherThan}[/]");
i++;
}
AnsiConsole.Write(table);
}
else
{
AnsiConsole.MarkupLine("The application has [bold][u]no[/] Flights[/].");
}
AnsiConsole.WriteLine();
return 0;
}
catch (Exception err)
{
_logger.LogError(err, "Error while retrieving Flights.");
ctx.ErrorStatus(err);
return -1;
}
}), ct);
}
}
}
}
18 changes: 18 additions & 0 deletions MSStore.CLI/Commands/FlightsCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.CommandLine;
using MSStore.CLI.Commands.Flights;

namespace MSStore.CLI.Commands
{
internal class FlightsCommand : Command
{
public FlightsCommand()
: base("flights", "Execute flights related tasks.")
{
AddCommand(new ListCommand());
this.SetDefaultHelpHandler();
}
}
}
1 change: 1 addition & 0 deletions MSStore.CLI/MicrosoftStoreCLI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public MicrosoftStoreCLI()
AddCommand(new SettingsCommand());
AddCommand(new AppsCommand());
AddCommand(new SubmissionCommand());
AddCommand(new FlightsCommand());
AddCommand(new InitCommand());
AddCommand(new PackageCommand());
AddCommand(new PublishCommand());
Expand Down
2 changes: 2 additions & 0 deletions MSStore.CLI/StoreHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ internal static class StoreHostBuilderExtensions
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Submission.PollCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Submission.PublishCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Submission.DeleteCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.ListCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PublishCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(MicrosoftStoreCLI.Handler))]
Expand All @@ -49,6 +50,7 @@ public static IHostBuilder ConfigureStoreCLICommands(this IHostBuilder builder)
.UseCommandHandler<Commands.Submission.PollCommand, Commands.Submission.PollCommand.Handler>()
.UseCommandHandler<Commands.Submission.PublishCommand, Commands.Submission.PublishCommand.Handler>()
.UseCommandHandler<Commands.Submission.DeleteCommand, Commands.Submission.DeleteCommand.Handler>()
.UseCommandHandler<Commands.Flights.ListCommand, Commands.Flights.ListCommand.Handler>()
.UseCommandHandler<MicrosoftStoreCLI, MicrosoftStoreCLI.Handler>();
}
}
Expand Down

0 comments on commit ad09d7d

Please sign in to comment.