Current section

Files

Jump to
absinthe lib absinthe phase schema mark_referenced.ex
Raw

lib/absinthe/phase/schema/mark_referenced.ex

defmodule Absinthe.Phase.Schema.MarkReferenced do
@moduledoc false
use Absinthe.Phase
alias Absinthe.Blueprint.{Schema, TypeReference}
def run(blueprint, _opts) do
%{schema_definitions: [schema]} = blueprint
schema =
Map.update!(
schema,
:type_definitions,
&mark_referenced(&1, schema.directive_definitions, schema.schema_declaration)
)
{:ok, %{blueprint | schema_definitions: [schema]}}
end
defp mark_referenced(type_defs, directive_defs, schema_declaration) do
types_by_ref =
Enum.reduce(type_defs, %{}, fn type_def, acc ->
acc
|> Map.put(type_def.identifier, type_def)
|> Map.put(type_def.name, type_def)
end)
referenced_type_ids =
schema_declaration.field_definitions
|> Enum.map(&Map.get(types_by_ref, &1.identifier))
|> Enum.reject(&is_nil/1)
|> Enum.concat(directive_defs)
|> Enum.reduce(MapSet.new(), &referenced_types(&1, types_by_ref, &2))
for type <- type_defs do
if type.identifier in referenced_type_ids do
put_in(type.__private__[:__absinthe_referenced__], true)
else
type
end
end
end
defp referenced_types(%Schema.InputValueDefinition{type: type}, types, acc) do
referenced_types(type, types, acc)
end
defp referenced_types(%Schema.DirectiveDefinition{} = type, types, acc) do
type.arguments
|> Enum.reduce(acc, &referenced_types(&1, types, &2))
end
defp referenced_types(%Schema.EnumTypeDefinition{identifier: identifier}, _types, acc) do
MapSet.put(acc, identifier)
end
defp referenced_types(%Schema.FieldDefinition{} = field, types, acc) do
acc =
field.arguments
|> Enum.reduce(acc, &referenced_types(&1, types, &2))
referenced_types(field.type, types, acc)
end
defp referenced_types(
%Schema.InputObjectTypeDefinition{identifier: identifier} = input_object,
types,
acc
) do
if identifier in acc do
acc
else
acc = MapSet.put(acc, identifier)
input_object.fields
|> Enum.reduce(acc, &referenced_types(&1, types, &2))
end
end
defp referenced_types(
%Schema.InterfaceTypeDefinition{identifier: identifier} = interface,
schema,
acc
) do
if identifier in acc do
acc
else
acc = MapSet.put(acc, identifier)
acc =
interface
|> Schema.InterfaceTypeDefinition.find_implementors(Map.values(schema))
|> Enum.reduce(acc, &referenced_types(&1, schema, &2))
interface.fields
|> Enum.reduce(acc, &referenced_types(&1, schema, &2))
end
end
defp referenced_types(%TypeReference.List{of_type: inner_type}, schema, acc) do
referenced_types(inner_type, schema, acc)
end
defp referenced_types(%TypeReference.NonNull{of_type: inner_type}, schema, acc) do
referenced_types(inner_type, schema, acc)
end
defp referenced_types(
%Schema.ObjectTypeDefinition{identifier: identifier} = object,
schema,
acc
) do
if identifier in acc do
acc
else
acc = MapSet.put(acc, identifier)
acc =
object.fields
|> Enum.reduce(acc, &referenced_types(&1, schema, &2))
object.interfaces
|> Enum.reduce(acc, &referenced_types(&1, schema, &2))
end
end
defp referenced_types(%Schema.ScalarTypeDefinition{identifier: identifier}, _schema, acc) do
MapSet.put(acc, identifier)
end
defp referenced_types(%Schema.UnionTypeDefinition{identifier: identifier} = union, schema, acc) do
if identifier in acc do
acc
else
acc = MapSet.put(acc, identifier)
union.types
|> Enum.reduce(acc, &referenced_types(&1, schema, &2))
end
end
defp referenced_types(%TypeReference.Identifier{} = ref, schema, acc) do
referenced_types(Map.fetch!(schema, ref.id), schema, acc)
end
defp referenced_types(%TypeReference.Name{} = ref, schema, acc) do
referenced_types(Map.fetch!(schema, ref.name), schema, acc)
end
defp referenced_types(type, schema, acc) when is_atom(type) and type != nil do
referenced_types(Map.fetch!(schema, type), schema, acc)
end
end