mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
chore: remove docs writer (#9494)
This was added in an earlier era and will be just too cumbersome to maintain going forward. We have OpenAPI docs which are more flexible.
This commit is contained in:
@@ -78,7 +78,6 @@ defmodule Web.MixProject do
|
||||
# Test deps
|
||||
{:floki, "~> 0.37.0", only: :test},
|
||||
{:bypass, "~> 2.1", only: :test},
|
||||
{:bureaucrat, "~> 0.2.9", only: :test},
|
||||
{:wallaby, "~> 0.30.0", only: :test},
|
||||
{:credo, "~> 1.5", only: [:dev, :test], runtime: false},
|
||||
{:dialyxir, "~> 1.1", only: [:dev], runtime: false},
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
defmodule Web.Documentation.Generator do
|
||||
alias Domain.Config.Definition
|
||||
|
||||
@keep_req_headers ["authorization"]
|
||||
@keep_resp_headers ["content-type", "location"]
|
||||
|
||||
def write(conns, path) do
|
||||
write_config_doc!(Domain.Config.Definitions, "../../www/docs/reference/env-vars.mdx")
|
||||
File.mkdir_p!(path)
|
||||
write_api_doc!(conns, path)
|
||||
end
|
||||
|
||||
def write_config_doc!(module, file_path) do
|
||||
file = File.open!(file_path, [:write, :utf8])
|
||||
|
||||
w!(file, "---")
|
||||
|
||||
w!(
|
||||
file,
|
||||
docusaurus_header(
|
||||
title: "Environment Variables",
|
||||
sidebar_position: 1
|
||||
)
|
||||
)
|
||||
|
||||
w!(file, "---")
|
||||
w!(file, "")
|
||||
|
||||
with {:ok, doc} <- Definition.fetch_doc(module) do
|
||||
w!(file, doc)
|
||||
end
|
||||
|
||||
w!(file, "## Environment Variable Listing")
|
||||
w!(file, "We recommend setting these in your Docker ENV file (`$HOME/.firezone/.env` by")
|
||||
w!(file, "default). Required fields in **bold**.")
|
||||
|
||||
keys =
|
||||
Enum.flat_map(module.doc_sections(), fn
|
||||
{header, description, keys} ->
|
||||
w_env_vars!(file, module, header, description, keys)
|
||||
keys
|
||||
|
||||
{header, keys} ->
|
||||
w_env_vars!(file, module, header, nil, keys)
|
||||
keys
|
||||
end)
|
||||
|
||||
all_keys = module.configs() |> Enum.map(&elem(&1, 1))
|
||||
w_env_vars!(file, module, "Other", nil, all_keys -- keys)
|
||||
end
|
||||
|
||||
defp w_env_vars!(_file, _module, _header, _description, []), do: :ok
|
||||
|
||||
defp w_env_vars!(file, module, header, description, keys) do
|
||||
w!(file, "")
|
||||
w!(file, "### #{header}")
|
||||
if description, do: w!(file, description)
|
||||
|
||||
w!(file, "")
|
||||
w!(file, "| Env Key | Description | Format | Default |")
|
||||
w!(file, "| ------ | --------------- | ------ | ------- |")
|
||||
|
||||
for key <- keys do
|
||||
with {:ok, doc} <- Definition.fetch_doc(module, key) do
|
||||
{type, {resolve_opts, _validate_opts, _dump_opts, _debug_opts}} =
|
||||
Definition.fetch_spec_and_opts!(module, key)
|
||||
|
||||
default = Keyword.get(resolve_opts, :default)
|
||||
required? = if Keyword.has_key?(resolve_opts, :default), do: false, else: true
|
||||
|
||||
key = Domain.Config.Resolver.env_key(key)
|
||||
key = if required?, do: "**#{key}**", else: key
|
||||
|
||||
doc = doc_env(doc)
|
||||
|
||||
{type, default} = type_and_default(type, default)
|
||||
|
||||
w!(file, "| #{key} | #{doc} | #{type} | #{default} |")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp doc_env(doc) do
|
||||
doc
|
||||
|> String.trim()
|
||||
|> String.replace("\n * `", "<br /> - `")
|
||||
|> String.replace("```json", "```")
|
||||
|> String.replace("\n\n", "<br /> <br />")
|
||||
|> String.replace("\n", " ")
|
||||
end
|
||||
|
||||
defp type_and_default(type, default) when is_function(default),
|
||||
do: type_and_default(type, "generated")
|
||||
|
||||
defp type_and_default(type, nil),
|
||||
do: type_and_default(type, "")
|
||||
|
||||
defp type_and_default(type, []),
|
||||
do: type_and_default(type, "[]")
|
||||
|
||||
defp type_and_default({:parameterized, {Ecto.Enum, opts}}, default) do
|
||||
values =
|
||||
opts.mappings
|
||||
|> Keyword.keys()
|
||||
|> Enum.map(&to_string/1)
|
||||
|> Enum.map_join(", ", &"`#{&1}`")
|
||||
|
||||
default =
|
||||
default
|
||||
|> Atom.to_string()
|
||||
|
||||
{"One of #{values}", "`#{default}`"}
|
||||
end
|
||||
|
||||
defp type_and_default(Domain.Types.CIDR, default),
|
||||
do: {"CIDR", default}
|
||||
|
||||
defp type_and_default(Domain.Types.IP, default),
|
||||
do: {"IP", default}
|
||||
|
||||
defp type_and_default(Domain.Types.IPPort, default),
|
||||
do: {"IP with port", default}
|
||||
|
||||
defp type_and_default(:integer, default),
|
||||
do: {"integer", default}
|
||||
|
||||
defp type_and_default(:string, default),
|
||||
do: {"string", default}
|
||||
|
||||
defp type_and_default(:boolean, default),
|
||||
do: {"boolean", default}
|
||||
|
||||
defp type_and_default(:map, default),
|
||||
do: {"JSON-encoded map", "`" <> Jason.encode!(default) <> "`"}
|
||||
|
||||
defp type_and_default(:embed, default),
|
||||
do: {"JSON-encoded map", "`" <> Jason.encode!(default) <> "`"}
|
||||
|
||||
defp type_and_default({:one_of, types}, default) do
|
||||
types =
|
||||
types
|
||||
|> Enum.map(&type_and_default(&1, default))
|
||||
|> Enum.map(&elem(&1, 0))
|
||||
|> Enum.map(&to_string/1)
|
||||
|> Enum.map_join(", ", &"`#{&1}`")
|
||||
|
||||
{"one of #{types}", default}
|
||||
end
|
||||
|
||||
defp type_and_default({:json_array, _}, default),
|
||||
do: {"JSON-encoded list", "`" <> Jason.encode!(default) <> "`"}
|
||||
|
||||
defp type_and_default({:array, separator, type}, default) do
|
||||
{type, default} = type_and_default(type, default)
|
||||
{"a list of #{type} separated by `#{separator}`", default}
|
||||
end
|
||||
|
||||
defp type_and_default(type, default) when not is_binary(default),
|
||||
do: type_and_default(type, inspect(default))
|
||||
|
||||
defp type_and_default(type, default),
|
||||
do: {inspect(type), "`" <> default <> "`"}
|
||||
|
||||
defp write_api_doc!(conns, path) do
|
||||
routes = Phoenix.Router.routes(List.first(conns).private.phoenix_router)
|
||||
|
||||
conns
|
||||
|> Enum.group_by(& &1.private.phoenix_controller)
|
||||
|> Enum.map(fn {controller, conns} ->
|
||||
{module_doc, module_assigns, function_docs} = fetch_module_docs!(controller)
|
||||
|
||||
title =
|
||||
Keyword.get_lazy(module_assigns, :title, fn ->
|
||||
controller
|
||||
|> to_string()
|
||||
|> String.split(".")
|
||||
|> List.last()
|
||||
|> String.replace_trailing("Controller", "")
|
||||
end)
|
||||
|
||||
path = Path.join(path, "#{String.downcase(title)}.mdx")
|
||||
file = File.open!(path, [:write, :utf8])
|
||||
|
||||
w!(file, "---")
|
||||
w!(file, docusaurus_header(module_assigns))
|
||||
w!(file, "---")
|
||||
w!(file, "\n")
|
||||
w!(file, module_doc)
|
||||
w!(file, "## API Documentation")
|
||||
|
||||
conns
|
||||
|> Enum.group_by(& &1.private.phoenix_action)
|
||||
# We order actions nicely
|
||||
|> Enum.sort_by(fn
|
||||
{:index, _} -> 1
|
||||
{:show, _} -> 3
|
||||
{:create, _} -> 2
|
||||
{:update, _} -> 4
|
||||
{:delete, _} -> 5
|
||||
{_other, _} -> 1000
|
||||
end)
|
||||
|> Enum.map(fn {action, conns} ->
|
||||
{path, verb} = fetch_route!(routes, controller, action)
|
||||
{function_doc, function_assigns} = get_function_docs(function_docs, action)
|
||||
|
||||
title = maybe_wrap(function_assigns[:action], "#{verb} #{path}")
|
||||
|
||||
w!(file, "### #{title}")
|
||||
w!(file, "\n")
|
||||
w!(file, function_doc)
|
||||
|
||||
uri_params = build_uri_params(path)
|
||||
|
||||
write_examples(file, conns, path, uri_params)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp maybe_wrap(nil, title), do: title
|
||||
defp maybe_wrap(action_assign, title), do: "#{action_assign} [`#{title}`]"
|
||||
|
||||
defp docusaurus_header(assigns) do
|
||||
assigns
|
||||
|> Enum.map_join("\n", fn {key, value} ->
|
||||
"#{key}: #{value}"
|
||||
end)
|
||||
end
|
||||
|
||||
defp fetch_route!(routes, controller, controller_action) do
|
||||
%{path: path, verb: verb} =
|
||||
Enum.find(routes, fn
|
||||
%{plug: ^controller, plug_opts: ^controller_action} -> true
|
||||
_other -> false
|
||||
end)
|
||||
|
||||
path = String.replace(path, ~r|:([^/]*)|, "{\\1}")
|
||||
verb = verb |> to_string() |> String.upcase()
|
||||
|
||||
{path, verb}
|
||||
end
|
||||
|
||||
defp fetch_module_docs!(controller) do
|
||||
case Code.fetch_docs(controller) do
|
||||
{:docs_v1, _, _, _, module_doc, %{api_doc: module_assigns}, function_docs} ->
|
||||
{get_doc(module_doc), module_assigns, function_docs}
|
||||
|
||||
{:error, :module_not_found} ->
|
||||
raise "No module #{controller}"
|
||||
end
|
||||
end
|
||||
|
||||
defp get_doc(md) when is_map(md), do: Map.get(md, "en")
|
||||
defp get_doc(_md), do: nil
|
||||
|
||||
defp get_function_docs(function_docs, function) do
|
||||
function_docs
|
||||
|> Enum.find(fn
|
||||
{{:function, ^function, _}, _, _, _, _} -> true
|
||||
{{:function, _function, _}, _, _, _, _} -> false
|
||||
end)
|
||||
|> case do
|
||||
{_, _, _, :none, %{api_doc: function_assigns}} ->
|
||||
{nil, function_assigns}
|
||||
|
||||
{_, _, _, doc, %{api_doc: function_assigns}} ->
|
||||
{get_doc(doc), function_assigns}
|
||||
|
||||
{_, _, _, doc, _chunks} ->
|
||||
{get_doc(doc), %{}}
|
||||
|
||||
_other ->
|
||||
{nil, %{}}
|
||||
end
|
||||
end
|
||||
|
||||
defp build_uri_params(path) do
|
||||
Regex.scan(~r/{([^}]*)}/, path)
|
||||
|> Enum.map(fn [_, param] ->
|
||||
param
|
||||
end)
|
||||
end
|
||||
|
||||
defp write_examples(file, conns, path, uri_params) do
|
||||
conns
|
||||
|> Enum.sort_by(& &1.status)
|
||||
|> Enum.each(fn conn ->
|
||||
example_description = conn.assigns.bureaucrat_opts[:example_description] || "Example"
|
||||
w!(file, "#### #{example_description}")
|
||||
|
||||
w_req_uri_params!(file, conn, uri_params)
|
||||
|
||||
w!(
|
||||
file,
|
||||
"""
|
||||
```bash
|
||||
$ curl -i \\
|
||||
-X #{conn.method} "https://{firezone_host}#{path}" \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
"""
|
||||
|> String.trim_trailing()
|
||||
)
|
||||
|
||||
maybe_w!(file, b_req_headers(conn))
|
||||
maybe_w!(file, b_req_body(conn.body_params))
|
||||
|
||||
w!(file, "")
|
||||
|
||||
w!(file, "HTTP/1.1 #{conn.status}")
|
||||
maybe_w!(file, b_resp_headers(conn))
|
||||
maybe_w!(file, b_resp_body(conn.resp_body))
|
||||
w!(file, "```")
|
||||
end)
|
||||
end
|
||||
|
||||
defp w_req_uri_params!(_file, _conn, []), do: :ok
|
||||
|
||||
defp w_req_uri_params!(file, conn, params) do
|
||||
w!(file, "**URI Parameters:**\n")
|
||||
|
||||
Enum.each(params, fn param ->
|
||||
w!(file, i(1, "- `#{param}`: `#{conn.params[param]}`"))
|
||||
end)
|
||||
end
|
||||
|
||||
defp b_req_headers(conn) do
|
||||
for {key, value} <- conn.req_headers, key in @keep_req_headers do
|
||||
case {key, value} do
|
||||
{"authorization", "bearer " <> _} ->
|
||||
i(1, "-H 'Authorization: Bearer {api_token}' \\")
|
||||
|
||||
{key, value} ->
|
||||
i(1, "-H '#{camelize_header_key(key)}: #{value}' \\")
|
||||
end
|
||||
end
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
|
||||
defp b_req_body(params) when params == %{}, do: ""
|
||||
|
||||
defp b_req_body(params) do
|
||||
i(1, "--data-binary @- << EOF\n#{Jason.encode!(params, pretty: true)}\nEOF")
|
||||
end
|
||||
|
||||
defp b_resp_headers(conn) do
|
||||
for {key, value} <- conn.resp_headers, key in @keep_resp_headers do
|
||||
"#{camelize_header_key(key)}: #{value}"
|
||||
end
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
|
||||
defp b_resp_body(resp_body) do
|
||||
case Jason.decode(resp_body) do
|
||||
{:ok, map} ->
|
||||
"\n" <> Jason.encode!(map, pretty: true)
|
||||
|
||||
_error ->
|
||||
resp_body
|
||||
end
|
||||
end
|
||||
|
||||
defp camelize_header_key(key) do
|
||||
key
|
||||
|> String.split("-")
|
||||
|> Enum.map_join("-", fn
|
||||
<<first::utf8, rest::binary>> -> String.upcase(<<first::utf8>>) <> rest
|
||||
other -> other
|
||||
end)
|
||||
end
|
||||
|
||||
defp i(level, text) do
|
||||
String.duplicate(" ", level) <> text
|
||||
end
|
||||
|
||||
defp maybe_w!(_file, ""), do: :ok
|
||||
defp maybe_w!(_file, nil), do: :ok
|
||||
defp maybe_w!(file, text), do: w!(file, text)
|
||||
|
||||
defp w!(file, content) do
|
||||
IO.puts(file, content)
|
||||
end
|
||||
end
|
||||
@@ -1,12 +1,7 @@
|
||||
# Delete screenshots from previous acceptance test executions
|
||||
Path.join(File.cwd!(), "screenshots") |> File.rm_rf!()
|
||||
|
||||
Bureaucrat.start(
|
||||
writer: Web.Documentation.Generator,
|
||||
default_path: "../../www/docs/reference/rest-api"
|
||||
)
|
||||
|
||||
Finch.start_link(name: TestPool)
|
||||
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Domain.Repo, :manual)
|
||||
ExUnit.start(formatters: [ExUnit.CLIFormatter, JUnitFormatter, Bureaucrat.Formatter])
|
||||
ExUnit.start(formatters: [ExUnit.CLIFormatter, JUnitFormatter])
|
||||
|
||||
@@ -101,8 +101,6 @@ config :logger, level: :warning
|
||||
|
||||
config :argon2_elixir, t_cost: 1, m_cost: 8
|
||||
|
||||
config :bureaucrat, :json_library, Jason
|
||||
|
||||
config :wallaby,
|
||||
driver: Wallaby.Chrome,
|
||||
screenshot_on_failure: true,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "4.1.3", "4f28318286f89453364d7fbb53e03d4563fd7ed2438a60237eba5e426e97785f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7c295b8d8e0eaf6f43641698f962526cdf87c6feb7d14bd21e599271b510608c"},
|
||||
"bandit": {:hex, :bandit, "1.7.0", "d1564f30553c97d3e25f9623144bb8df11f3787a26733f00b21699a128105c0c", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3e2f7a98c7a11f48d9d8c037f7177cd39778e74d55c7af06fe6227c742a8168a"},
|
||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||
"bureaucrat": {:hex, :bureaucrat, "0.2.10", "b0de157dad540e40007b663b683f716ced21f85ff0591093aadb209ad0d967e1", [:mix], [{:inflex, ">= 1.10.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.2.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, ">= 1.0.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "bc7e5162b911c29c8ebefee87a2c16fbf13821a58f448a8fd024eb6c17fae15c"},
|
||||
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
|
||||
"castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"},
|
||||
"certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"},
|
||||
@@ -45,7 +44,6 @@
|
||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||
"httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
|
||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||
"junit_formatter": {:hex, :junit_formatter, "3.4.0", "d0e8db6c34dab6d3c4154c3b46b21540db1109ae709d6cf99ba7e7a2ce4b1ac2", [:mix], [], "hexpm", "bb36e2ae83f1ced6ab931c4ce51dd3dbef1ef61bb4932412e173b0cfa259dacd"},
|
||||
|
||||
Reference in New Issue
Block a user