ShowOpenApiDocumentAsync(HidiOptions options, ILogger logger, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(options.OpenApi) && string.IsNullOrEmpty(options.Csdl))
+ {
+ throw new ArgumentException("Please input a file path or URL");
+ }
+
+ var openApiFormat = options.OpenApiFormat ?? (!string.IsNullOrEmpty(options.OpenApi) ? GetOpenApiFormat(options.OpenApi, logger) : OpenApiConstants.Yaml);
+ var document = await GetOpenApiAsync(options, openApiFormat, logger, null, cancellationToken).ConfigureAwait(false);
+ if (document is not null)
+ {
+ using (logger.BeginScope("Creating diagram"))
+ {
+ // If output is null, create a HTML file in the user's temporary directory
+ var sourceUrl = (string.IsNullOrEmpty(options.OpenApi), string.IsNullOrEmpty(options.Csdl)) switch
+ {
+ (false, _) => options.OpenApi!,
+ (_, false) => options.Csdl!,
+ _ => throw new InvalidOperationException("No input file path or URL provided")
+ };
+ if (options.Output == null)
+ {
+ var tempPath = Path.GetTempPath() + "/hidi/";
+ if (!File.Exists(tempPath))
+ {
+ Directory.CreateDirectory(tempPath);
+ }
+
+ var fileName = Path.GetRandomFileName();
+
+ var output = new FileInfo(Path.Combine(tempPath, fileName + ".html"));
+ using (var file = new FileStream(output.FullName, FileMode.Create))
+ {
+ using var writer = new StreamWriter(file);
+ WriteTreeDocumentAsHtml(sourceUrl, document, writer);
+ }
+ logger.LogTrace("Created Html document with diagram ");
+
+ // Launch a browser to display the output html file
+ using var process = new Process();
+ process.StartInfo.FileName = output.FullName;
+ process.StartInfo.UseShellExecute = true;
+ process.Start();
+
+ return output.FullName;
+ }
+ else // Write diagram as Markdown document to output file
+ {
+ using var file = new FileStream(options.Output.FullName, FileMode.Create);
+ using var writer = new StreamWriter(file);
+ WriteTreeDocumentAsMarkdown(sourceUrl, document, writer);
+
+ logger.LogTrace("Created markdown document with diagram ");
+ return options.Output.FullName;
+ }
+ }
+ }
+ }
+ catch (TaskCanceledException)
+ {
+ await Console.Error.WriteLineAsync("CTRL+C pressed, aborting the operation.").ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Could not generate the document, reason: {ex.Message}", ex);
+ }
+ return null;
+ }
+
+ private static void LogErrors(ILogger logger, ReadResult result)
+ {
+ var context = result.Diagnostic;
+ if (context is { Errors.Count: > 0 })
+ {
+ using (logger.BeginScope("Detected errors"))
+ {
+ foreach (var error in context.Errors)
+ {
+ logger.LogError("Detected error during parsing: {Error}", error.ToString());
+ }
+ }
+ }
+ }
+
+ private static void LogWarnings(ILogger logger, ReadResult result)
+ {
+ var context = result.Diagnostic;
+ if (context is { Warnings.Count: > 0 })
+ {
+ using (logger.BeginScope("Detected warnings"))
+ {
+ foreach (var warning in context.Warnings)
+ {
+ logger.LogWarning("Detected warning during parsing: {Warning}", warning.ToString());
+ }
+ }
+ }
+ }
+
+ internal static void WriteTreeDocumentAsMarkdown(string openapiUrl, OpenApiDocument document, StreamWriter writer)
+ {
+ var rootNode = OpenApiUrlTreeNode.Create(document, "main");
+
+ writer.WriteLine("# " + document.Info.Title);
+ writer.WriteLine();
+ writer.WriteLine("API Description: " + openapiUrl);
+
+ writer.WriteLine(@"");
+ // write a span for each mermaidcolorscheme
+ foreach (var style in OpenApiUrlTreeNode.MermaidNodeStyles)
+ {
+ writer.WriteLine($"{style.Key.Replace("_", " ", StringComparison.OrdinalIgnoreCase)}");
+ }
+ writer.WriteLine("
");
+ writer.WriteLine();
+ writer.WriteLine("```mermaid");
+ rootNode.WriteMermaid(writer);
+ writer.WriteLine("```");
+ }
+
+ internal static void WriteTreeDocumentAsHtml(string sourceUrl, OpenApiDocument document, StreamWriter writer, bool asHtmlFile = false)
+ {
+ var rootNode = OpenApiUrlTreeNode.Create(document, "main");
+
+ writer.WriteLine(
+ """
+
+
+
+
+
+
+
+
+ """);
+ writer.WriteLine("" + document?.Info.Title + "
");
+ writer.WriteLine();
+ writer.WriteLine($"");
+
+ writer.WriteLine(@"");
+ // write a span for each mermaidcolorscheme
+ foreach (var style in OpenApiUrlTreeNode.MermaidNodeStyles)
+ {
+ writer.WriteLine($"{style.Key.Replace("_", " ", StringComparison.OrdinalIgnoreCase)}");
+ }
+
+ writer.WriteLine("
");
+ writer.WriteLine("
");
+ writer.WriteLine("");
+ rootNode.WriteMermaid(writer);
+ writer.WriteLine("");
+
+ // Write script tag to include JS library for rendering markdown
+ writer.WriteLine(
+ """
+
+ """);
+ // Write script tag to include JS library for rendering mermaid
+ writer.WriteLine(" OpenApiDescriptionOption = new("--openapi", "-d")
+ {
+ Description = "Input OpenAPI description file path or URL",
+ };
+ public readonly Option CsdlOption = new("--csdl", "--cs")
+ {
+ Description = "Input CSDL file path or URL",
+ };
+ public readonly Option CsdlFilterOption = new("--csdl-filter", "--csf")
+ {
+ Description = "Comma delimited list of EntitySets or Singletons to filter CSDL on. e.g. tasks,accounts",
+ };
+ public readonly Option OutputOption = new("--output", "-o")
+ {
+ Description = "The output file path for the generated file.",
+ Arity = ArgumentArity.ZeroOrOne
+ };
+ public readonly Option OutputFolderOption = new("--output-folder", "--of")
+ {
+ Description = "The output directory path for the generated files.",
+ Arity = ArgumentArity.ZeroOrOne
+ };
+ public readonly Option CleanOutputOption = new("--clean-output", "--co")
+ {
+ Description = "Overwrite an existing file",
+ };
+ public readonly Option VersionOption = new("--version", "-v")
+ {
+ Description = "OpenAPI specification version",
+ };
+ public readonly Option MetadataVersionOption = new("--metadata-version", "--mv")
+ {
+ Description = "Graph metadata version to use.",
+ };
+ public readonly Option FormatOption = new("--format", "-f")
+ {
+ Description = "File format",
+ };
+ public readonly Option TerseOutputOption = new("--terse-output", "--to")
+ {
+ Description = "Produce terse json output",
+ };
+ public readonly Option SettingsFileOption = new("--settings-path", "--sp")
+ {
+ Description = "The configuration file with CSDL conversion settings.",
+ };
+ public readonly Option LogLevelOption = new("--log-level", "--ll")
+ {
+ Description = "The log level to use when logging messages to the main output.",
+ DefaultValueFactory = (_) => LogLevel.Information,
+ };
+ public readonly Option FilterByOperationIdsOption = new("--filter-by-operationids", "--op")
+ {
+ Description = "Filters OpenApiDocument by comma delimited list of OperationId(s) provided",
+ };
+ public readonly Option FilterByTagsOption = new("--filter-by-tags", "-t")
+ {
+ Description = "Filters OpenApiDocument by comma delimited list of Tag(s) provided. Also accepts a single regex.",
+ };
+ public readonly Option FilterByCollectionOption = new("--filter-by-collection", "-c")
+ {
+ Description = "Filters OpenApiDocument by Postman collection provided. Provide path to collection file.",
+ };
+ public readonly Option ManifestOption = new("--manifest", "-m")
+ {
+ Description = "Path to API manifest file to locate and filter an OpenApiDocument",
+ };
+ public readonly Option InlineLocalOption = new("--inline-local", "--il")
+ {
+ Description = "Inline local $ref instances",
+ };
+ public readonly Option InlineExternalOption = new("--inline-external", "--ie")
+ {
+ Description = "Inline external $ref instances",
+ };
+
+ public IReadOnlyList