diff --git a/lib/absinthe/phase/schema/compile.ex b/lib/absinthe/phase/schema/compile.ex index 5046119371..a76a166e63 100644 --- a/lib/absinthe/phase/schema/compile.ex +++ b/lib/absinthe/phase/schema/compile.ex @@ -8,7 +8,10 @@ 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 = @@ -16,19 +19,35 @@ defmodule Absinthe.Phase.Schema.Compile do {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) @@ -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: @@ -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 ]) diff --git a/lib/absinthe/schema/persistent_term.ex b/lib/absinthe/schema/persistent_term.ex index 7effeaef11..db230e30f1 100644 --- a/lib/absinthe/schema/persistent_term.ex +++ b/lib/absinthe/schema/persistent_term.ex @@ -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() @@ -68,6 +81,7 @@ 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 @@ -75,6 +89,17 @@ if Code.ensure_loaded?(:persistent_term) do |> 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 diff --git a/test/absinthe/introspection_test.exs b/test/absinthe/introspection_test.exs index c2e5dd1eba..c9c1606cd7 100644 --- a/test/absinthe/introspection_test.exs +++ b/test/absinthe/introspection_test.exs @@ -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 + + 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 =