Skip to content

Commit

Permalink
Added update and publish flight submission commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
azchohfi committed Feb 26, 2024
1 parent dc62f3d commit 1ba7c18
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 3 deletions.
2 changes: 1 addition & 1 deletion MSStore.API/Packaged/Models/DevCenterFlightSubmission.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace MSStore.API.Packaged.Models
{
public class DevCenterFlightSubmission
public class DevCenterFlightSubmission : IDevCenterSubmission
{
public string? Id { get; set; }
public string? FlightId { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion MSStore.API/Packaged/Models/DevCenterSubmission.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace MSStore.API.Packaged.Models
{
public class DevCenterSubmission
public class DevCenterSubmission : IDevCenterSubmission
{
public string? Id { get; set; }
public DevCenterApplicationCategory? ApplicationCategory { get; set; }
Expand All @@ -22,6 +22,7 @@ public class DevCenterSubmission
public List<GamingOption>? GamingOptions { get; set; }
public bool HasExternalInAppProducts { get; set; }
public bool MeetAccessibilityGuidelines { get; set; }
public string? NotesForCertification { get; set; }
public string? Status { get; set; }
public StatusDetails? StatusDetails { get; set; }
public string? FileUploadUrl { get; set; }
Expand Down
19 changes: 19 additions & 0 deletions MSStore.API/Packaged/Models/IDevCenterSubmission.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace MSStore.API.Packaged.Models
{
public interface IDevCenterSubmission
{
public string? Id { get; set; }
public string? Status { get; set; }
public StatusDetails? StatusDetails { get; set; }
public PackageDeliveryOptions? PackageDeliveryOptions { get; set; }
public string? FileUploadUrl { get; set; }
public string? TargetPublishMode { get; set; }
public DateTime? TargetPublishDate { get; set; }
public string? NotesForCertification { get; set; }
}
}
2 changes: 2 additions & 0 deletions MSStore.CLI/Commands/Flights/FlightSubmissionCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public FlightSubmissionCommand()
{
AddCommand(new Submission.GetCommand());
AddCommand(new Submission.DeleteCommand());
AddCommand(new Submission.UpdateCommand());
AddCommand(new Submission.PublishCommand());
this.SetDefaultHelpHandler();
}
}
Expand Down
109 changes: 109 additions & 0 deletions MSStore.CLI/Commands/Flights/Submission/PublishCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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;
using MSStore.CLI.Helpers;
using MSStore.CLI.Services;
using Spectre.Console;

namespace MSStore.CLI.Commands.Flights.Submission
{
internal class PublishCommand : Command
{
public PublishCommand()
: base("publish", "Starts the flight submission process for the existing Draft.")
{
AddArgument(SubmissionCommand.ProductIdArgument);
AddArgument(Flights.GetCommand.FlightIdArgument);
}

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

public string ProductId { get; set; } = null!;
public string FlightId { 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>(
ProductId,
await AnsiConsole.Status().StartAsync("Publishing flight submission", async ctx =>
{
try
{
var storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: 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 -1;
}
var flightSubmission = flight.PendingFlightSubmission;
if (flightSubmission?.Id == null)
{
ctx.ErrorStatus($"Could not find flight submission for application flight with ID '{ProductId}'/'{FlightId}'");
return -1;
}
var flightSubmissionCommit = await storePackagedAPI.CommitFlightSubmissionAsync(ProductId, FlightId, flightSubmission.Id, ct);
if (flightSubmissionCommit == null)
{
throw new MSStoreException("Flight Submission commit failed");
}
if (flightSubmissionCommit.Status != null)
{
ctx.SuccessStatus($"Flight Submission Commited with status [green u]{flightSubmissionCommit.Status}[/]");
return 0;
}
ctx.ErrorStatus($"Could not commit flight submission for application flight with ID '{ProductId}'/'{FlightId}'");
AnsiConsole.MarkupLine($"[red]{flightSubmissionCommit.ToErrorMessage()}[/]");
return -1;
}
catch (Exception err)
{
_logger.LogError(err, "Error while publishing flight submission");
ctx.ErrorStatus(err);
return -1;
}
}), ct);
}
}
}
}
145 changes: 145 additions & 0 deletions MSStore.CLI/Commands/Flights/Submission/UpdateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

namespace MSStore.CLI.Commands.Flights.Submission
{
internal class UpdateCommand : Command
{
public UpdateCommand()
: base("update", "Updates the existing flight draft with the provided JSON.")
{
var product = new Argument<string>(
"product",
description: "The updated JSON product representation.");

AddArgument(SubmissionCommand.ProductIdArgument);
AddArgument(Flights.GetCommand.FlightIdArgument);
AddArgument(product);
AddOption(SubmissionCommand.SkipInitialPolling);
}

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

public string Product { get; set; } = null!;
public bool SkipInitialPolling { get; set; }
public string ProductId { get; set; } = null!;
public string FlightId { 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);
}

var updateFlightSubmission = JsonSerializer.Deserialize(Product, SourceGenerationContext.GetCustom().DevCenterFlightSubmissionUpdate);

if (updateFlightSubmission == null)
{
throw new MSStoreException("Invalid product provided.");
}

IStorePackagedAPI storePackagedAPI = null!;

var flight = await AnsiConsole.Status().StartAsync("Retrieving application flight", async ctx =>
{
try
{
storePackagedAPI = await _storeAPIFactory.CreatePackagedAsync(ct: ct);
var flight = await storePackagedAPI.GetFlightAsync(ProductId, FlightId, ct);
if (flight?.FlightId == null)
{
throw new MSStoreException($"Could not find application flight with ID '{ProductId}'/'{FlightId}'");
}
return flight;
}
catch (Exception err)
{
_logger.LogError(err, "Error while updating submission product.");
ctx.ErrorStatus(err);
return null;
}
});

if (storePackagedAPI == null || flight == null || flight?.FlightId == null)
{
return 1;
}

string? submissionId = flight.PendingFlightSubmission?.Id;

if (submissionId == null)
{
AnsiConsole.MarkupLine("Could not find an existing flight submission. [b green]Creating new flight submission[/].");

var flightSubmission = await storePackagedAPI.CreateNewFlightSubmissionAsync(ProductId, FlightId, _logger, ct);
submissionId = flightSubmission?.Id;

if (submissionId == null)
{
throw new MSStoreException("Could not create new flight submission.");
}
}

var updatedFlightSubmission = await AnsiConsole.Status().StartAsync("Updating flight submission product", async ctx =>
{
try
{
return await storePackagedAPI.UpdateFlightSubmissionAsync(ProductId, FlightId, submissionId, updateFlightSubmission, ct);
}
catch (Exception err)
{
_logger.LogError(err, "Error while updating flight submission product.");
ctx.ErrorStatus(err);
return null;
}
});

if (updatedFlightSubmission == null)
{
return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, -1, ct);
}

AnsiConsole.WriteLine(JsonSerializer.Serialize(updatedFlightSubmission, SourceGenerationContext.GetCustom(true).DevCenterFlightSubmission));

return await _telemetryClient.TrackCommandEventAsync<Handler>(ProductId, 0, ct);
}
}
}
}
2 changes: 1 addition & 1 deletion MSStore.CLI/Commands/Submission/UpdateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public new class Handler : ICommandHandler

