mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-20 13:24:14 +12:00
c67b9f5ab6
resolves #201
most of the code is adapted from 104b8c199c
246 lines
7.9 KiB
Elixir
246 lines
7.9 KiB
Elixir
defmodule AshGraphql.Igniter do
|
|
@moduledoc "Codemods and utilities for working with AshGraphql & Igniter"
|
|
|
|
@doc "Returns the AshGraphql schema containing the domain in question, or a list of all AshGraphql schemas"
|
|
def find_schema(igniter, domain) do
|
|
{igniter, modules} = ash_graphql_schemas(igniter)
|
|
|
|
modules
|
|
|> Enum.find(fn module ->
|
|
with {:ok, {_igniter, _source, zipper}} <- Igniter.Code.Module.find_module(igniter, module),
|
|
{:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, AshGraphql),
|
|
{:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1),
|
|
{:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :domains),
|
|
{:ok, _zipper} <-
|
|
Igniter.Code.List.move_to_list_item(
|
|
zipper,
|
|
&Igniter.Code.Common.nodes_equal?(&1, domain)
|
|
) do
|
|
true
|
|
else
|
|
_ ->
|
|
false
|
|
end
|
|
end)
|
|
|> case do
|
|
nil ->
|
|
{:error, igniter, modules}
|
|
|
|
module ->
|
|
{:ok, igniter, module}
|
|
end
|
|
end
|
|
|
|
@doc "Sets up an absinthe schema for AshGraphql"
|
|
def setup_absinthe_schema(igniter, schema_name \\ nil) do
|
|
schema_name = schema_name || Igniter.Code.Module.module_name("GraphqlSchema")
|
|
|
|
{igniter, domains} = Ash.Domain.Igniter.list_domains(igniter)
|
|
|
|
{igniter, domains} =
|
|
Enum.reduce(domains, {igniter, []}, fn domain, {igniter, list} ->
|
|
case Spark.Igniter.has_extension(
|
|
igniter,
|
|
domain,
|
|
Ash.Domain,
|
|
:extensions,
|
|
AshGraphql.Domain
|
|
) do
|
|
{igniter, true} -> {igniter, [domain | list]}
|
|
{igniter, false} -> {igniter, list}
|
|
end
|
|
end)
|
|
|
|
{igniter, resources} = Ash.Resource.Igniter.list_resources(igniter)
|
|
|
|
{igniter, any_queries?} =
|
|
Enum.reduce_while(
|
|
Enum.map(resources, &{&1, [2, 3]}) ++ Enum.map(domains, &{&1, [3, 4]}),
|
|
{igniter, false},
|
|
fn {mod, arities}, {igniter, false} ->
|
|
with {:ok, {igniter, _source, zipper}} <-
|
|
Igniter.Code.Module.find_module(igniter, mod),
|
|
{:ok, zipper} <-
|
|
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
:graphql,
|
|
1
|
|
),
|
|
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper),
|
|
{:ok, zipper} <-
|
|
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
:queries,
|
|
1
|
|
),
|
|
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do
|
|
has_query? =
|
|
Enum.any?([:get, :read_one, :list, :action], fn query_name ->
|
|
match?(
|
|
{:ok, _},
|
|
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
query_name,
|
|
arities
|
|
)
|
|
)
|
|
end)
|
|
|
|
if has_query? do
|
|
{:halt, {igniter, true}}
|
|
else
|
|
{:cont, {igniter, false}}
|
|
end
|
|
else
|
|
_ ->
|
|
{:cont, {igniter, false}}
|
|
end
|
|
end
|
|
)
|
|
|
|
placeholder_query =
|
|
unless any_queries? do
|
|
~S'''
|
|
@desc """
|
|
Hello! This is a sample query to verify that AshGraphql has been set up correctly.
|
|
Remove me once you have a query of your own!
|
|
"""
|
|
field :say_hello, :string do
|
|
resolve fn _, _, _ ->
|
|
{:ok, "Hello from AshGraphql!"}
|
|
end
|
|
end
|
|
'''
|
|
end
|
|
|
|
igniter
|
|
|> Igniter.Code.Module.find_and_update_or_create_module(
|
|
schema_name,
|
|
"""
|
|
use Absinthe.Schema
|
|
use AshGraphql, domains: #{inspect(domains)}
|
|
|
|
import_types Absinthe.Plug.Types
|
|
|
|
query do
|
|
# Custom Absinthe queries can be placed here
|
|
#{placeholder_query}
|
|
end
|
|
|
|
mutation do
|
|
# Custom Absinthe mutations can be placed here
|
|
end
|
|
""",
|
|
fn zipper ->
|
|
# Should never get here
|
|
{:ok, zipper}
|
|
end
|
|
)
|
|
end
|
|
|
|
@doc "Sets up the phoenix module for AshGraphql"
|
|
def setup_phoenix(igniter, schema_name \\ nil) do
|
|
schema_name = schema_name || Igniter.Code.Module.module_name("GraphqlSchema")
|
|
|
|
case Igniter.Libs.Phoenix.select_router(igniter) do
|
|
{igniter, nil} ->
|
|
igniter
|
|
|> Igniter.add_warning("""
|
|
No Phoenix router found, skipping Phoenix installation.
|
|
|
|
See the Getting Started guide for instructions on installing AshGraphql with `plug`.
|
|
If you have yet to set up Phoenix, you'll have to do that manually and then rerun this installer.
|
|
""")
|
|
|
|
{igniter, router} ->
|
|
igniter
|
|
|> update_endpoints(router)
|
|
|> Igniter.Libs.Phoenix.add_pipeline(:graphql, "plug AshGraphql.Plug", router: router)
|
|
|> Igniter.Libs.Phoenix.add_scope(
|
|
"/gql",
|
|
"""
|
|
pipe_through [:graphql]
|
|
|
|
forward "/playground",
|
|
Absinthe.Plug.GraphiQL,
|
|
schema: Module.concat(["#{inspect(schema_name)}"]),
|
|
interface: :playground
|
|
|
|
forward "/",
|
|
Absinthe.Plug,
|
|
schema: Module.concat(["#{inspect(schema_name)}"])
|
|
""",
|
|
router: router
|
|
)
|
|
end
|
|
end
|
|
|
|
@doc "Returns all modules that `use AshGraphql`"
|
|
def ash_graphql_schemas(igniter) do
|
|
Igniter.Code.Module.find_all_matching_modules(igniter, fn _name, zipper ->
|
|
match?({:ok, _}, Igniter.Code.Module.move_to_use(zipper, AshGraphql))
|
|
end)
|
|
end
|
|
|
|
defp update_endpoints(igniter, router) do
|
|
{igniter, endpoints_that_need_parser} =
|
|
Igniter.Libs.Phoenix.endpoints_for_router(igniter, router)
|
|
|
|
Enum.reduce(endpoints_that_need_parser, igniter, fn endpoint, igniter ->
|
|
Igniter.Code.Module.find_and_update_module!(igniter, endpoint, fn zipper ->
|
|
case Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
:plug,
|
|
2,
|
|
&Igniter.Code.Function.argument_equals?(&1, 0, Plug.Parsers)
|
|
) do
|
|
{:ok, zipper} ->
|
|
with {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1),
|
|
{:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :parsers),
|
|
{:ok, zipper} <-
|
|
Igniter.Code.List.append_new_to_list(zipper, Absinthe.Plug.Parser) do
|
|
{:ok, zipper}
|
|
else
|
|
_ ->
|
|
{:warning,
|
|
"Could not add `Absinthe.Plug.Parser` to parsers in endpoint #{endpoint}. Please make this change manually."}
|
|
end
|
|
|
|
:error ->
|
|
case parser_location(zipper) do
|
|
{:ok, zipper} ->
|
|
{:ok,
|
|
Igniter.Code.Common.add_code(zipper, """
|
|
plug Plug.Parsers,
|
|
parsers: [:urlencoded, :multipart, :json, Absinthe.Plug.Parser],
|
|
pass: ["*/*"],
|
|
json_decoder: Jason
|
|
""")}
|
|
|
|
_ ->
|
|
{:warning,
|
|
"Could not add `Absinthe.Plug.Parser` to parsers in endpoint #{endpoint}. Please make this change manually."}
|
|
end
|
|
end
|
|
end)
|
|
end)
|
|
end
|
|
|
|
defp parser_location(zipper) do
|
|
with :error <-
|
|
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
:plug,
|
|
[1, 2],
|
|
&Igniter.Code.Function.argument_equals?(&1, 0, Plug.Telemetry)
|
|
),
|
|
:error <-
|
|
Igniter.Code.Function.move_to_function_call_in_current_scope(
|
|
zipper,
|
|
:plug,
|
|
[1, 2]
|
|
) do
|
|
Igniter.Code.Module.move_to_use(zipper, Phoenix.Endpoint)
|
|
end
|
|
end
|
|
end
|