Skip to content

Commit

Permalink
Merge pull request #53 from microsoft/flightSubmission
Browse files Browse the repository at this point in the history
Full support for Flight submission
  • Loading branch information
azchohfi authored Jun 27, 2024
2 parents dbf1a0e + d7183d6 commit bbf9255
Show file tree
Hide file tree
Showing 20 changed files with 494 additions and 95 deletions.
1 change: 1 addition & 0 deletions MSStore.API/Packaged/IStorePackagedAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ public interface IStorePackagedAPI
Task<DevCenterFlightSubmission> CreateFlightSubmissionAsync(string productId, string flightId, CancellationToken ct = default);
Task<DevCenterFlightSubmission> UpdateFlightSubmissionAsync(string productId, string flightId, string submissionId, DevCenterFlightSubmissionUpdate updatedFlightSubmission, CancellationToken ct = default);
Task<DevCenterCommitResponse?> CommitFlightSubmissionAsync(string productId, string flightId, string submissionId, CancellationToken ct = default);
Task<DevCenterSubmissionStatusResponse> GetFlightSubmissionStatusAsync(string productId, string flightId, string submissionId, CancellationToken ct = default);
}
}
2 changes: 0 additions & 2 deletions MSStore.API/Packaged/Models/ApplicationPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ public class ApplicationPackage
public string? Id { get; set; }
public string? Version { get; set; }
public string? Architecture { get; set; }
public string? TargetPlatform { get; set; }
public List<string>? Languages { get; set; }
public List<string>? Capabilities { get; set; }
public string? MinimumDirectXVersion { get; set; }
public string? MinimumSystemRam { get; set; }
public List<string>? TargetDeviceFamilies { get; set; }
}
}
2 changes: 1 addition & 1 deletion MSStore.API/Packaged/Models/DevCenterFlightSubmission.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class DevCenterFlightSubmission : IDevCenterSubmission
public string? FlightId { get; set; }
public string? Status { get; set; }
public StatusDetails? StatusDetails { get; set; }
public List<FlightPackage>? FlightPackages { get; set; }
public List<ApplicationPackage>? FlightPackages { get; set; }
public PackageDeliveryOptions? PackageDeliveryOptions { get; set; }
public string? FileUploadUrl { get; set; }
public string? TargetPublishMode { get; set; }
Expand Down
19 changes: 0 additions & 19 deletions MSStore.API/Packaged/Models/FlightPackage.cs

This file was deleted.

18 changes: 18 additions & 0 deletions MSStore.API/Packaged/StorePackagedAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class StorePackagedAPI : IStorePackagedAPI, IDisposable
private static readonly CompositeFormat DevCenterGetFlightSubmissionTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/flights/{2}/submissions/{3}");
private static readonly CompositeFormat DevCenterDeleteFlightSubmissionTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/flights/{2}/submissions/{3}");
private static readonly CompositeFormat DevCenterCommitFlightSubmissionTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/flights/{2}/submissions/{3}/commit");
private static readonly CompositeFormat DevCenterFlightSubmissionStatusTemplate = CompositeFormat.Parse("/v{0}/my/applications/{1}/flights/{2}/submissions/{3}/status");

private SubmissionClient? _devCenterClient;

Expand Down Expand Up @@ -469,5 +470,22 @@ public async Task<DevCenterFlightSubmission> UpdateFlightSubmissionAsync(string

return JsonSerializer.Deserialize(ret, typeof(DevCenterCommitResponse), SourceGenerationContext.GetCustom()) as DevCenterCommitResponse;
}

