Skip to content

Commit 6ed9b10

Browse files
Add openapi publish command
1 parent c7a2233 commit 6ed9b10

File tree

8 files changed

+60965
-38224
lines changed

8 files changed

+60965
-38224
lines changed

src/Nitro/CommandLine/src/CommandLine.Cloud/Commands/OpenApi/OpenApiCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public OpenApiCommand() : base("openapi")
1212
AddCommand(new DeleteOpenApiCollectionCommand());
1313
AddCommand(new ListOpenApiCollectionCommand());
1414
AddCommand(new UploadOpenApiCollectionCommand());
15+
AddCommand(new PublishOpenApiCollectionCommand());
1516
}
1617
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using System.Reactive;
2+
using System.Reactive.Linq;
3+
using System.Reactive.Subjects;
4+
using ChilliCream.Nitro.CommandLine.Cloud.Client;
5+
using ChilliCream.Nitro.CommandLine.Cloud.Option;
6+
using ChilliCream.Nitro.CommandLine.Cloud.Option.Binders;
7+
using StrawberryShake;
8+
using static ChilliCream.Nitro.CommandLine.Cloud.ThrowHelper;
9+
10+
namespace ChilliCream.Nitro.CommandLine.Cloud;
11+
12+
internal sealed class PublishOpenApiCollectionCommand : Command
13+
{
14+
public PublishOpenApiCollectionCommand() : base("publish")
15+
{
16+
Description = "Publish an OpenAPI collection version to an stage";
17+
18+
AddOption(Opt<TagOption>.Instance);
19+
AddOption(Opt<StageNameOption>.Instance);
20+
AddOption(Opt<OpenApiCollectionIdOption>.Instance);
21+
AddOption(Opt<ForceOption>.Instance);
22+
AddOption(Opt<OptionalWaitForApprovalOption>.Instance);
23+
24+
this.SetHandler(
25+
ExecuteAsync,
26+
Bind.FromServiceProvider<IAnsiConsole>(),
27+
Bind.FromServiceProvider<IApiClient>(),
28+
Opt<TagOption>.Instance,
29+
Opt<StageNameOption>.Instance,
30+
Opt<OpenApiCollectionIdOption>.Instance,
31+
Opt<ForceOption>.Instance,
32+
Opt<OptionalWaitForApprovalOption>.Instance,
33+
Bind.FromServiceProvider<CancellationToken>());
34+
}
35+
36+
private static async Task<int> ExecuteAsync(
37+
IAnsiConsole console,
38+
IApiClient client,
39+
string tag,
40+
string stage,
41+
string openApiCollectionId,
42+
bool force,
43+
bool waitForApproval,
44+
CancellationToken ct)
45+
{
46+
console.Title(
47+
$"Publish OpenAPI collection with tag {tag.EscapeMarkup()} to {stage.EscapeMarkup()}");
48+
49+
var committed = false;
50+
51+
if (console.IsHumanReadable())
52+
{
53+
await console
54+
.Status()
55+
.Spinner(Spinner.Known.BouncingBar)
56+
.SpinnerStyle(Style.Parse("green bold"))
57+
.StartAsync("Publishing...", PublishOpenApiCollection);
58+
}
59+
else
60+
{
61+
await PublishOpenApiCollection(null);
62+
}
63+
64+
return committed ? ExitCodes.Success : ExitCodes.Error;
65+
66+
async Task PublishOpenApiCollection(StatusContext? ctx)
67+
{
68+
var input = new PublishOpenApiCollectionInput
69+
{
70+
OpenApiCollectionId = openApiCollectionId,
71+
Stage = stage,
72+
Tag = tag,
73+
WaitForApproval = waitForApproval
74+
};
75+
76+
if (force)
77+
{
78+
input = input with { Force = true };
79+
console.Log("[yellow]Force push is enabled[/]");
80+
}
81+
82+
console.Log("Create publish request");
83+
84+
var requestId = await PublishOpenApiCollectionAsync(console, client, input, ct);
85+
86+
console.Log($"Publish request created [grey](ID: {requestId.EscapeMarkup()})[/]");
87+
88+
using var stopSignal = new Subject<Unit>();
89+
90+
var subscription = client.PublishOpenApiCollectionCommandSubscription
91+
.Watch(requestId, ExecutionStrategy.NetworkOnly)
92+
.TakeUntil(stopSignal);
93+
94+
await foreach (var x in subscription.ToAsyncEnumerable().WithCancellation(ct))
95+
{
96+
if (x.Errors is { Count: > 0 } errors)
97+
{
98+
console.PrintErrorsAndExit(errors);
99+
throw Exit("No request id returned");
100+
}
101+
102+
switch (x.Data?.OnOpenApiCollectionVersionPublishingUpdate)
103+
{
104+
case IProcessingTaskIsQueued v:
105+
ctx?.Status(
106+
$"Your request is queued. The current position in the queue is {v.QueuePosition}.");
107+
break;
108+
109+
case IOpenApiCollectionVersionPublishFailed { Errors: var openApiCollectionErrors }:
110+
console.ErrorLine("OpenAPI collection publish failed");
111+
console.PrintErrorsAndExit(openApiCollectionErrors);
112+
stopSignal.OnNext(Unit.Default);
113+
break;
114+
115+
case IOpenApiCollectionVersionPublishSuccess:
116+
committed = true;
117+
stopSignal.OnNext(Unit.Default);
118+
119+
console.Success("Successfully published OpenAPI collection!");
120+
break;
121+
122+
case IProcessingTaskIsReady:
123+
console.Success("Your request is ready for processing.");
124+
break;
125+
126+
case IOperationInProgress:
127+
ctx?.Status("Your request is in progress.");
128+
break;
129+
130+
case IWaitForApproval e:
131+
if (e.Deployment is
132+
IOnSchemaVersionPublishUpdated_OnSchemaVersionPublishingUpdate_Deployment_OpenApiCollectionDeployment
133+
deployment)
134+
{
135+
// TODO: Print the errors here
136+
// console.PrintErrors(deployment.Errors);
137+
}
138+
139+
ctx?.Status(
140+
"The processing of your request is waiting for approval. Check Nitro to approve the request.");
141+
break;
142+
143+
case IProcessingTaskApproved:
144+
ctx?.Status("The processing of your request is approved.");
145+
146+
break;
147+
148+
default:
149+
ctx?.Status(
150+
"This is an unknown response, upgrade Nitro CLI to the latest version.");
151+
break;
152+
}
153+
}
154+
}
155+
}
156+
157+
private static async Task<string> PublishOpenApiCollectionAsync(
158+
IAnsiConsole console,
159+
IApiClient client,
160+
PublishOpenApiCollectionInput input,
161+
CancellationToken ct)
162+
{
163+
var result =
164+
await client.PublishOpenApiCollectionCommandMutation.ExecuteAsync(input, ct);
165+
166+
console.EnsureNoErrors(result);
167+
var data = console.EnsureData(result);
168+
console.PrintErrorsAndExit(data.PublishOpenApiCollection.Errors);
169+
170+
if (data.PublishOpenApiCollection.Id is null)
171+
{
172+
throw new ExitException("Could not create publish request!");
173+
}
174+
175+
return data.PublishOpenApiCollection.Id;
176+
}
177+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
mutation PublishOpenApiCollectionCommandMutation($input: PublishOpenApiCollectionInput!) {
2+
publishOpenApiCollection(input: $input) {
3+
id
4+
errors {
5+
...UnauthorizedOperation
6+
...StageNotFoundError
7+
...OpenApiCollectionNotFoundError
8+
...OpenApiCollectionVersionNotFoundError
9+
...Error
10+
}
11+
}
12+
}
13+
14+
subscription PublishOpenApiCollectionCommandSubscription($requestId: ID!) {
15+
onOpenApiCollectionVersionPublishingUpdate(requestId: $requestId) {
16+
__typename
17+
...OpenApiCollectionVersionPublishFailed
18+
...OpenApiCollectionVersionPublishSuccess
19+
...OperationInProgress
20+
...WaitForApproval
21+
...ProcessingTaskApproved
22+
...ProcessingTaskIsReady
23+
...ProcessingTaskIsQueued
24+
}
25+
}

src/Nitro/CommandLine/src/CommandLine.Cloud/Extensions/ApiClientCommandLineBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace ChilliCream.Nitro.CommandLine.Cloud.Option.Binders;
1010
internal static class ApiClientCommandLineBuilderExtensions
1111
{
1212
private static readonly string s_userAgent = $"Nitro CLI/{Version}";
13-
private const string ClientId = "66973038-95a2-40aa-9b7a-b7de544c0da8";
13+
private const string ClientId = BuildSecrets.NitroApiClientId;
1414

1515
public static CommandLineBuilder AddApiClient(this CommandLineBuilder builder)
1616
=> builder

0 commit comments

Comments
 (0)