diff --git a/elixir/apps/web/mix.exs b/elixir/apps/web/mix.exs
index 7b2f7d291..528bfd25f 100644
--- a/elixir/apps/web/mix.exs
+++ b/elixir/apps/web/mix.exs
@@ -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},
diff --git a/elixir/apps/web/test/support/documentation/docs_generator.ex b/elixir/apps/web/test/support/documentation/docs_generator.ex
deleted file mode 100644
index e8e07def4..000000000
--- a/elixir/apps/web/test/support/documentation/docs_generator.ex
+++ /dev/null
@@ -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 * `", "
- `")
- |> String.replace("```json", "```")
- |> String.replace("\n\n", "
")
- |> 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
- <> -> String.upcase(<>) <> 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
diff --git a/elixir/apps/web/test/test_helper.exs b/elixir/apps/web/test/test_helper.exs
index 866707d21..578427f52 100644
--- a/elixir/apps/web/test/test_helper.exs
+++ b/elixir/apps/web/test/test_helper.exs
@@ -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])
diff --git a/elixir/config/test.exs b/elixir/config/test.exs
index 3d29ce99b..ac7ec17f9 100644
--- a/elixir/config/test.exs
+++ b/elixir/config/test.exs
@@ -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,
diff --git a/elixir/mix.lock b/elixir/mix.lock
index 14f58bd18..1bd080b22 100644
--- a/elixir/mix.lock
+++ b/elixir/mix.lock
@@ -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"},