Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/weaver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ defmodule Weaver do
}

def new(id) when is_binary(id), do: %__MODULE__{id: id}
def from(obj), do: new(Weaver.Resolvers.id_for(obj))
end

defmodule Marker do
Expand All @@ -37,21 +36,22 @@ defmodule Weaver do
data in `Weaver.Graph`.
"""

@enforce_keys [:type, :ref, :val]
defstruct @enforce_keys
@enforce_keys [:type, :ref]
defstruct @enforce_keys ++ [:val, :cursor]

@type t() :: %__MODULE__{
ref: any(),
val: any(),
cursor: any(),
type: :chunk_start | :chunk_end
}

def chunk_start(id, val) do
def chunk_start(id, val \\ nil) do
%__MODULE__{type: :chunk_start, ref: %Ref{id: id}, val: val}
end

def chunk_end(id, val) do
%__MODULE__{type: :chunk_end, ref: %Ref{id: id}, val: val}
def chunk_end(id, val, cursor) do
%__MODULE__{type: :chunk_end, ref: %Ref{id: id}, val: val, cursor: cursor}
end
end

Expand Down
41 changes: 38 additions & 3 deletions lib/weaver/absinthe/middleware/continue.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,31 @@ defmodule Weaver.Absinthe.Middleware.Continue do

# call resolver function only if this is the resolution part for the current step
def call(%{state: :suspended, acc: %{resolution: path}, path: path} = res, fun) do
order =
res.definition.schema_node
|> Absinthe.Type.meta()
|> Map.take([:ordered_by, :order, :unique])

type_name =
res.definition.schema_node.type
|> Absinthe.Type.unwrap()
|> Absinthe.Type.expand(res.schema)
|> Map.get(:name)

id_fun =
res.definition.schema_node.type
|> Absinthe.Type.unwrap()
|> Absinthe.Type.expand(res.schema)
|> id_fun_for()

id_for = fn obj -> "#{type_name}:#{id_fun.(obj)}" end

cache = res.context.cache
parent_ref = res.source && Ref.from(res.source)

parent_type_name = Map.get(res.parent_type, :name)
parent_id_fun = id_fun_for(res.parent_type)

parent_ref = res.source && Ref.new("#{parent_type_name}:#{parent_id_fun.(res.source)}")
[%Absinthe.Blueprint.Document.Field{name: field} | _] = path

Map.get(res.acc, __MODULE__, %__MODULE__{})
Expand All @@ -40,9 +63,11 @@ defmodule Weaver.Absinthe.Middleware.Continue do
|> Absinthe.Resolution.put_result({:ok, []})

step ->
resolved = fun.(step.prev_chunk_end)
prev_cursor = step.prev_chunk_end && step.prev_chunk_end.cursor
resolved = fun.(prev_cursor)

{value, meta, next} = Step.process_resolved(resolved, step, cache, parent_ref, field)
{value, meta, next} =
Step.process_resolved(resolved, step, cache, parent_ref, field, order, id_for)

%{
res
Expand All @@ -57,4 +82,14 @@ defmodule Weaver.Absinthe.Middleware.Continue do
def call(res, _fun) do
res
end

def id_fun_for(schema_type) do
schema_type
|> Absinthe.Type.meta(:weaver_id)
|> case do
nil -> &Weaver.Node.id_for/1
fun when is_function(fun) -> fun
key -> &Map.get(&1, key)
end
end
end
148 changes: 125 additions & 23 deletions lib/weaver/absinthe/phase/document/result.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ defmodule Weaver.Absinthe.Phase.Document.Result do
end
end

defp data([], parent, %{errors: [_ | _] = field_errors, emitter: emitter}, result) do
defp data([], parent_ref, %{errors: [_ | _] = field_errors, emitter: emitter}, result) do
Result.add_errors(
result,
Enum.map(field_errors, &{Ref.from(parent), field_name(emitter), &1})
Enum.map(field_errors, &{parent_ref, field_name(emitter), &1})
)
end

# Leaf
defp data(path, parent, %Leaf{value: nil, emitter: emitter} = field, result) do
defp data(path, parent_ref, %Leaf{value: nil, emitter: emitter} = field, result) do
if on_path?(field, path) do
Result.add_data(result, {Ref.from(parent), field_name(emitter), nil})
Result.add_data(result, {parent_ref, field_name(emitter), nil})
else
result
end
Expand All @@ -70,7 +70,7 @@ defmodule Weaver.Absinthe.Phase.Document.Result do
result
end

defp data(path, parent, %{value: value, emitter: emitter} = field, result) do
defp data(path, parent_ref, %{value: value, emitter: emitter} = field, result) do
if on_path?(field, path) do
value =
case Type.unwrap(emitter.schema_node.type) do
Expand All @@ -81,7 +81,7 @@ defmodule Weaver.Absinthe.Phase.Document.Result do
Type.Enum.serialize(schema_node, value)
end

Result.add_data(result, {Ref.from(parent), field_name(emitter), value})
Result.add_data(result, {parent_ref, field_name(emitter), value})
else
result
end
Expand All @@ -92,62 +92,58 @@ defmodule Weaver.Absinthe.Phase.Document.Result do
field_data(next_path(field, path), nil, fields, result)
end

defp data(path, nil, %{fields: fields, root_value: obj} = field, result) do
field_data(next_path(field, path), obj, fields, result)
defp data(path, nil, %{fields: fields} = field, result) do
field_data(next_path(field, path), to_ref(field), fields, result)
end

defp data(path, parent, %{fields: fields, emitter: emitter, root_value: obj} = field, result) do
defp data(path, parent_ref, %{fields: fields, emitter: emitter} = field, result) do
next_path = next_path(field, path)

if next_path do
result =
if next_path == [] do
Result.add_relation_data(result, {Ref.from(parent), field_name(emitter), [obj]})
Result.add_relation_data(result, {parent_ref, field_name(emitter), [to_ref(field)]})
else
result
end

field_data(next_path, obj, fields, result)
field_data(next_path, to_ref(field), fields, result)
else
result
end
end

# List
defp data(path, parent, %{values: values} = field, result) do
defp data(path, parent_ref, %{values: values} = field, result) do
if on_path?(field, path) do
case path do
[next, pos | rest] ->
val = Enum.at(values, pos)
data([next | rest], parent, val, result)
data([next | rest], parent_ref, val, result)

_ ->
Enum.reduce(values, result, fn val, acc ->
data(path, parent, val, acc)
end)
Enum.reduce(values, result, &data(path, parent_ref, &1, &2))
end

# Enum.reduce(values, result, &data(path, parent, &1, &2))
else
result
end
end

defp field_data(_path, _parent, [], result), do: result

defp field_data(path, parent, [%Absinthe.Resolution{} | fields], result) do
field_data(path, parent, fields, result)
defp field_data(path, parent_ref, [%Absinthe.Resolution{} | fields], result) do
field_data(path, parent_ref, fields, result)
end

defp field_data(path, parent, [field | fields], result) do
defp field_data(path, parent_ref, [field | fields], result) do
result =
if on_path?(field, path) do
data(path, parent, field, result)
data(path, parent_ref, field, result)
else
result
end

field_data(path, parent, fields, result)
field_data(path, parent_ref, fields, result)
end

defp field_name(%{alias: nil, name: name}), do: name
Expand All @@ -170,4 +166,110 @@ defmodule Weaver.Absinthe.Phase.Document.Result do
end

defp next_path(_field, []), do: []

defp to_ref(field) do
%Ref{id: "#{type_for(field)}:#{id_for(field)}"}
end

defp id_for(%{emitter: %{schema_node: %{type: %{of_type: schema_type}}}} = field) do
id_for(put_in(field.emitter.schema_node.type, schema_type))
end

defp id_for(%{emitter: %{schema_node: %{type: schema_type}}, root_value: obj}) do
id_fun =
schema_type
|> get_concrete_type(obj, %{schema: schema_type.definition})
|> Continue.id_fun_for()

id_fun.(obj)
end

defp type_for(%{emitter: %{schema_node: %{type: %{of_type: schema_type}}}} = field) do
type_for(put_in(field.emitter.schema_node.type, schema_type))
end

defp type_for(%{emitter: %{schema_node: %{type: schema_type}}, root_value: obj}) do
schema_type
|> get_concrete_type(obj, %{schema: schema_type.definition})
|> Map.get(:name)
end

defp get_concrete_type(%Type.Union{} = parent_type, source, res) do
# Type.Union.resolve_type(parent_type, source, res)
resolve_union_type(parent_type, source, res)
end

defp get_concrete_type(%Type.Interface{} = parent_type, source, res) do
# Type.Interface.resolve_type(parent_type, source, res)
resolve_interface_type(parent_type, source, res)
end

defp get_concrete_type(parent_type, _source, _res) do
parent_type
end

def resolve_union_type(type, object, env, opts \\ [lookup: true])

def resolve_union_type(%{types: types} = union, obj, env = %{schema: schema}, opts) do
if resolver = Type.function(union, :resolve_type) do
case resolver.(obj, env) do
nil ->
nil

ident when is_atom(ident) ->
if opts[:lookup] do
Absinthe.Schema.lookup_type(schema, ident)
else
ident
end
end
else
type_name =
Enum.find(types, fn
%{is_type_of: nil} ->
false

type ->
type = Absinthe.Schema.lookup_type(schema, type)
Absinthe.Type.function(type, :is_type_of).(obj)
end)

if opts[:lookup] do
Absinthe.Schema.lookup_type(schema, type_name)
else
type_name
end
end
end

def resolve_interface_type(type, obj, env, opts \\ [lookup: true])

def resolve_interface_type(interface, obj, env = %{schema: schema}, opts) do
implementors = Absinthe.Schema.implementors(schema, interface.identifier)

if resolver = Type.function(interface, :resolve_type) do
case resolver.(obj, env) do
nil ->
nil

ident when is_atom(ident) ->
if opts[:lookup] do
Absinthe.Schema.lookup_type(schema, ident)
else
ident
end
end
else
type_name =
Enum.find(implementors, fn type ->
Absinthe.Type.function(type, :is_type_of).(obj)
end)

if opts[:lookup] do
Absinthe.Schema.lookup_type(schema, type_name)
else
type_name
end
end
end
end
Loading