defmodule Mix.Tasks.SlackApiDocs.Verify do
use Mix.Task
@shortdoc "Verifies the local docs against the remote Slack Web API docs"
@moduledoc """
Verifies the local docs against the remote Slack Web API docs
## Usage
$ mix slack_api_docs.verify tmp/slack/docs
## Command line options
* `--concurrency 75` - default: 50, the amount of requests running in parallel
* `--quiet` - suppress all informational messages.
"""
alias Mix.SlackApiDocs.{MethodPage, Helpers, Request, HomePage}
@command_options [
concurrency: :integer,
quiet: :boolean
]
@default_opts [concurrency: 50, quiet: false]
@impl Mix.Task
def run(all_args) do
HTTPoison.start()
{original_opts, args, _} = OptionParser.parse(all_args, switches: @command_options)
opts = Keyword.merge(@default_opts, original_opts)
input_path = List.first(args) || raise "missing input path"
concurrency = opts[:concurrency]
original_shell = Mix.shell()
if opts[:quiet], do: Mix.shell(Mix.Shell.Quiet)
# Compare local files to remote methods
local_file_paths =
File.ls!(input_path)
|> Enum.filter(fn file_name -> String.ends_with?(file_name, "json") end)
|> Enum.map(fn file_name -> "#{input_path}/#{file_name}" end)
Request.get!("/methods")
|> HomePage.gather_methods!()
|> report_missing_local_methods!(local_file_paths)
# Generate files
Mix.shell().info("Validating local API docs")
local_file_paths
|> Helpers.partition_list(concurrency)
|> Enum.map(&enqueue_group/1)
|> Task.await_many(:infinity)
Mix.shell(original_shell)
end
defp compare_local_to_remote!(file_path) do
Mix.shell().info("Validating: #{file_path}")
local =
File.read!(file_path)
|> Jason.decode!()
remote =
MethodPage.gather!(%{
"link" => local["link"],
"description" => local["desc"],
"isDeprecated" => false,
"name" => local["name"]
})
|> Jason.encode!(pretty: true)
|> Jason.decode!()
if local == remote do
:ok
else
Mix.shell().error("Difference found between local: #{file_path} and remote.")
Mix.shell().error(
"Please run `mix slack_api_docs.gen.json #{Path.dirname(file_path)}` to generate new docs"
)
exit({:shutdown, 1})
end
end
defp enqueue_group(group) do
Task.async(fn ->
Enum.map(group, fn file_path -> compare_local_to_remote!(file_path) end)
end)
end
defp report_missing_local_methods!(remote_methods, local_file_paths) do
missing_methods =
remote_methods
|> Enum.filter(fn item ->
method_name = item["name"]
found? =
Enum.any?(local_file_paths, fn file_path ->
"#{method_name}.json" == Path.basename(file_path)
end)
found? == false
end)
|> Enum.map(fn item -> item["name"] end)
if Enum.empty?(missing_methods) do
:ok
else
file_path = List.first(local_file_paths) |> Path.dirname()
Mix.shell().error("""
Warning: Difference found between remote and local methods.
The methods that are missing locally are:
#{Enum.join(missing_methods, "\n ")}
Please run `mix slack_api_docs.gen.json #{file_path}` to generate new docs
""")
exit({:shutdown, 1})
end
end
end