Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open ended scalars don't support list values #1294

Open
JonRowe opened this issue Feb 1, 2024 · 7 comments
Open

Open ended scalars don't support list values #1294

JonRowe opened this issue Feb 1, 2024 · 7 comments

Comments

@JonRowe
Copy link
Contributor

JonRowe commented Feb 1, 2024

👋 Thanks for all the hard work on Absinthe, I think I've found a bug in open ended scalars, we're using them to transport values where we don't own the structure for from a third party to our UI, I actually tried to type these but unions don't support all the types we would need and for queries the open ended scalar works fine, the problem happens trying to use this scalar for mutations, one of the sub type data we need to send is a list of booleans which causes Absinthe to decide the argument is invalid (because its a list).

I can appreciate you might decide this is not a bug, because why is a scalar sending down a list, but given that its valid json to have a root list and given that the inverse works its at least inconsistent if not a bug.

Environment

  • Elixir version (elixir -v):
  • Absinthe version (mix deps | grep absinthe): 1.7.6
  • Client Framework and version (Relay, Apollo, etc): N/A

Expected behavior

Absinthe sees the field is an open ended scalar and passes the parsed JSON result through as is.

Actual behavior

Absinthe sees a list and decides its invalid.

Relevant Schema/Middleware Code

scalar :magic, open_ended: true do
  parse(& {:ok, &1})
  serialize(& &1)
end

input_object :something do
  field :value, :magic
end
@benwilson512
Copy link
Contributor

Hi @JonRowe can you show a concrete example?

@JonRowe
Copy link
Contributor Author

JonRowe commented Feb 1, 2024

defmodule MagicValuesTest do
  use ExUnit.Case, async: true

  defmodule Schema do
    use Absinthe.Schema

    scalar :magic, open_ended: true do
      parse(&{:ok, &1})
      serialize(& &1)
    end

    object :something do
      field :name, non_null(:string)
      field :value, :magic
    end

    input_object :something_input do
      field :name, non_null(:string)
      field :value, :magic
    end

    query do
      field :get_values, non_null(list_of(non_null(:something))) do
        resolve(fn _, _, _ ->
          {:ok, [%{name: "thing_one", value: ["magic"]}]}
        end)
      end
    end

    mutation do
      field :patch_values, type: non_null(list_of(non_null(:something))) do
        arg(:somethings, non_null(list_of(non_null(:something_input))))

        resolve(fn _, %{somethings: somethings}, _ -> {:ok, somethings} end)
      end
    end
  end

  setup do
    Absinthe.Test.prime(Schema)
  end

  test "query passes" do
    assert {:ok, %{data: %{"getValues" => [%{"name" => "thing_one", "value" => ["magic"]}]}}} =
             """
             query {
               getValues {
                 name
                 value
               }
             }
             """
             |> Absinthe.run(Schema, context: %{}, variables: %{})
  end

  test "mutation passes without a list" do
    assert {:ok, %{data: %{"patchValues" => [%{"name" => "thing_one", "value" => _}]}}} =
             """
             mutation patchValues($somethings: [SomethingInput!]!) {
               patchValues(somethings: $somethings) {
                 name
                 value
               }
             }
             """
             |> Absinthe.run(
               Schema,
               context: %{},
               variables: %{
                 "somethings" => [
                   %{"name" => "thing_one", "value" => "magic"}
                 ]
               }
             )
  end

  test "mutation fails" do
    assert {:ok, %{data: %{"patchValues" => [%{"name" => "thing_one", "value" => ["magic"]}]}}} =
             """
             mutation patchValues($somethings: [SomethingInput!]!) {
               patchValues(somethings: $somethings) {
                 name
                 value
               }
             }
             """
             |> Absinthe.run(
               Schema,
               context: %{},
               variables: %{
                 "somethings" => [
                   %{"name" => "thing_one", "value" => ["magic"]}
                 ]
               }
             )
  end
end

@benwilson512
Copy link
Contributor

Hey Jon I think you're confused abut how open ended scalars are supposed to work. You've got your open ended scalar deep inside other structures. Those structures are not invalidated or consumed by the scalar just because they have an open ended scalar inside. If you want to have your open ended scalar consume the entire input you'd do something more like:

arg :somethings, :magic

I can try to find time later to edit your example to show what I mean. The reason you have versions that pass is that you are passing in JSON structures that are just straight up compatible with the values you have. It'd work even if you used :string instead of :magic.

@JonRowe
Copy link
Contributor Author

JonRowe commented Feb 1, 2024

Those structures are not invalidated or consumed by the scalar just because they have an open ended scalar inside.

I know that, thats the point, I want name to be a mandatory string, and I only want value to be open ended, hence why I showed if I use another non list value it works, objects, strings, booleans all work in the fashion I expect its only the list that triggers the invalid argument

@benwilson512
Copy link
Contributor

Apologies, I misread the test. Let me dig into this later today.

@benwilson512 benwilson512 reopened this Feb 1, 2024
@Wigny
Copy link

Wigny commented May 8, 2024

the problem happens trying to use this scalar for mutations, one of the sub type data we need to send is a list

I'm facing the same requirements right now, and I was hoping that open-ended scalars would help here so I could avoid needing to have a JSON string scalar. Any advice if that could be accomplished?

@JonRowe
Copy link
Contributor Author

JonRowe commented May 9, 2024

As a workaround you can use a structure like {entries: list} as the scalar, its not great but it works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants