Packages
ash_graphql
0.15.2
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/ash_graphql.ex
defmodule AshGraphql do
@moduledoc """
AshGraphql is a graphql front extension for the Ash framework.
See the [getting started guide](/getting_started.md) for information on setting it up, and
see the `AshGraphql.Resource` documentation for docs on its DSL
"""
defmacro __using__(opts) do
quote bind_quoted: [apis: opts[:apis], api: opts[:api]] do
apis =
api
|> List.wrap()
|> Kernel.++(List.wrap(apis))
apis =
apis
|> Enum.map(&{&1, false})
|> List.update_at(0, fn {api, _} -> {api, true} end)
for {api, first?} <- apis do
defmodule Module.concat(api, AshTypes) do
@moduledoc false
alias Absinthe.{Blueprint, Phase, Pipeline}
Code.ensure_compiled(api)
def pipeline(pipeline) do
Pipeline.insert_before(
pipeline,
Absinthe.Phase.Schema.ApplyDeclaration,
__MODULE__
)
end
def run(blueprint, _opts) do
api = unquote(api)
Code.ensure_compiled(api)
blueprint_with_queries =
api
|> AshGraphql.Api.queries(__MODULE__)
|> Enum.reduce(blueprint, fn query, blueprint ->
Absinthe.Blueprint.add_field(blueprint, "RootQueryType", query)
end)
blueprint_with_mutations =
api
|> AshGraphql.Api.mutations(__MODULE__)
|> Enum.reduce(blueprint_with_queries, fn mutation, blueprint ->
Absinthe.Blueprint.add_field(blueprint, "RootMutationType", mutation)
end)
type_definitions =
if unquote(first?) do
embedded_types = AshGraphql.get_embedded_types(unquote(apis))
AshGraphql.Api.global_type_definitions(__MODULE__) ++
AshGraphql.Api.type_definitions(api, __MODULE__) ++
embedded_types
else
AshGraphql.Api.type_definitions(api, __MODULE__)
end
new_defs =
List.update_at(blueprint_with_mutations.schema_definitions, 0, fn schema_def ->
%{
schema_def
| type_definitions: schema_def.type_definitions ++ type_definitions
}
end)
{:ok, %{blueprint_with_mutations | schema_definitions: new_defs}}
end
end
if first? do
import_types(Absinthe.Type.Custom)
import_types(AshGraphql.Types.JSON)
end
@pipeline_modifier Module.concat(api, AshTypes)
end
end
end
def get_embedded_types(apis) do
apis
|> Enum.map(&elem(&1, 0))
|> Enum.flat_map(&Ash.Api.resources/1)
|> Enum.flat_map(fn resource ->
resource
|> Ash.Resource.Info.public_attributes()
|> Enum.map(&{resource, &1})
end)
|> Enum.filter(fn {_resource, attribute} ->
Ash.Type.embedded_type?(attribute.type)
end)
|> Enum.map(fn
{source_resource, attribute} ->
{source_resource, attribute, embedded_resource(attribute.type)}
end)
|> Enum.flat_map(fn {source_resource, attribute, embedded} ->
[{source_resource, attribute, embedded}] ++ get_nested_embedded_types(embedded)
end)
|> Enum.flat_map(fn {source_resource, attribute, embedded_type} ->
if AshGraphql.Resource.type(embedded_type) do
[
AshGraphql.Resource.type_definition(
embedded_type,
Module.concat(embedded_type, ShadowApi),
__MODULE__
),
AshGraphql.Resource.embedded_type_input(
source_resource,
attribute,
embedded_type,
__MODULE__
)
]
else
[
AshGraphql.Resource.embedded_type_input(
source_resource,
attribute,
embedded_type,
__MODULE__
)
]
end
end)
end
defp embedded_resource({:array, type}), do: embedded_resource(type)
defp embedded_resource(type), do: type
defp get_nested_embedded_types(embedded_type) do
embedded_type
|> Ash.Resource.Info.public_attributes()
|> Enum.filter(&Ash.Type.embedded_type?(&1.type))
|> Enum.map(fn attribute ->
{attribute, embedded_resource(attribute.type)}
end)
|> Enum.flat_map(fn {attribute, embedded} ->
[{embedded_type, attribute, embedded}] ++ get_nested_embedded_types(embedded)
end)
end
def add_context(ctx, apis) do
dataloader =
apis
|> List.wrap()
|> Enum.reduce(Dataloader.new(), fn api, dataloader ->
Dataloader.add_source(
dataloader,
api,
AshGraphql.Dataloader.new(api)
)
end)
Map.put(ctx, :loader, dataloader)
end
end