type_references_exist.ex (3156B)
1 defmodule Absinthe.Phase.Schema.Validation.TypeReferencesExist do 2 @moduledoc false 3 4 use Absinthe.Phase 5 alias Absinthe.Blueprint 6 alias Absinthe.Blueprint.Schema 7 8 def run(blueprint, _opts) do 9 blueprint = Blueprint.prewalk(blueprint, &validate_schema/1) 10 11 {:ok, blueprint} 12 end 13 14 def validate_schema(%Schema.SchemaDefinition{} = schema) do 15 types = 16 schema.type_definitions 17 |> Enum.flat_map(&[&1.name, &1.identifier]) 18 |> MapSet.new() 19 20 schema = Blueprint.prewalk(schema, &validate_types(&1, types)) 21 {:halt, schema} 22 end 23 24 def validate_schema(node), do: node 25 26 def validate_types(%Blueprint.Schema.FieldDefinition{} = field, types) do 27 check_or_error(field, field.type, types) 28 end 29 30 def validate_types(%Blueprint.Schema.ObjectTypeDefinition{} = object, types) do 31 object 32 |> check_types(:interfaces, &check_or_error(&2, &1, types)) 33 |> check_types(:imports, fn {type, _}, obj -> check_or_error(obj, type, types) end) 34 end 35 36 def validate_types(%Blueprint.Schema.InterfaceTypeDefinition{} = interface, types) do 37 check_types(interface, :interfaces, &check_or_error(&2, &1, types)) 38 end 39 40 def validate_types(%Blueprint.Schema.InputObjectTypeDefinition{} = object, types) do 41 check_types(object, :imports, fn {type, _}, obj -> check_or_error(obj, type, types) end) 42 end 43 44 def validate_types(%Blueprint.Schema.InputValueDefinition{} = input, types) do 45 check_or_error(input, input.type, types) 46 end 47 48 def validate_types(%Blueprint.Schema.UnionTypeDefinition{} = union, types) do 49 check_types(union, :types, &check_or_error(&2, &1, types)) 50 end 51 52 @no_types [ 53 Blueprint.Schema.DirectiveDefinition, 54 Blueprint.Schema.EnumTypeDefinition, 55 Blueprint.Schema.EnumValueDefinition, 56 Blueprint.Schema.InterfaceTypeDefinition, 57 Blueprint.Schema.ObjectTypeDefinition, 58 Blueprint.Schema.ScalarTypeDefinition, 59 Blueprint.Schema.SchemaDefinition, 60 Blueprint.TypeReference.NonNull, 61 Blueprint.TypeReference.ListOf, 62 Absinthe.Blueprint.TypeReference.Name 63 ] 64 def validate_types(%struct{} = type, _) when struct in @no_types do 65 type 66 end 67 68 def validate_types(type, _) do 69 type 70 end 71 72 defp check_types(entity, key, fun) do 73 entity 74 |> Map.fetch!(key) 75 |> Enum.reduce(entity, fun) 76 end 77 78 defp check_or_error(thing, type, types) do 79 type = unwrap(type) 80 81 if type in types do 82 thing 83 else 84 put_error(thing, error(thing, type)) 85 end 86 end 87 88 defp unwrap(value) when is_binary(value) or is_atom(value) do 89 value 90 end 91 92 defp unwrap(%Absinthe.Blueprint.TypeReference.Name{name: name}) do 93 name 94 end 95 96 defp unwrap(type) do 97 unwrap_type = Absinthe.Blueprint.TypeReference.unwrap(type) 98 99 if unwrap_type == type do 100 type 101 else 102 unwrap(unwrap_type) 103 end 104 end 105 106 defp error(thing, type) do 107 artifact_name = String.capitalize(thing.name) 108 109 %Absinthe.Phase.Error{ 110 message: """ 111 In #{artifact_name}, #{inspect(type)} is not defined in your schema. 112 113 Types must exist if referenced. 114 """, 115 locations: [thing.__reference__.location], 116 phase: __MODULE__ 117 } 118 end 119 end