Packages
ash_graphql
1.3.3
1.10.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.0
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.0
1.5.1
1.5.0
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.5
1.0.0-rc.4
retired
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.28.1
0.28.0
0.27.1
0.27.0
0.26.9
0.26.8
0.26.7
0.26.6
0.26.5
0.26.4
0.26.3
0.26.2
0.26.0
0.25.13
0.25.12
0.25.10
0.25.9
0.25.8
0.25.7
0.25.6
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.1
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.13
0.22.12
0.22.11
0.22.10
0.22.9
0.22.8
0.22.7
0.22.6
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.0
retired
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.20.0-rc.3
0.20.0-rc.2
0.20.0-rc.1
0.20.0-rc.0
0.19.0
0.18.0-rc0
0.17.5
0.17.5-rc0
0.17.4
0.17.2
0.17.1
0.17.0
0.16.28
0.16.27
0.16.26
0.16.25
0.16.24
0.16.23
0.16.22
0.16.21
0.16.20
0.16.18-rc5
0.16.18-rc4
0.16.18-rc3
0.16.18-rc2
0.16.18-rc1
0.16.18-rc0
0.16.17
0.16.16
0.16.15
0.16.14
0.16.13
0.16.12
0.16.11
0.16.10
0.16.9
0.16.8
0.16.7
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.10
0.15.9
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.5
0.12.4
0.12.3
0.12.1
0.12.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
0.1.2
The extension for building GraphQL APIs with Ash
Current section
Files
Jump to
Current section
Files
lib/igniter.ex
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