if (submissionId == null)
{
throw new MSStoreException($"Could not find submission with ID '{productId}'");
throw new MSStoreException("Could not create new submission.");
}
}

Expand Down
22 changes: 22 additions & 0 deletions MSStore.CLI/Helpers/IStorePackagedAPIExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ internal static class IStorePackagedAPIExtensions
});
}

public static async Task<DevCenterFlightSubmission?> CreateNewFlightSubmissionAsync(this IStorePackagedAPI storePackagedAPI, string productId, string flightId, ILogger logger, CancellationToken ct)
{
return await AnsiConsole.Status().StartAsync("Creating new Flight Submission", async ctx =>
{
try
{
var flightSubmission = await storePackagedAPI.CreateFlightSubmissionAsync(productId, flightId, ct);
ctx.SuccessStatus($"Flight Submission created.");
logger.LogInformation("Flight Submission created. Id={FlightSubmissionId}", flightSubmission.Id);
return flightSubmission;
}
catch (Exception err)
{
logger.LogError(err, "Error while creating flight submission.");
ctx.ErrorStatus("Error while creating flight submission. Please try again.");
return null;
}
});
}

public static async Task<DevCenterSubmission?> GetExistingSubmission(this IStorePackagedAPI storePackagedAPI, string appId, string submissionId, ILogger logger, CancellationToken ct)
{
return await AnsiConsole.Status().StartAsync("Retrieving existing Submission", async ctx =>
Expand Down
4 changes: 4 additions & 0 deletions MSStore.CLI/StoreHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ internal static class StoreHostBuilderExtensions
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.GetCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.Submission.GetCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.Submission.DeleteCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.Submission.UpdateCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Commands.Flights.Submission.PublishCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PublishCommand.Handler))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(MicrosoftStoreCLI.Handler))]
Expand Down Expand Up @@ -57,6 +59,8 @@ public static IHostBuilder ConfigureStoreCLICommands(this IHostBuilder builder)
.UseCommandHandler<Commands.Flights.GetCommand, Commands.Flights.GetCommand.Handler>()
.UseCommandHandler<Commands.Flights.Submission.GetCommand, Commands.Flights.Submission.GetCommand.Handler>()
.UseCommandHandler<Commands.Flights.Submission.DeleteCommand, Commands.Flights.Submission.DeleteCommand.Handler>()
.UseCommandHandler<Commands.Flights.Submission.UpdateCommand, Commands.Flights.Submission.UpdateCommand.Handler>()
.UseCommandHandler<Commands.Flights.Submission.PublishCommand, Commands.Flights.Submission.PublishCommand.Handler>()
.UseCommandHandler<MicrosoftStoreCLI, MicrosoftStoreCLI.Handler>();
}
}
Expand Down

0 comments on commit 1ba7c18

Please sign in to comment.