From b6e2711b058136e88315c27e8aa3db1a82ae0fb8 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Thu, 31 Jul 2025 15:14:35 -0500 Subject: [PATCH 1/2] feat: New Mix Task for Generating Static Markdown Swagger Page for ExDoc --- lib/mix/tasks/openapi.static_docs.ex | 100 +++++++++++++++++++++++++++ lib/open_api_spex/export_spec.ex | 5 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 lib/mix/tasks/openapi.static_docs.ex diff --git a/lib/mix/tasks/openapi.static_docs.ex b/lib/mix/tasks/openapi.static_docs.ex new file mode 100644 index 00000000..4e118eb6 --- /dev/null +++ b/lib/mix/tasks/openapi.static_docs.ex @@ -0,0 +1,100 @@ +defmodule Mix.Tasks.Openapi.StaticDocs do + @shortdoc "Generate a static file that renders the OpenAPI spec using the Swagger UI" + @moduledoc """ + Generate a static file that renders the OpenAPI spec using the Swagger UI. Uses + assets from https://unpkg.com/swagger-ui-dist. + + ## Examples + + $ mix openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file swagger.md + $ mix openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file swagger.md --swagger-version 4.4.1 + + ## Usage with ExDoc + + To use with ExDoc, you can add the following to your `mix.exs` file: + + ```elixir + def docs do + [ + extras: ["guides/swagger.md"] + ] + end + + def aliases do + [ + "docs": [ + "cmd mkdir -p guides/", + "openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file guides/swagger.md --swagger-version 4.4.0", + "docs" + ] + ] + end + ``` + + ## Command line options + + Accepts all the same options as `Mix.Tasks.Openapi.Spec.Json` plus: + + * `--output-file` - The file to write the static file to (defaults to `swagger.md`) + + * `--swagger-version` - The version of the Swagger UI to use (defaults to `4.4.1`) + """ + use Mix.Task + + require Mix.Generator + + alias Mix.Tasks.Openapi.Spec.Json + + @recursive true + @default_static_filename "swagger.md" + @default_swagger_version "4.4.1" + + @static_template """ + # <%= spec_name %> + + + + + + +
+ + + + + """ + + @flags [ + output_file: :string, + swagger_version: :string, + spec: :string + ] + + @impl true + def run(argv) do + {opts, _, _} = OptionParser.parse(argv, switches: @flags) + + json_spec_filename = Json.run(argv) + + output_file = Keyword.get(opts, :output_file, @default_static_filename) + swagger_version = Keyword.get(opts, :swagger_version, @default_swagger_version) + + json_spec = File.read!(json_spec_filename) + + rendered_template = + EEx.eval_string(@static_template, + spec_name: opts[:spec], + json_spec: json_spec, + swagger_version: swagger_version + ) + + Mix.Generator.create_file(output_file, rendered_template, force: true, quiet: true) + end +end diff --git a/lib/open_api_spex/export_spec.ex b/lib/open_api_spex/export_spec.ex index 4e8c1ff7..77aae9e7 100644 --- a/lib/open_api_spex/export_spec.ex +++ b/lib/open_api_spex/export_spec.ex @@ -15,6 +15,7 @@ defmodule OpenApiSpex.ExportSpec do quiet: false end + @spec call(list(binary()), (any(), any() -> any()), String.t()) :: false | nil | String.t() def call(argv, encode_spec, default_filename) do opts = parse_options(argv, default_filename) @@ -86,7 +87,9 @@ defmodule OpenApiSpex.ExportSpec do dir -> Mix.Generator.create_directory(dir, quiet: opts.quiet) end - Mix.Generator.create_file(opts.filename, content, force: true, quiet: opts.quiet) + with true <- Mix.Generator.create_file(opts.filename, content, force: true, quiet: opts.quiet) do + opts.filename + end end defp find_spec(opts) do From b60666bcea94b9058fd22648bfaeca1905b488fa Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Fri, 1 Aug 2025 09:28:02 -0500 Subject: [PATCH 2/2] bump to latest swagger version --- lib/mix/tasks/openapi.static_docs.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mix/tasks/openapi.static_docs.ex b/lib/mix/tasks/openapi.static_docs.ex index 4e118eb6..acec79fa 100644 --- a/lib/mix/tasks/openapi.static_docs.ex +++ b/lib/mix/tasks/openapi.static_docs.ex @@ -7,7 +7,7 @@ defmodule Mix.Tasks.Openapi.StaticDocs do ## Examples $ mix openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file swagger.md - $ mix openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file swagger.md --swagger-version 4.4.1 + $ mix openapi.static_docs --spec PhoenixAppWeb.ApiSpec --output-file swagger.md --swagger-version 5.27.1 ## Usage with ExDoc @@ -37,7 +37,7 @@ defmodule Mix.Tasks.Openapi.StaticDocs do * `--output-file` - The file to write the static file to (defaults to `swagger.md`) - * `--swagger-version` - The version of the Swagger UI to use (defaults to `4.4.1`) + * `--swagger-version` - The version of the Swagger UI to use (defaults to `5.27.1`) """ use Mix.Task @@ -47,7 +47,7 @@ defmodule Mix.Tasks.Openapi.StaticDocs do @recursive true @default_static_filename "swagger.md" - @default_swagger_version "4.4.1" + @default_swagger_version "5.27.1" @static_template """ # <%= spec_name %>