Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix type resolution by recursing prototypes #1334

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions lib/absinthe/phase/schema/compile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,46 @@ defmodule Absinthe.Phase.Schema.Compile do

%{schema_definitions: [schema]} = blueprint

type_ast = build_types(schema.type_artifacts)
prototype_schema = Keyword.fetch!(opts, :prototype_schema)

type_ast = build_types(schema.type_artifacts, prototype_schema)

directive_ast = build_directives(schema.directive_artifacts)

type_list =
Map.new(schema.type_definitions, fn type_def ->
{type_def.identifier, type_def.name}
end)

type_list =
case prototype_schema do
Absinthe.Schema.Prototype ->
type_list

prototype_schema ->
Map.merge(type_list, prototype_schema.__absinthe_types__())
end

referenced_types =
for type_def <- schema.type_definitions,
type_def.__private__[:__absinthe_referenced__],
into: %{},
do: {type_def.identifier, type_def.name}

referenced_types =
case prototype_schema do
Absinthe.Schema.Prototype ->
referenced_types

prototype_schema ->
Map.merge(referenced_types, prototype_schema.__absinthe_types__(:referenced))
end

directive_list =
Map.new(schema.directive_artifacts, fn type_def ->
{type_def.identifier, type_def.name}
end)

prototype_schema = Keyword.fetch!(opts, :prototype_schema)

metadata = build_metadata(schema)

implementors = build_implementors(schema)
Expand Down Expand Up @@ -86,7 +105,7 @@ defmodule Absinthe.Phase.Schema.Compile do
end
end

def build_types(types) do
def build_types(types, prototype_schema) do
for type <- types do
if !type.definition,
do:
Expand All @@ -108,9 +127,13 @@ defmodule Absinthe.Phase.Schema.Compile do
end
end
|> Enum.concat([
quote do
def __absinthe_type__(_type) do
nil
if prototype_schema == Absinthe.Schema.Prototype do
quote do
def __absinthe_type__(_type), do: nil
end
else
quote do
def __absinthe_type__(type), do: unquote(prototype_schema).__absinthe_type__(type)
end
end
])
Expand Down
25 changes: 25 additions & 0 deletions lib/absinthe/schema/persistent_term.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,21 @@ if Code.ensure_loaded?(:persistent_term) do
|> get()
|> Map.fetch!(:__absinthe_type__)
|> Map.get(name)
|> __maybe_absinthe_type_from_prototype(name, schema_mod)
end

defp __maybe_absinthe_type_from_prototype(nil, name, schema_mod) do
prototype_schema_mod = schema_mod.__absinthe_prototype_schema__()

if prototype_schema_mod == Absinthe.Schema.Prototype do
nil
else
prototype_schema_mod.__absinthe_type__(name)
end
end

defp __maybe_absinthe_type_from_prototype(value, _, _), do: value

def __absinthe_directive__(schema_mod, name) do
schema_mod
|> get()
Expand All @@ -68,13 +81,25 @@ if Code.ensure_loaded?(:persistent_term) do
|> get()
|> Map.fetch!(:__absinthe_types__)
|> Map.fetch!(:referenced)
|> __maybe_merge_types_from_prototype(schema_mod, :referenced)
end

def __absinthe_types__(schema_mod, group) do
schema_mod
|> get()
|> Map.fetch!(:__absinthe_types__)
|> Map.fetch!(group)
|> __maybe_merge_types_from_prototype(schema_mod, group)
end

defp __maybe_merge_types_from_prototype(types, schema_mod, group) do
prototype_schema_mod = schema_mod.__absinthe_prototype_schema__()

if prototype_schema_mod == Absinthe.Schema.Prototype do
types
else
Map.merge(types, prototype_schema_mod.__absinthe_types__(group))
end
end

def __absinthe_directives__(schema_mod) do
Expand Down
154 changes: 154 additions & 0 deletions test/absinthe/introspection_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,160 @@ defmodule Absinthe.IntrospectionTest do
end
end

describe "introspection of complex directives" do
defmodule ComplexDirectiveSchema do
use Absinthe.Schema
use Absinthe.Fixture

defmodule Utils do
def parse(value), do: value
def serialize(value), do: value
end

defmodule ComplexDirectivePrototype do
use Absinthe.Schema.Prototype

jeffutter marked this conversation as resolved.
Show resolved Hide resolved
input_object :complex do
field :str, :string
end

scalar :normal_string, description: "string" do
parse &Utils.parse/1
serialize &Utils.serialize/1
end

scalar :_underscore_normal_string, name: "_UnderscoreNormalString" do
parse &Utils.parse/1
serialize &Utils.serialize/1
end

enum :color_channel do
description "The selected color channel"
value :red, as: :r, description: "Color Red"
value :green, as: :g, description: "Color Green"
value :blue, as: :b, description: "Color Blue"
end

directive :complex_directive do
arg :complex, :complex
arg :normal_string, :normal_string
arg :color_channel, :color_channel
arg :_underscore_normal_string, :_underscore_normal_string

on [:field]
end
end

@prototype_schema ComplexDirectivePrototype

query do
field :foo,
type: :string,
args: [],
resolve: fn _, _ -> {:ok, "foo"} end
end
end

test "renders type for complex directives" do
result =
"""
query IntrospectionQuery {
__schema {
types {
name
}
directives {
name
args {
name
description
type {
kind
name
}
defaultValue
}
}
}
}
"""
|> run(ComplexDirectiveSchema)

assert {:ok,
%{
data: %{
"__schema" => %{
"directives" => [
%{"name" => "complexDirective", "args" => complex_directive_args}
| _
],
"types" => types
}
}
}} = result

assert Enum.member?(
complex_directive_args,
%{
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "Complex"
},
"defaultValue" => nil,
"description" => nil,
"name" => "complex"
}
)

assert Enum.member?(types, %{"name" => "Complex"})

assert Enum.member?(
complex_directive_args,
%{
"type" => %{
"kind" => "SCALAR",
"name" => "NormalString"
},
"defaultValue" => nil,
"description" => nil,
"name" => "normalString"
}
)

assert Enum.member?(types, %{"name" => "NormalString"})

assert Enum.member?(
complex_directive_args,
%{
"type" => %{
"kind" => "ENUM",
"name" => "ColorChannel"
},
"defaultValue" => nil,
"description" => nil,
"name" => "colorChannel"
}
)

assert Enum.member?(types, %{"name" => "ColorChannel"})

assert Enum.member?(
complex_directive_args,
%{
"type" => %{
"kind" => "SCALAR",
"name" => "_UnderscoreNormalString"
},
"defaultValue" => nil,
"description" => nil,
"name" => "_underscoreNormalString"
}
)

assert Enum.member?(types, %{"name" => "_UnderscoreNormalString"})
end
end

describe "introspection of an enum type" do
test "can use __type and value information with deprecations" do
result =
Expand Down
Loading