Skip to content

Commit 41ffb1b

Browse files
committed
feat(cli): Updated System.CommandLine.
1 parent 24a7c11 commit 41ffb1b

File tree

10 files changed

+494
-289
lines changed

10 files changed

+494
-289
lines changed

src/libs/AutoSDK.CLI/Commands/AI/AiCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ internal sealed class AiCommand : Command
66
{
77
public AiCommand() : base(name: "ai", description: "AI subcommands.")
88
{
9-
AddCommand(new SpecFromDocsCommand());
9+
Subcommands.Add(new SpecFromDocsCommand());
1010
}
1111
}

src/libs/AutoSDK.CLI/Commands/AI/SpecFromDocsCommand.cs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,52 @@ namespace AutoSDK.CLI.Commands.AI;
66

77
internal sealed class SpecFromDocsCommand : Command
88
{
9+
private Argument<Uri> Input { get; } = new(
10+
name: "url")
11+
{
12+
DefaultValueFactory = _ => new Uri("https://example.com"),
13+
Description = "Input url",
14+
};
15+
16+
private Option<string> Output { get; } = new(
17+
name: "--output",
18+
aliases: ["-o"])
19+
{
20+
DefaultValueFactory = _ => "generated.yaml",
21+
Description = "Output file path",
22+
};
23+
924
public SpecFromDocsCommand() : base(name: "spec-from-docs", description: "Generates OpenAPI spec from SPA HTML docs.")
1025
{
11-
var inputOption = new Argument<Uri>(
12-
name: "url",
13-
getDefaultValue: () => new Uri("https://example.com"),
14-
description: "Input url");
15-
var outputOption = new Option<string>(
16-
aliases: ["--output", "-o"],
17-
getDefaultValue: () => "generated.yaml",
18-
description: "Output file path");
19-
AddArgument(inputOption);
20-
AddOption(outputOption);
26+
Arguments.Add(Input);
27+
Options.Add(Output);
2128

22-
this.SetHandler(
23-
HandleAsync,
24-
inputOption,
25-
outputOption);
29+
SetAction(HandleAsync);
2630
}
2731

28-
private static async Task HandleAsync(
29-
Uri uri,
30-
string outputPath)
32+
private async Task HandleAsync(
33+
ParseResult parseResult)
3134
{
35+
var uri = parseResult.GetRequiredValue(Input);
36+
var outputPath = parseResult.GetRequiredValue(Output);
37+
3238
Console.WriteLine($"Loading {uri}...");
3339

3440
using var firecrawlApi = new FirecrawlApp(apiKey:
3541
Environment.GetEnvironmentVariable("FIRECRAWL_API_KEY") ??
3642
throw new InvalidOperationException("Please set FIRECRAWL_API_KEY environment variable."));
3743
var firecrawlResponse = await firecrawlApi.Scraping.ScrapeAndExtractFromUrlAsync(
38-
uri.ToString(),
39-
waitFor: 15000).ConfigureAwait(false);
44+
new Firecrawl.AllOf<ScrapeAndExtractFromUrlRequest2, ScrapeOptions>
45+
{
46+
Value1 = new ScrapeAndExtractFromUrlRequest2
47+
{
48+
Url = uri.ToString(),
49+
},
50+
Value2 = new ScrapeOptions
51+
{
52+
WaitFor = 15000,
53+
}
54+
}).ConfigureAwait(false);
4055

4156
var markdown = firecrawlResponse.Data?.Markdown ?? throw new InvalidOperationException("No markdown data found.");
4257

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System.CommandLine;
2+
using AutoSDK.Extensions;
3+
using AutoSDK.Helpers;
4+
using AutoSDK.Models;
5+
6+
namespace AutoSDK.CLI.Commands;
7+
8+
internal sealed class CliCommand : Command
9+
{
10+
private Argument<string> Input { get; } = new(
11+
name: "input")
12+
{
13+
DefaultValueFactory = _ => string.Empty,
14+
Description = "Input file path",
15+
};
16+
17+
private Option<string> Output { get; } = new(
18+
name: "--output",
19+
aliases: ["-o"])
20+
{
21+
DefaultValueFactory = _ => "Testing",
22+
Description = "Output file path",
23+
};
24+
25+
private Option<bool> ExcludeDeprecated { get; } = new(
26+
name: "--exclude-deprecated-operations",
27+
aliases: ["-e"])
28+
{
29+
DefaultValueFactory = _ => Settings.Default.ExcludeDeprecatedOperations,
30+
Description = "Exclude deprecated operations",
31+
};
32+
33+
private Option<bool> IgnoreOpenApiErrors { get; } = new(
34+
name: "--ignore-openapi-errors")
35+
{
36+
DefaultValueFactory = _ => Settings.Default.IgnoreOpenApiErrors,
37+
Description = "Ignore OpenAPI errors",
38+
};
39+
40+
private Option<bool> IgnoreOpenApiWarnings { get; } = new(
41+
name: "--ignore-openapi-warnings")
42+
{
43+
DefaultValueFactory = _ => Settings.Default.IgnoreOpenApiWarnings,
44+
Description = "Ignore OpenAPI warnings",
45+
};
46+
47+
public CliCommand() : base(name: "cli", description: "Creates CLI .cs files for a OpenAPI spec.")
48+
{
49+
Arguments.Add(Input);
50+
Options.Add(Output);
51+
Options.Add(ExcludeDeprecated);
52+
Options.Add(IgnoreOpenApiErrors);
53+
Options.Add(IgnoreOpenApiWarnings);
54+
55+
SetAction(HandleAsync);
56+
}
57+
58+
private async Task HandleAsync(ParseResult parseResult)
59+
{
60+
string input = parseResult.GetRequiredValue(Input);
61+
string output = parseResult.GetRequiredValue(Output);
62+
bool excludeDeprecatedOperations = parseResult.GetRequiredValue(ExcludeDeprecated);
63+
bool ignoreOpenApiErrors = parseResult.GetRequiredValue(IgnoreOpenApiErrors);
64+
bool ignoreOpenApiWarnings = parseResult.GetRequiredValue(IgnoreOpenApiWarnings);
65+
66+
Console.WriteLine($"Loading {input}...");
67+
68+
using var client = new HttpClient();
69+
var yaml = input.StartsWith("http", StringComparison.OrdinalIgnoreCase)
70+
? await client.GetStringAsync(new Uri(input)).ConfigureAwait(false)
71+
: await File.ReadAllTextAsync(input).ConfigureAwait(false);
72+
73+
Console.WriteLine("Creating...");
74+
75+
var settings = Settings.Default with
76+
{
77+
ExcludeDeprecatedOperations = excludeDeprecatedOperations,
78+
IgnoreOpenApiErrors = ignoreOpenApiErrors,
79+
IgnoreOpenApiWarnings = ignoreOpenApiWarnings,
80+
};
81+
var openApiDocument = yaml.GetOpenApiDocument(settings);
82+
var schemas = openApiDocument.GetSchemas(settings);
83+
var operations = openApiDocument.GetOperations(settings, globalSettings: settings, schemas);
84+
85+
var files = new List<FileWithName>
86+
{
87+
new("http-client.env.json", @$"{{
88+
{openApiDocument.Servers.Select(x => @$"
89+
""{x.Description}"": {{
90+
""host"": ""{x.Url}"",
91+
""token"": """"
92+
}},").Inject().TrimEnd(',')}
93+
}}")
94+
};
95+
96+
foreach (var group in operations
97+
.SelectMany(x => x.Tags.Select(y => (Tag: y, x)))
98+
.GroupBy(x => x.Tag))
99+
{
100+
var content = string.Empty;
101+
102+
foreach (var (_, operation) in group)
103+
{
104+
// "initialAmount": 1000.0,
105+
// "numberOfMonths": 36,
106+
// "startDate": "2025-02-25"
107+
108+
var requestSchema = operation.Schemas.FirstOrDefault(y => y.Hint == Hint.Request);
109+
content += $@"
110+
### {operation.OperationType.ToString("G").ToUpperInvariant()} {operation.OperationPath}
111+
{operation.OperationType.ToString("G").ToUpperInvariant()} {{{{host}}}}{operation.OperationPath}
112+
{(operation.GlobalSecurityRequirements.Any() || operation.Operation.Security.Any() ? @"
113+
Authorization: Bearer {{token}}" : " ")}
114+
Content-Type: application/json
115+
116+
{(requestSchema != null ? @$"
117+
{{
118+
{requestSchema.Schema.Properties.Select(x => @$"
119+
""{x.Key}"": {x.Value.Example?.ToString() ?? "\"1\""},").Inject()}
120+
}}
121+
" : " ")}
122+
123+
".RemoveBlankLinesWhereOnlyWhitespaces();
124+
}
125+
126+
files.Add(new FileWithName(group.Key + ".http", content));
127+
}
128+
129+
Directory.CreateDirectory(output);
130+
131+
foreach (var file in files)
132+
{
133+
await File.WriteAllTextAsync(Path.Combine(output, file.Name), file.Text).ConfigureAwait(false);
134+
}
135+
136+
Console.WriteLine("Done.");
137+
}
138+
}

src/libs/AutoSDK.CLI/Commands/ConvertCommand.cs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,35 @@ namespace AutoSDK.CLI.Commands;
55

66
internal sealed class ConvertCommand : Command
77
{
8+
private Argument<string> Input { get; } = new(
9+
name: "input")
10+
{
11+
DefaultValueFactory = _ => string.Empty,
12+
Description = "Input file path",
13+
};
14+
15+
private Option<string> Output { get; } = new(
16+
name: "--output",
17+
aliases: ["-o"])
18+
{
19+
DefaultValueFactory = _ => "openapi30.yaml",
20+
Description = "Output file path",
21+
};
22+
823
public ConvertCommand() : base(name: "convert-to-openapi30", description: "Converts OpenAPI 3.1 spec to OpenAPI 3.0.")
924
{
10-
var inputOption = new Argument<string>(
11-
name: "input",
12-
getDefaultValue: () => string.Empty,
13-
description: "Input file path");
14-
var outputOption = new Option<string>(
15-
aliases: ["--output", "-o"],
16-
getDefaultValue: () => "openapi30.yaml",
17-
description: "Output file path");
18-
AddArgument(inputOption);
19-
AddOption(outputOption);
25+
Arguments.Add(Input);
26+
Options.Add(Output);
2027

21-
this.SetHandler(
22-
HandleAsync,
23-
inputOption,
24-
outputOption);
28+
SetAction(HandleAsync);
2529
}
2630

27-
private static async Task HandleAsync(
28-
string inputPath,
29-
string outputPath)
31+
private async Task HandleAsync(ParseResult parseResult)
3032
{
33+
var inputPath = parseResult.GetRequiredValue(Input);
34+
var outputPath = parseResult.GetRequiredValue(Output);
35+
36+
3137
Console.WriteLine($"Loading {inputPath}...");
3238

3339
using var client = new HttpClient();

0 commit comments

Comments
 (0)