Skip to content

Commit

Permalink
Support passing through object scalars
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaden Wilkinson committed May 25, 2021
1 parent 4b40f3d commit 2c0d7e8
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Dev

- Feature: Always inline functions when using persistent_term backend.
- Feature: [Support fowarding arbitrary scalar objects to resolvers](https://github.com/absinthe-graphql/absinthe/pull/1069)

## 1.6.4

Expand Down
4 changes: 3 additions & 1 deletion lib/absinthe/blueprint/schema/scalar_type_definition.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule Absinthe.Blueprint.Schema.ScalarTypeDefinition do
serialize: nil,
directives: [],
source_location: nil,
open_ended: false,
# Added by phases
flags: %{},
errors: [],
Expand All @@ -37,7 +38,8 @@ defmodule Absinthe.Blueprint.Schema.ScalarTypeDefinition do
description: type_def.description,
definition: type_def.module,
serialize: type_def.serialize,
parse: type_def.parse
parse: type_def.parse,
open_ended: type_def.open_ended
}
end

Expand Down
7 changes: 6 additions & 1 deletion lib/absinthe/phase/document/arguments/data.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ defmodule Absinthe.Phase.Document.Arguments.Data do
def handle_node(%Input.Value{normalized: %Input.Object{fields: fields}} = node) do
data =
for field <- fields, include_field?(field), into: %{} do
{field.schema_node.identifier, field.input_value.data}
# Scalar child nodes will not have a schema_node
if field.schema_node != nil do
{field.schema_node.identifier, field.input_value.data}
else
{field.name, field.input_value.data}
end
end

%{node | data: data}
Expand Down
6 changes: 5 additions & 1 deletion lib/absinthe/phase/document/arguments/flag_invalid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Absinthe.Phase.Document.Arguments.FlagInvalid do
#
# This is later used by the ArgumentsOfCorrectType phase.

alias Absinthe.{Blueprint, Phase}
alias Absinthe.{Blueprint, Phase, Type}

use Absinthe.Phase

Expand Down Expand Up @@ -34,6 +34,10 @@ defmodule Absinthe.Phase.Document.Arguments.FlagInvalid do
check_children(node, node.items |> Enum.map(& &1.normalized), :bad_list)
end

defp handle_node(%Blueprint.Input.Object{schema_node: %Type.Scalar{open_ended: true}} = node) do
node
end

defp handle_node(%Blueprint.Input.Object{} = node) do
check_children(node, node.fields, :bad_object)
end
Expand Down
22 changes: 18 additions & 4 deletions lib/absinthe/phase/document/arguments/parse.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ defmodule Absinthe.Phase.Document.Arguments.Parse do
{:ok, result}
end

defp handle_node(%{schema_node: nil} = node, _context) do
{:halt, node}
end

defp handle_node(%{normalized: nil} = node, _context) do
node
end
Expand Down Expand Up @@ -50,10 +46,28 @@ defmodule Absinthe.Phase.Document.Arguments.Parse do
end
end

defp build_value(
%Input.Object{} = normalized,
%Type.Scalar{open_ended: true} = schema_node,
context
) do
case Type.Scalar.parse(schema_node, normalized, context) do
:error ->
{:error, :bad_parse}

{:ok, val} ->
{:ok, val}
end
end

defp build_value(_normalized, %Type.Scalar{}, _context) do
{:error, :bad_parse}
end

defp build_value(%{value: value} = _normalized, nil = _schema_node, _context) do
{:ok, value}
end

defp build_value(%Input.Null{}, %Type.Enum{}, _) do
{:ok, nil}
end
Expand Down
3 changes: 2 additions & 1 deletion lib/absinthe/type/scalar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ defmodule Absinthe.Type.Scalar do
definition: nil,
__reference__: nil,
parse: nil,
serialize: nil
serialize: nil,
open_ended: false

@typedoc "The internal, canonical representation of a scalar value"
@type value_t :: any
Expand Down
52 changes: 52 additions & 0 deletions test/absinthe/execution/arguments_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,58 @@ defmodule Absinthe.Execution.ArgumentsTest do
)
end

describe "open ended scalar" do
@graphql """
query {
entities(representations: [{__typename: "Product", id: "123"}])
}
"""
test "supports passing an object directly" do
assert_data(
%{"entities" => [%{"__typename" => "Product", "id" => "123"}]},
run(@graphql, @schema)
)
end

@graphql """
query($representations: [Any!]!) {
entities(representations: $representations)
}
"""
test "supports passing an object through variables" do
assert_data(
%{"entities" => [%{"__typename" => "Product", "id" => "123"}]},
run(@graphql, @schema,
variables: %{"representations" => [%{"__typename" => "Product", "id" => "123"}]}
)
)
end

@graphql """
query {
entities(representations: [{__typename: "Product", id: null}])
}
"""
test "supports passing an object with a nested value of null" do
assert_data(
%{"entities" => [%{"__typename" => "Product", "id" => nil}]},
run(@graphql, @schema)
)
end

@graphql """
query {
entities(representations: [{__typename: "Product", contact_type: PHONE}])
}
"""
test "supports passing an object with a nested value of ENUM" do
assert_data(
%{"entities" => [%{"__typename" => "Product", "contact_type" => "PHONE"}]},
run(@graphql, @schema)
)
end
end

describe "errors" do
@graphql """
query FindUser {
Expand Down
28 changes: 19 additions & 9 deletions test/absinthe/integration/execution/aliases/with_errors_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,24 @@ defmodule Elixir.Absinthe.Integration.Execution.Aliases.WithErrorsTest do

test "multiple aliases" do
assert {
:ok,
%{
data: %{"foo" => nil, "bar" => nil},
errors: [
%{code: 42, locations: [%{column: 3, line: 3}], message: "Custom Error", path: ["bar"]},
%{code: 42, locations: [%{column: 3, line: 2}], message: "Custom Error", path: ["foo"]}
]
}
} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, [])
:ok,
%{
data: %{"foo" => nil, "bar" => nil},
errors: [
%{
code: 42,
locations: [%{column: 3, line: 3}],
message: "Custom Error",
path: ["bar"]
},
%{
code: 42,
locations: [%{column: 3, line: 2}],
message: "Custom Error",
path: ["foo"]
}
]
}
} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, [])
end
end
13 changes: 13 additions & 0 deletions test/support/fixtures/arguments_schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ defmodule Absinthe.Fixtures.ArgumentsSchema do
false: "NO"
}

scalar :any, open_ended: true do
parse fn value -> {:ok, value} end
serialize fn value -> value end
end

scalar :input_name do
parse fn %{value: value} -> {:ok, %{first_name: value}} end
serialize fn %{first_name: name} -> name end
Expand Down Expand Up @@ -62,6 +67,14 @@ defmodule Absinthe.Fixtures.ArgumentsSchema do
end

query do
field :entities, list_of(:any) do
arg :representations, non_null(list_of(non_null(:any)))

resolve fn %{representations: representations}, _ ->
{:ok, representations}
end
end

field :stuff, :integer do
arg :stuff, non_null(:input_stuff)

Expand Down

0 comments on commit 2c0d7e8

Please sign in to comment.