hydrate.ex (6373B)
1 defmodule Absinthe.Phase.Schema.Hydrate do 2 @moduledoc false 3 @behaviour Absinthe.Schema.Hydrator 4 5 use Absinthe.Phase 6 alias Absinthe.Blueprint 7 8 @hydrate [ 9 Blueprint.Schema.DirectiveDefinition, 10 Blueprint.Schema.EnumTypeDefinition, 11 Blueprint.Schema.EnumValueDefinition, 12 Blueprint.Schema.FieldDefinition, 13 Blueprint.Schema.InputObjectTypeDefinition, 14 Blueprint.Schema.InputValueDefinition, 15 Blueprint.Schema.InterfaceTypeDefinition, 16 Blueprint.Schema.ObjectTypeDefinition, 17 Blueprint.Schema.ScalarTypeDefinition, 18 Blueprint.Schema.SchemaDefinition, 19 Blueprint.Schema.UnionTypeDefinition 20 ] 21 22 @impl Absinthe.Phase 23 def run(blueprint, opts \\ []) do 24 {:ok, schema} = Keyword.fetch(opts, :schema) 25 hydrator = Keyword.get(opts, :hydrator, __MODULE__) 26 blueprint = Blueprint.prewalk(blueprint, &handle_node(&1, [], schema, hydrator)) 27 {:ok, blueprint} 28 end 29 30 defp handle_node(%Blueprint{} = node, ancestors, schema, hydrator) do 31 node 32 |> hydrate_node(ancestors, schema, hydrator) 33 |> set_children(ancestors, schema, hydrator) 34 end 35 36 defp handle_node(%node_module{} = node, ancestors, schema, hydrator) 37 when node_module in @hydrate do 38 node 39 |> hydrate_node(ancestors, schema, hydrator) 40 |> set_children(ancestors, schema, hydrator) 41 end 42 43 defp handle_node(node, ancestors, schema, hydrator) do 44 set_children(node, ancestors, schema, hydrator) 45 end 46 47 defp set_children(parent, ancestors, schema, hydrator) do 48 Blueprint.prewalk(parent, fn 49 ^parent -> parent 50 child -> {:halt, handle_node(child, [parent | ancestors], schema, hydrator)} 51 end) 52 end 53 54 defp hydrate_node(%{} = node, ancestors, schema, hydrator) do 55 hydrations = schema.hydrate(node, ancestors) 56 apply_hydrations(node, hydrations, hydrator) 57 end 58 59 defp apply_hydrations(node, hydrations, hydrator) do 60 hydrations 61 |> List.wrap() 62 |> Enum.reduce(node, fn hydration, node -> 63 hydrator.apply_hydration(node, hydration) 64 end) 65 end 66 67 @impl Absinthe.Schema.Hydrator 68 69 def apply_hydration( 70 node, 71 {:meta, keyword_list} 72 ) 73 when is_list(keyword_list) do 74 %{node | __private__: Keyword.put(node.__private__, :meta, keyword_list)} 75 end 76 77 def apply_hydration( 78 node, 79 {:description, text} 80 ) do 81 %{node | description: text} 82 end 83 84 def apply_hydration( 85 %Blueprint.Schema.FieldDefinition{} = node, 86 {:resolve, resolver} 87 ) do 88 %{node | middleware: [{Absinthe.Resolution, resolver}]} 89 end 90 91 def apply_hydration( 92 %Blueprint.Schema.FieldDefinition{} = node, 93 {:middleware, {_module, _opts} = middleware} 94 ) do 95 %{node | middleware: [middleware]} 96 end 97 98 def apply_hydration( 99 %Blueprint.Schema.FieldDefinition{} = node, 100 {:complexity, complexity} 101 ) 102 when is_integer(complexity) do 103 %{node | complexity: complexity} 104 end 105 106 def apply_hydration( 107 %Blueprint.Schema.ScalarTypeDefinition{} = node, 108 {:parse, parse} 109 ) 110 when is_function(parse) do 111 %{node | parse: parse} 112 end 113 114 def apply_hydration( 115 %Blueprint.Schema.ScalarTypeDefinition{} = node, 116 {:serialize, serialize} 117 ) 118 when is_function(serialize) do 119 %{node | serialize: serialize} 120 end 121 122 def apply_hydration( 123 %Blueprint.Schema.InterfaceTypeDefinition{} = node, 124 {:resolve_type, resolve_type} 125 ) 126 when is_function(resolve_type) do 127 %{node | resolve_type: resolve_type} 128 end 129 130 def apply_hydration( 131 %Blueprint.Schema.UnionTypeDefinition{} = node, 132 {:resolve_type, resolve_type} 133 ) 134 when is_function(resolve_type) do 135 %{node | resolve_type: resolve_type} 136 end 137 138 def apply_hydration( 139 %Blueprint.Schema.ObjectTypeDefinition{} = node, 140 {:is_type_of, is_type_of} 141 ) 142 when is_function(is_type_of) do 143 %{node | is_type_of: is_type_of} 144 end 145 146 def apply_hydration( 147 %Blueprint.Schema.EnumValueDefinition{} = node, 148 {:as, value} 149 ) do 150 %{node | value: value} 151 end 152 153 @hydration_level1 [ 154 Blueprint.Schema.DirectiveDefinition, 155 Blueprint.Schema.EnumTypeDefinition, 156 Blueprint.Schema.InputObjectTypeDefinition, 157 Blueprint.Schema.InterfaceTypeDefinition, 158 Blueprint.Schema.ObjectTypeDefinition, 159 Blueprint.Schema.ScalarTypeDefinition, 160 Blueprint.Schema.UnionTypeDefinition 161 ] 162 163 @hydration_level2 [ 164 Blueprint.Schema.FieldDefinition, 165 Blueprint.Schema.EnumValueDefinition 166 ] 167 168 @hydration_level3 [ 169 Blueprint.Schema.InputValueDefinition 170 ] 171 172 def apply_hydration(%Absinthe.Blueprint{} = root, %{} = sub_hydrations) do 173 {root, _} = 174 Blueprint.prewalk(root, nil, fn 175 %module{identifier: ident} = node, nil when module in @hydration_level1 -> 176 case Map.fetch(sub_hydrations, ident) do 177 :error -> 178 {node, nil} 179 180 {:ok, type_hydrations} -> 181 {apply_hydrations(node, type_hydrations, __MODULE__), nil} 182 end 183 184 node, nil -> 185 {node, nil} 186 end) 187 188 root 189 end 190 191 def apply_hydration(%module{} = root, %{} = sub_hydrations) 192 when module in @hydration_level1 do 193 {root, _} = 194 Blueprint.prewalk(root, nil, fn 195 %module{identifier: ident} = node, nil when module in @hydration_level2 -> 196 case Map.fetch(sub_hydrations, ident) do 197 :error -> 198 {node, nil} 199 200 {:ok, type_hydrations} -> 201 {apply_hydrations(node, type_hydrations, __MODULE__), nil} 202 end 203 204 node, nil -> 205 {node, nil} 206 end) 207 208 root 209 end 210 211 def apply_hydration(%module{} = root, %{} = sub_hydrations) 212 when module in @hydration_level2 do 213 {root, _} = 214 Blueprint.prewalk(root, nil, fn 215 %module{identifier: ident} = node, nil when module in @hydration_level3 -> 216 case Map.fetch(sub_hydrations, ident) do 217 :error -> 218 {node, nil} 219 220 {:ok, type_hydrations} -> 221 {apply_hydrations(node, type_hydrations, __MODULE__), nil} 222 end 223 224 node, nil -> 225 {node, nil} 226 end) 227 228 root 229 end 230 231 def apply_hydration(root, result) do 232 raise ArgumentError, """ 233 Invalid hydration! 234 235 #{inspect(result)} 236 237 is not a valid way to hydrate 238 239 #{inspect(root)} 240 """ 241 end 242 end