Current section
Files
Jump to
Current section
Files
lib/mix/tasks/censer.gen.graphql.function.ex
defmodule Mix.Tasks.Censer.Gen.Graphql.Function.Docs do
@moduledoc false
@spec short_doc() :: String.t()
def short_doc do
"Generates an Elixir function with a pattern-matching head from a GraphQL query."
end
@spec example() :: String.t()
def example do
"mix censer.gen.graphql.function MyApp.UserContext priv/queries/get_user.graphql"
end
@spec long_doc() :: String.t()
def long_doc do
"""
#{short_doc()}
This task parses a GraphQL `.graphql` file and injects a new function into the
specified module. The function's name is derived from the GraphQL operation name,
and its argument is a map pattern that matches the exact shape of the query's selection set.
## Usage
Pass the target module name and the relative path to your GraphQL file:
```sh
#{example()}
```
## Generated Code Shape
Given a query:
```graphql
query GetUser { user { id email } }
```
Censer will generate:
```elixir
def handle_get_user(%{"user" => %{"id" => id, "email" => email}}) do
# ...
end
```
"""
end
end
defmodule Mix.Tasks.Censer.Gen.Graphql.Function do
@shortdoc "#{__MODULE__.Docs.short_doc()}"
@moduledoc __MODULE__.Docs.long_doc()
use Igniter.Mix.Task
@impl Igniter.Mix.Task
def info(_argv, _parent) do
%Igniter.Mix.Task.Info{
positional: [:module, :graphql_file_path]
}
end
@impl Igniter.Mix.Task
def igniter(igniter) do
%{module: module, graphql_file_path: file_path} = igniter.args.positional
target_module = Igniter.Project.Module.parse(module)
if File.exists?(file_path) do
query = File.read!(file_path)
do_igniter(igniter, target_module, query)
else
Igniter.add_issue(igniter, "Query file not found at: #{file_path}")
end
end
@spec do_igniter(Igniter.t(), module(), String.t()) :: Igniter.t()
defp do_igniter(igniter, target_module, query) do
{function_name, function_ast} =
build_function_ast(query)
igniter
|> ensure_module_exists(target_module)
|> Igniter.Project.Module.find_and_update_module(target_module, fn zipper ->
case Igniter.Code.Function.move_to_def(zipper, function_name, 1) do
{:ok, _zipper} ->
{:ok, zipper}
:error ->
{:ok, Igniter.Code.Common.add_code(zipper, function_ast)}
end
end)
|> case do
{:ok, igni} -> igni
{:error, igni} -> Igniter.add_issue(igni, "There was an error in the generation")
end
end
defp ensure_module_exists(igniter, module) do
case Igniter.Project.Module.find_module(igniter, module) do
{:ok, {new_igniter, _, _zipper}} ->
new_igniter
{:error, new_igniter} ->
Igniter.Project.Module.create_module(new_igniter, module, "")
end
end
defp build_function_ast(query) do
{function_name, pattern_ast} = Censer.Graphql.parse_and_build(query)
ast =
quote do
def unquote(function_name)(unquote(pattern_ast)) do
:ok
end
end
{function_name, ast}
end
end