Skip to content

Commit

Permalink
Support passing through object scalars (#1069)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdawgwilk authored Jun 15, 2021
1 parent fe02d15 commit 1948513
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 9 deletions.
4 changes: 3 additions & 1 deletion 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 optional open ended scalars](https://github.com/absinthe-graphql/absinthe/pull/1069)

## 1.6.4

Expand Down Expand Up @@ -72,11 +73,12 @@
## v1.5.0 (Rc)

- Breaking Bug Fix: Variable types must align exactly with the argument type. Previously
Absinthe allowed variables of different types to be used by accident as long as the data parsed.
Absinthe allowed variables of different types to be used by accident as long as the data parsed.
- Feature (Experimental): `:persistent_term` based schema backend
- Breaking Change: `telemetry` event keys [changed](https://github.com/absinthe-graphql/absinthe/pull/901) since the beta release.

## v1.5.0 (Beta)

- Feature: SDL directives, other improvements
- Feature: Output rendered SDL for a schema
- Feature: Substantially lower subscription memory usage.
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
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 1948513

Please sign in to comment.