mark_referenced.ex (4142B)
1 defmodule Absinthe.Phase.Schema.MarkReferenced do 2 @moduledoc false 3 4 use Absinthe.Phase 5 6 alias Absinthe.Blueprint.{Schema, TypeReference} 7 8 def run(blueprint, _opts) do 9 %{schema_definitions: [schema]} = blueprint 10 11 schema = 12 Map.update!(schema, :type_definitions, &mark_referenced(&1, schema.directive_definitions)) 13 14 {:ok, %{blueprint | schema_definitions: [schema]}} 15 end 16 17 @roots [:query, :mutation, :subscription] 18 defp mark_referenced(type_defs, directive_defs) do 19 types_by_ref = 20 Enum.reduce(type_defs, %{}, fn type_def, acc -> 21 acc 22 |> Map.put(type_def.identifier, type_def) 23 |> Map.put(type_def.name, type_def) 24 end) 25 26 referenced_type_ids = 27 @roots 28 |> Enum.map(&Map.get(types_by_ref, &1)) 29 |> Enum.reject(&is_nil/1) 30 |> Enum.concat(directive_defs) 31 |> Enum.reduce(MapSet.new(), &referenced_types(&1, types_by_ref, &2)) 32 33 for type <- type_defs do 34 if type.identifier in referenced_type_ids do 35 put_in(type.__private__[:__absinthe_referenced__], true) 36 else 37 type 38 end 39 end 40 end 41 42 defp referenced_types(%Schema.InputValueDefinition{type: type}, types, acc) do 43 referenced_types(type, types, acc) 44 end 45 46 defp referenced_types(%Schema.DirectiveDefinition{} = type, types, acc) do 47 type.arguments 48 |> Enum.reduce(acc, &referenced_types(&1, types, &2)) 49 end 50 51 defp referenced_types(%Schema.EnumTypeDefinition{identifier: identifier}, _types, acc) do 52 MapSet.put(acc, identifier) 53 end 54 55 defp referenced_types(%Schema.FieldDefinition{} = field, types, acc) do 56 acc = 57 field.arguments 58 |> Enum.reduce(acc, &referenced_types(&1, types, &2)) 59 60 referenced_types(field.type, types, acc) 61 end 62 63 defp referenced_types( 64 %Schema.InputObjectTypeDefinition{identifier: identifier} = input_object, 65 types, 66 acc 67 ) do 68 if identifier in acc do 69 acc 70 else 71 acc = MapSet.put(acc, identifier) 72 73 input_object.fields 74 |> Enum.reduce(acc, &referenced_types(&1, types, &2)) 75 end 76 end 77 78 defp referenced_types( 79 %Schema.InterfaceTypeDefinition{identifier: identifier} = interface, 80 schema, 81 acc 82 ) do 83 if identifier in acc do 84 acc 85 else 86 acc = MapSet.put(acc, identifier) 87 88 acc = 89 interface 90 |> Schema.InterfaceTypeDefinition.find_implementors(Map.values(schema)) 91 |> Enum.reduce(acc, &referenced_types(&1, schema, &2)) 92 93 interface.fields 94 |> Enum.reduce(acc, &referenced_types(&1, schema, &2)) 95 end 96 end 97 98 defp referenced_types(%TypeReference.List{of_type: inner_type}, schema, acc) do 99 referenced_types(inner_type, schema, acc) 100 end 101 102 defp referenced_types(%TypeReference.NonNull{of_type: inner_type}, schema, acc) do 103 referenced_types(inner_type, schema, acc) 104 end 105 106 defp referenced_types( 107 %Schema.ObjectTypeDefinition{identifier: identifier} = object, 108 schema, 109 acc 110 ) do 111 if identifier in acc do 112 acc 113 else 114 acc = MapSet.put(acc, identifier) 115 116 acc = 117 object.fields 118 |> Enum.reduce(acc, &referenced_types(&1, schema, &2)) 119 120 object.interfaces 121 |> Enum.reduce(acc, &referenced_types(&1, schema, &2)) 122 end 123 end 124 125 defp referenced_types(%Schema.ScalarTypeDefinition{identifier: identifier}, _schema, acc) do 126 MapSet.put(acc, identifier) 127 end 128 129 defp referenced_types(%Schema.UnionTypeDefinition{identifier: identifier} = union, schema, acc) do 130 if identifier in acc do 131 acc 132 else 133 acc = MapSet.put(acc, identifier) 134 135 union.types 136 |> Enum.reduce(acc, &referenced_types(&1, schema, &2)) 137 end 138 end 139 140 defp referenced_types(%TypeReference.Identifier{} = ref, schema, acc) do 141 referenced_types(Map.fetch!(schema, ref.id), schema, acc) 142 end 143 144 defp referenced_types(%TypeReference.Name{} = ref, schema, acc) do 145 referenced_types(Map.fetch!(schema, ref.name), schema, acc) 146 end 147 148 defp referenced_types(type, schema, acc) when is_atom(type) and type != nil do 149 referenced_types(Map.fetch!(schema, type), schema, acc) 150 end 151 end