public async Task<DevCenterSubmissionStatusResponse> GetFlightSubmissionStatusAsync(string productId, string flightId, string submissionId, CancellationToken ct = default)
{
AssertClientInitialized();

return await _devCenterClient.InvokeAsync<DevCenterSubmissionStatusResponse>(
HttpMethod.Get,
string.Format(
CultureInfo.InvariantCulture,
DevCenterFlightSubmissionStatusTemplate,
DevCenterVersion,
productId,
flightId,
submissionId),
null,
ct);
}
}
}
2 changes: 2 additions & 0 deletions MSStore.CLI/Commands/Flights/FlightSubmissionCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public FlightSubmissionCommand()
AddCommand(new Submission.DeleteCommand());
AddCommand(new Submission.UpdateCommand());
AddCommand(new Submission.PublishCommand());
AddCommand(new Submission.PollCommand());
AddCommand(new Submission.StatusCommand());
this.SetDefaultHelpHandler();
}
}
Expand Down
28 changes: 20 additions & 8 deletions MSStore.CLI/Commands/Flights/Submission/DeleteCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ namespace MSStore.CLI.Commands.Flights.Submission
internal class DeleteCommand : Command
{
public DeleteCommand()
: base("delete", "Deletes the package flight submission.")
: base("delete", "Deletes the pending package flight submission from the store.")
{
AddArgument(SubmissionCommand.ProductIdArgument);
AddArgument(Flights.GetCommand.FlightIdArgument);
AddArgument(GetCommand.SubmissionIdArgument);
AddOption(Commands.Submission.DeleteCommand.NoConfirmOption);
}

Expand All @@ -36,7 +35,6 @@ public DeleteCommand()

public string ProductId { get; set; } = null!;
public string FlightId { get; set; } = null!;
public string SubmissionId { get; set; } = null!;
public bool? NoConfirm { get; set; }

public Handler(ILogger<Handler> logger, IStoreAPIFactory storeAPIFactory, IConsoleReader consoleReader, IBrowserLauncher browserLauncher, TelemetryClient telemetryClient)
Expand Down Expand Up @@ -65,13 +63,27 @@ public async Task<int> InvokeAsync(InvocationContext context)

IStorePackagedAPI storePackagedAPI = null!;

var flightSubmission = await AnsiConsole.Status().StartAsync("Retrieving Flight Submission", async ctx =>
var flightSubmissionId = await AnsiConsole.Status().StartAsync("Retrieving Flight Submission", async ctx =>
{
try
{
storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: ct);

return await storePackagedAPI.GetFlightSubmissionAsync(ProductId, FlightId, SubmissionId, ct);
var flight = await storePackagedAPI.GetFlightAsync(ProductId, FlightId, ct);

if (flight?.FlightId == null)
{
ctx.ErrorStatus($"Could not find application flight with ID '{ProductId}'/'{FlightId}'");
return null;
}

if (flight.PendingFlightSubmission?.Id != null)
{
ctx.SuccessStatus($"Found [green]Pending Flight Submission[/].");
return flight.PendingFlightSubmission.Id;
}

return null;
}
catch (MSStoreHttpException err)
{
Expand All @@ -96,19 +108,19 @@ public async Task<int> InvokeAsync(InvocationContext context)
}
});

if (flightSubmission == null)
if (flightSubmissionId == null)
{
AnsiConsole.WriteLine("Could not find flight submission.");
return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, -1, ct);
}

AnsiConsole.WriteLine($"Found Flight Submission with Id '{flightSubmission.Id}'");
AnsiConsole.WriteLine($"Found Flight Submission with Id '{flightSubmissionId}'");
if (NoConfirm == false && !await _consoleReader.YesNoConfirmationAsync("Do you want to delete the pending flight submission?", ct))
{
return -2;
}

var success = await storePackagedAPI.DeleteSubmissionAsync(ProductId, FlightId, SubmissionId, _browserLauncher, _logger, ct);
var success = await storePackagedAPI.DeleteSubmissionAsync(ProductId, FlightId, flightSubmissionId, _browserLauncher, _logger, ct);

