result.ex (3996B)
1 defmodule Absinthe.Phase.Document.Result do 2 @moduledoc false 3 4 # Produces data fit for external encoding from annotated value tree 5 6 alias Absinthe.{Blueprint, Phase, Type} 7 use Absinthe.Phase 8 9 @spec run(Blueprint.t() | Phase.Error.t(), Keyword.t()) :: {:ok, map} 10 def run(%Blueprint{} = bp, _options \\ []) do 11 result = Map.merge(bp.result, process(bp)) 12 {:ok, %{bp | result: result}} 13 end 14 15 defp process(blueprint) do 16 result = 17 case blueprint.execution do 18 %{validation_errors: [], result: nil} -> 19 {:ok, data(%{value: nil}, [])} 20 21 %{validation_errors: [], result: result} -> 22 {:ok, data(result, [])} 23 24 %{validation_errors: errors} -> 25 {:validation_failed, errors} 26 end 27 28 format_result(result) 29 end 30 31 defp format_result({:ok, {data, []}}) do 32 %{data: data} 33 end 34 35 defp format_result({:ok, {data, errors}}) do 36 errors = errors |> Enum.uniq() |> Enum.map(&format_error/1) 37 %{data: data, errors: errors} 38 end 39 40 defp format_result({:validation_failed, errors}) do 41 errors = errors |> Enum.uniq() |> Enum.map(&format_error/1) 42 %{errors: errors} 43 end 44 45 defp data(%{errors: [_ | _] = field_errors}, errors), do: {nil, field_errors ++ errors} 46 47 # Leaf 48 defp data(%{value: nil}, errors), do: {nil, errors} 49 50 defp data(%{value: value, emitter: emitter}, errors) do 51 value = 52 case Type.unwrap(emitter.schema_node.type) do 53 %Type.Scalar{} = schema_node -> 54 try do 55 Type.Scalar.serialize(schema_node, value) 56 rescue 57 _e in [Absinthe.SerializationError, Protocol.UndefinedError] -> 58 raise( 59 Absinthe.SerializationError, 60 """ 61 Could not serialize term #{inspect(value)} as type #{schema_node.name} 62 63 When serializing the field: 64 #{emitter.parent_type.name}.#{emitter.schema_node.name} (#{ 65 emitter.schema_node.__reference__.location.file 66 }:#{emitter.schema_node.__reference__.location.line}) 67 """ 68 ) 69 end 70 71 %Type.Enum{} = schema_node -> 72 Type.Enum.serialize(schema_node, value) 73 end 74 75 {value, errors} 76 end 77 78 # Object 79 defp data(%{fields: fields}, errors), do: field_data(fields, errors) 80 81 # List 82 defp data(%{values: values}, errors), do: list_data(values, errors) 83 84 defp list_data(fields, errors, acc \\ []) 85 defp list_data([], errors, acc), do: {:lists.reverse(acc), errors} 86 87 defp list_data([%{errors: errs} = field | fields], errors, acc) do 88 {value, errors} = data(field, errors) 89 list_data(fields, errs ++ errors, [value | acc]) 90 end 91 92 defp field_data(fields, errors, acc \\ []) 93 defp field_data([], errors, acc), do: {Map.new(acc), errors} 94 95 defp field_data([%Absinthe.Resolution{} = res | _], _errors, _acc) do 96 raise """ 97 Found unresolved resolution struct! 98 99 You probably forgot to run the resolution phase again. 100 101 #{inspect(res)} 102 """ 103 end 104 105 defp field_data([field | fields], errors, acc) do 106 {value, errors} = data(field, errors) 107 field_data(fields, errors, [{field_name(field.emitter), value} | acc]) 108 end 109 110 defp field_name(%{alias: nil, name: name}), do: name 111 defp field_name(%{alias: name}), do: name 112 defp field_name(%{name: name}), do: name 113 114 defp format_error(%Phase.Error{locations: []} = error) do 115 error_object = %{message: error.message} 116 Map.merge(error.extra, error_object) 117 end 118 119 defp format_error(%Phase.Error{} = error) do 120 error_object = %{ 121 message: error.message, 122 locations: Enum.flat_map(error.locations, &format_location/1) 123 } 124 125 error_object = 126 case error.path do 127 [] -> error_object 128 path -> Map.put(error_object, :path, path) 129 end 130 131 Map.merge(Map.new(error.extra), error_object) 132 end 133 134 defp format_location(%{line: line, column: col}) do 135 [%{line: line || 0, column: col || 0}] 136 end 137 138 defp format_location(_), do: [] 139 end