Packages

The package "AutoDocPackage" is used by the "Auto Doc" microservice for generating API documentation based on OpenApiSpex format for Elixir projects. and generate the API Docs directories and files filled with data, nested inside the respected `/documentation` directory.

Current section

Files

Jump to
auto_doc_package lib requests.ex
Raw

lib/requests.ex

defmodule AutoDocPackage.Requests do
@moduledoc """
The `AutoDocPackage.Requests` module is used for generating the `example_data.json` file
and the API Docs files based on the data provided by the `example_data.json` file.
The module is used for the communication between the `AutoDoc` microservice and your Elixir project.
More information about the `AutoDoc` microservice can be found at:
- [Auto Doc](https://auto-doc.fly.dev)
- [Github README](https://github.com/zen-dev-lab/auto_doc_package?tab=readme-ov-file#auto-doc-effortless-openapispex-documentation).
"""
# ## For Manual Tests via Terminal
# documentation_path = "auto_doc/lib/auto_doc_web/documentation"
# controller_path = "lib/auto_doc_web/controllers/page_controller.ex"
# AutoDocPackage.Requests.gen_example_data_file(documentation_path, controller_path)
# AutoDocPackage.Requests.gen_api_spex(:params)
# AutoDocPackage.Requests.gen_api_spex(:response)
# AutoDocPackage.Requests.gen_api_spex(:operations)
alias AutoDocPackage.Utils
@host "https://auto-doc.fly.dev"
@auth_api_path "/auth/api"
@doc """
Generate the `example_data.json` file containing the special keys(`__documentation_path__`, `__controller_path__`)
and the keys corresponding to the API actions which do not yet have Documentation.
## Extra Info:
- `example_data.json` is located under `/lib` directory of the project.
- the special keys MUST NOT be changed.
- the keys corresponding to the API actions are empty upon creation `{}` but
they should be filled by you(the user) with their respective HTTP Request/Response payload data.
- The data stored inside the `example_data.json` file is used for generating the API Docs files by the `gen_api_spex` function.
## Parameters
- `documentation_path` - The relative path to the documentation folder.
- `controller_path` - The relative path to the controller file.
## Returns
- `{:ok, "File formatted successfully."}` - If the file is successfully generated (since it's formatted in the end)
- `{:error, reason}` - If the file fails to be generated (`reason` here could be format failure or http response body/reason)
## Example
```elixir
iex> documentation_path = "auto_doc/lib/auto_doc_web/documentation"
iex> controller_path = "lib/auto_doc_web/controllers/page_controller.ex"
iex> AutoDocPackage.Requests.gen_example_data_file(documentation_path, controller_path)
{:ok, "File formatted successfully."}
```
"""
def gen_example_data_file(documentation_path, controller_path) do
documentation_path = Utils.parse_file_path(documentation_path)
controller_path = Utils.parse_file_path(controller_path)
controller_content = get_content(controller_path)
if is_nil(controller_content) do
{:error, "Controller file does not exist. Path: #{controller_path}"}
else
Utils.create_nested_directories("#{documentation_path}/dummy_file.ex")
operations_content =
documentation_path
|> Utils.operations_path(controller_path)
|> get_content()
request_body =
%{
"controller_path" => controller_path,
"documentation_path" => documentation_path,
"project_container_name" => Utils.project_container_name(),
"controller_content" => controller_content,
"operations_content" => operations_content
}
|> Jason.encode!()
url = "#{@host}#{@auth_api_path}/gen_example_data_file"
httpoison_post(url, request_body)
end
end
@doc """
Generate the API Docs files based on the data provided by the `example_data.json` file.
Used for generating the API Docs files for the Params, Response, and Operations. (+ Errors)
## Parameters
- `type` - The type of the API Docs file to be generated. Must be one of [:params, :response, :operations, "params", "response", "operations"].
## Returns
- `{:ok, "File formatted successfully."}` - If the file is successfully generated (since it's formatted in the end)
- `{:error, reason}` - If the file fails to be generated (`reason` here could be format failure or http response body/reason)
## Example
```elixir
iex> AutoDocPackage.Requests.gen_api_spex(:params)
{:ok, "File formatted successfully."}
```
"""
def gen_api_spex(type)
when type in [:params, :response, :operations, "params", "response", "operations"] do
do_gen_api_spex(type)
end
def gen_api_spex(_) do
{:error,
"Invalid type. Type must be one of [:params, :response, :operations, \"params\", \"response\", \"operations\"]"}
end
####################################
## GEN_API_SPEX Action Functions ##
####################################
defp do_gen_api_spex(type) do
example_data_file_path = "#{Utils.project_container_name()}/lib/example_data.json"
encoded_example_data_file_content = get_content(example_data_file_path)
if is_nil(encoded_example_data_file_content) do
{:error, "ERROR: Example Data file does not exist. Path: #{example_data_file_path}"}
else
write_modes = if type in [:params, :response, "params", "response"], do: [:append], else: []
do_gen_api_spex(type, encoded_example_data_file_content, write_modes)
end
end
# Common structure parts of the GEN_API_SPEX function patterns
defp do_gen_api_spex(type, encoded_example_data_file_content, write_modes) do
%{
"__documentation_path__" => documentation_path,
"__controller_path__" => controller_path
} = example_data_content = Jason.decode!(encoded_example_data_file_content)
table_schema_content =
documentation_path
|> Utils.find_schema_path(controller_path)
|> get_content()
request_body = %{
"has_base_error_files" =>
Utils.file_exists?("#{documentation_path}/errors/not_authenticated.ex"),
"has_error_files" =>
Utils.file_exists?("#{Utils.doc_path(documentation_path, controller_path)}/errors.ex"),
"type" => type,
"example_data_content" => example_data_content,
"table_schema_content" => table_schema_content,
"project_container_name" => Utils.project_container_name()
}
request_body =
if type in [:operations, "operations"] do
operations_specific_params(documentation_path, controller_path) |> Map.merge(request_body)
else
request_body
end
|> Jason.encode!()
url = "#{@host}#{@auth_api_path}/gen_api_spex"
httpoison_post(url, request_body, write_modes)
end
# `Request Body` params specific to the Operations HTTP Request.
defp operations_specific_params(documentation_path, controller_path) do
%{
"params_names" =>
Utils.doc_file_path(documentation_path, controller_path, "params.ex") |> get_names(),
"responses_names" =>
Utils.doc_file_path(documentation_path, controller_path, "responses.ex") |> get_names(),
"controller_content" => get_content(controller_path),
"operations_content" =>
Utils.doc_file_path(documentation_path, controller_path, "operations.ex") |> get_content()
}
end
################################
## Generic Private Functions ##
################################
# Get the content of a file if it exists. Otherwise, return nil.
defp get_content(path) do
if Utils.file_exists?(path) do
Utils.run_mix_format(path)
{:ok, content} = Utils.read_file(path)
content
else
nil
end
end
# Get the names of the modules in a file. Specifically designed for the Params/Response files in the documentation folder.
defp get_names(path) do
case Utils.read_file(path) do
{:ok, content} ->
~r/defmodule\s+(\S+)\s+do/
|> Regex.scan(content, capture: [1])
|> List.flatten()
|> Enum.map(&Enum.at(String.split(&1, "."), -1))
_ ->
[]
end
end
defp httpoison_post(url, request_body, write_modes \\ []) do
headers = http_req_headers()
case HTTPoison.post(url, request_body, headers) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, body}
%{"type" => _type, "data" => data} = Jason.decode!(body)
Enum.map(data, fn {k, v} ->
{file_path, content} = {k, v}
Utils.create_nested_directories(file_path)
Utils.write_file(file_path, content, write_modes)
Utils.run_mix_format(file_path)
end)
{:ok, %HTTPoison.Response{status_code: _status_code, body: body}} ->
{:error, body}
{:error, reason} ->
{:error, reason}
end
end
defp http_req_headers() do
content_type_header = {"Content-Type", "application/json"}
authorization_header = {"Authorization", "Bearer #{System.get_env("GITHUB_ACCESS_TOKEN")}"}
user_token_header = {"Token", System.get_env("AUTO_DOC_USER_TOKEN")}
[content_type_header, authorization_header, user_token_header]
end
end