/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using ApiGenerator.Configuration; using Spectre.Console; #pragma warning disable 162 namespace ApiGenerator { public static class Program { private static bool Interactive { get; set; } = false; /// /// A main function can also take which is hooked up to support termination (e.g CTRL+C) /// /// The stack's branch we are targeting the generation for /// Run the generation interactively, this will ignore all flags /// Whether to download the specs or use an already downloaded copy /// Also generate the high level client (OpenSearch.Client) /// Only download the specs, skip all code generation /// /// private static async Task Main( string branch, bool interactive = false, bool download = false, bool includeHighLevel = false, bool skipGenerate = false , CancellationToken token = default) { Interactive = interactive; try { if (string.IsNullOrEmpty(branch)) { throw new ArgumentException("--branch can not be null"); } await Generate(download, branch, includeHighLevel, skipGenerate, token); } catch (OperationCanceledException) { AnsiConsole.WriteLine(); AnsiConsole.Write(new Rule("[b white on orange4_1] Cancelled [/]").LeftJustified()); AnsiConsole.WriteLine(); return 1; } catch (Exception ex) { AnsiConsole.WriteLine(); AnsiConsole.Write(new Rule("[b white on darkred] Exception [/]").LeftJustified()); AnsiConsole.WriteLine(); AnsiConsole.WriteException(ex, ExceptionFormats.ShowLinks); return 1; } return 0; } private static async Task Generate(bool download, string branch, bool includeHighLevel, bool skipGenerate, CancellationToken token = default) { var redownloadCoreSpecification = Ask("Download online rest specifications?", download); var downloadBranch = branch; if (Interactive && redownloadCoreSpecification) { Console.Write($"Branch to download specification from (default {downloadBranch}): "); var readBranch = Console.ReadLine()?.Trim(); if (!string.IsNullOrEmpty(readBranch)) downloadBranch = readBranch; } if (string.IsNullOrEmpty(downloadBranch)) throw new Exception($"Branch to download from is null or empty"); var generateCode = Ask("Generate code from the specification files on disk?", !skipGenerate); var lowLevelOnly = generateCode && Ask("Generate low level client only?", !includeHighLevel); static string YesNo(bool value) => value ? "[bold green]Yes[/]" : "[grey]No[/]"; var grid = new Grid() .AddColumn(new GridColumn().PadRight(4)) .AddColumn() .AddRow("[b]Download specification[/]", $"{YesNo(download)}") .AddRow("[b]Download branch[/]", $"{downloadBranch}") .AddRow("[b]Generate code from specification[/]", $"{YesNo(generateCode)}") .AddRow("[b]Include high level client[/]", $"{YesNo(!lowLevelOnly)}"); Console.WriteLine(); AnsiConsole.Write( new Panel(grid) .Header(new PanelHeader("[b white on chartreuse4] OpenSearch .NET client API generator [/]", Justify.Left)) ); Console.WriteLine(); if (redownloadCoreSpecification) { Console.WriteLine(); AnsiConsole.Write(new Rule("[b white on chartreuse4] Downloading specification [/]").LeftJustified()); Console.WriteLine(); await RestSpecDownloader.DownloadAsync(downloadBranch, token); } if (!generateCode) return 0; Console.WriteLine(); AnsiConsole.Write(new Rule("[b white on chartreuse4] Loading specification [/]").LeftJustified()); Console.WriteLine(); var spec = await Generator.ApiGenerator.CreateRestApiSpecModel(token); if (!lowLevelOnly) { foreach (var endpoint in spec.Endpoints.Select(e => e.Value.Name)) { if (CodeConfiguration.IsNewHighLevelApi(endpoint) && Ask($"Generate highlevel code for new api {endpoint}", false)) CodeConfiguration.EnableHighLevelCodeGen.Add(endpoint); } } Console.WriteLine(); AnsiConsole.Write(new Rule("[b white on chartreuse4] Generating code [/]").LeftJustified()); Console.WriteLine(); await Generator.ApiGenerator.Generate(lowLevelOnly, spec, token); var warnings = Generator.ApiGenerator.Warnings; if (warnings.Count > 0) { Console.WriteLine(); AnsiConsole.Write(new Rule("[b black on yellow] Specification warnings [/]").LeftJustified()); Console.WriteLine(); foreach (var warning in warnings.Distinct().OrderBy(w => w)) AnsiConsole.MarkupLine(" {0} [yellow] {1} [/] ", Emoji.Known.Warning, warning); } return 0; } private static bool Ask(string question, bool defaultAnswer = true) { if (!Interactive) return defaultAnswer; var answer = "invalid"; var defaultResponse = defaultAnswer ? "y" : "n"; while (answer != "y" && answer != "n" && answer != "") { Console.Write($"{question}[y/N] (default {defaultResponse}): "); answer = Console.ReadLine()?.Trim().ToLowerInvariant(); if (string.IsNullOrWhiteSpace(answer)) answer = defaultResponse; defaultAnswer = answer == "y"; } return defaultAnswer; } } }