return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, success ? 0 : -1, ct);
}
Expand Down
25 changes: 12 additions & 13 deletions MSStore.CLI/Commands/Flights/Submission/GetCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,11 @@ namespace MSStore.CLI.Commands.Flights.Submission
{
internal class GetCommand : Command
{
internal static readonly Argument<string> SubmissionIdArgument;

static GetCommand()
{
SubmissionIdArgument = new Argument<string>("submissionId", "The submission Id.");
}

public GetCommand()
: base("get", "Retrieves the package flight submission.")
: base("get", "Retrieves the existing package flight submission, either the existing draft or the last published one.")
{
AddArgument(SubmissionCommand.ProductIdArgument);
AddArgument(Flights.GetCommand.FlightIdArgument);
AddArgument(SubmissionIdArgument);
}

public new class Handler : ICommandHandler
Expand All @@ -41,7 +33,6 @@ public GetCommand()

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

public Handler(ILogger<Handler> logger, IStoreAPIFactory storeAPIFactory, TelemetryClient telemetryClient)
{
Expand Down Expand Up @@ -71,14 +62,22 @@ public async Task<int> InvokeAsync(InvocationContext context)
{
var storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: ct);

return await storePackagedAPI.GetFlightSubmissionAsync(ProductId, FlightId, SubmissionId, ct);
var flight = await storePackagedAPI.GetFlightAsync(ProductId, FlightId, ct);

if (flight?.FlightId == null)
{
ctx.ErrorStatus($"Could not find application flight with ID '{ProductId}'/'{FlightId}'");
return null;
}

return await storePackagedAPI.GetAnyFlightSubmissionAsync(ProductId, flight, ctx, _logger, ct);
}
catch (MSStoreHttpException err)
{
if (err.Response.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
ctx.ErrorStatus("Could not find the flight submission. Please check the ProductId/FlightId/SubmissionId.");
_logger.LogError(err, "Could not find the flight submission. Please check the ProductId/FlightId/SubmissionId.");
ctx.ErrorStatus("Could not find the flight submission. Please check the ProductId/FlightId.");
_logger.LogError(err, "Could not find the flight submission. Please check the ProductId/FlightId.");
}
else
{
Expand Down
114 changes: 114 additions & 0 deletions MSStore.CLI/Commands/Flights/Submission/PollCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

namespace MSStore.CLI.Commands.Flights.Submission
{
internal class PollCommand : Command
{
public PollCommand()
: base("poll", "Polls until the existing flight submission is PUBLISHED or FAILED.")
{
AddArgument(SubmissionCommand.ProductIdArgument);
AddArgument(Flights.GetCommand.FlightIdArgument);
}

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

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

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

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

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

IStorePackagedAPI? storePackagedAPI = null;

DevCenterFlight? flight = null;
ApplicationSubmissionInfo? flightSubmission = null;

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

DevCenterSubmissionStatusResponse? lastSubmissionStatus = await AnsiConsole.Status().StartAsync("Polling flight submission status", async ctx =>
{
try
{
storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: ct);

flight = await storePackagedAPI.GetFlightAsync(ProductId, FlightId, ct);

if (flight?.FlightId == null)
{
ctx.ErrorStatus($"Could not find application flight with ID '{ProductId}'/'{FlightId}'");
return null;
}

flightSubmission = flight.PendingFlightSubmission;

if (flightSubmission?.Id == null)
{
ctx.ErrorStatus($"Could not find flight submission for application flight with ID '{ProductId}'/'{FlightId}'");
return null;
}

var lastSubmissionStatus = await storePackagedAPI.PollSubmissionStatusAsync(ProductId, flight.FlightId, flightSubmission.Id, false, _logger, ct: ct);

ctx.SuccessStatus();

return lastSubmissionStatus;
}
catch (Exception err)
{
_logger.LogError(err, "Error while polling submission status");
ctx.ErrorStatus(err);
}

return null;
});

if (lastSubmissionStatus != null && storePackagedAPI != null && flight?.FlightId != null && flightSubmission?.Id != null)
{
return await _telemetryClient.TrackCommandEventAsync<Handler>(
ProductId,
await storePackagedAPI.HandleLastSubmissionStatusAsync(lastSubmissionStatus, ProductId, flight.FlightId, flightSubmission.Id, _browserLauncher, _logger, ct),
ct);
}

return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, -1, ct);
}
}
}
}
Loading

0 comments on commit bbf9255

Please sign in to comment.