missing_literals.ex (4115B)
1 defmodule Absinthe.Phase.Document.MissingLiterals do 2 @moduledoc false 3 4 # Fills out missing arguments and input object fields. 5 # 6 # Filling out means inserting a stubbed `Input.Argument` or `Input.Field` struct. 7 # 8 # Only those arguments which are non null and / or have a default value are filled 9 # out. 10 # 11 # If an argument or input object field is non null and missing, it is marked invalid 12 13 use Absinthe.Phase 14 alias Absinthe.{Blueprint, Type} 15 16 @spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} 17 def run(input, _options \\ []) do 18 node = Blueprint.prewalk(input, &populate_node(&1, input.adapter, input.schema)) 19 {:ok, node} 20 end 21 22 defp populate_node(%{schema_node: nil} = node, _adapter, _schema), do: node 23 24 defp populate_node( 25 %{arguments: arguments, schema_node: %{args: schema_args}} = node, 26 adapter, 27 schema 28 ) do 29 arguments = 30 fill_missing_nodes( 31 Blueprint.Input.Argument, 32 arguments, 33 schema_args, 34 node.source_location, 35 adapter, 36 schema 37 ) 38 39 %{node | arguments: arguments} 40 end 41 42 defp populate_node( 43 %Blueprint.Input.Object{fields: fields, schema_node: %{fields: schema_fields}} = node, 44 adapter, 45 schema 46 ) do 47 fields = 48 fill_missing_nodes( 49 Blueprint.Input.Field, 50 fields, 51 schema_fields, 52 node.source_location, 53 adapter, 54 schema 55 ) 56 57 %{node | fields: fields} 58 end 59 60 defp populate_node( 61 %Blueprint.Input.Object{schema_node: %{of_type: type}} = node, 62 adapter, 63 schema 64 ) do 65 %{node | schema_node: type} 66 |> populate_node(adapter, schema) 67 end 68 69 defp populate_node(node, _adapter, _schema), do: node 70 71 defp fill_missing_nodes(type, arguments, schema_args, source_location, adapter, schema) do 72 missing_schema_args = find_missing_schema_nodes(arguments, schema_args) 73 74 missing_schema_args 75 |> Map.values() 76 |> Enum.reduce(arguments, fn 77 # If it's deprecated without a default, ignore it 78 %{deprecation: %{}, default_value: nil}, arguments -> 79 arguments 80 81 # If it has a default value, we want it. 82 %{default_value: val} = schema_node, arguments when not is_nil(val) -> 83 arg = build_node(type, schema_node, val, source_location, adapter, schema) 84 [arg | arguments] 85 86 # It isn't deprecated, it is null, and there's no default value. It's missing 87 %{type: %Type.NonNull{}} = missing_mandatory_arg_schema_node, arguments -> 88 arg = 89 type 90 |> build_node( 91 missing_mandatory_arg_schema_node, 92 missing_mandatory_arg_schema_node.default_value, 93 source_location, 94 adapter, 95 schema 96 ) 97 |> flag_invalid(:missing) 98 99 [arg | arguments] 100 101 # No default value, and it's allowed to be null. Ignore it. 102 _, arguments -> 103 arguments 104 end) 105 end 106 107 # Given the set of possible schema args, return only those not supplied in 108 # the document argument / fields 109 defp find_missing_schema_nodes(nodes, schema_nodes) do 110 nodes 111 |> Enum.filter(& &1.schema_node) 112 |> Enum.reduce(schema_nodes, fn 113 %{schema_node: %{identifier: id}}, acc -> 114 Map.delete(acc, id) 115 116 _, acc -> 117 acc 118 end) 119 end 120 121 defp build_node(type, schema_node_arg, default, source_location, adapter, schema) do 122 struct!(type, %{ 123 name: schema_node_arg.name |> build_name(adapter, type), 124 input_value: %Blueprint.Input.Value{ 125 data: default, 126 normalized: 127 if(is_nil(default), do: nil, else: %Blueprint.Input.Generated{by: __MODULE__}), 128 raw: nil, 129 schema_node: Type.expand(schema_node_arg.type, schema) 130 }, 131 schema_node: schema_node_arg, 132 source_location: source_location 133 }) 134 end 135 136 defp build_name(name, adapter, Blueprint.Input.Argument) do 137 adapter.to_external_name(name, :argument) 138 end 139 140 defp build_name(name, adapter, Blueprint.Input.Field) do 141 adapter.to_external_name(name, :field) 142 end 143 end