Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to accept JSON in Absinthe GraphQL requests

I'm attempting to receive a string of JSON in my graphql implementation but keep getting errors with the custom scalar I've defined to handle the JSON.

I've defined a custom scalar to properly serialize the JSON to an elixir map. I'm getting errors that I have invalid data types before my code reaches the parsing stage of the custom scalar. I'm attempting to use https://github.com/absinthe-graphql/absinthe/wiki/Scalar-Recipes#json-using-jason to create the scalar however I've modified to use Poison instead of Jason.

My absinthe mutator using the :json scalar type I created.

@desc "Update user"
field :update_user, type: :user do
  arg(:email, :string)
  arg(:password, :string)
  arg(:first_name, :string)
  arg(:last_name, :string)
  arg(:age, :integer)
  arg(:client_store, :json)

  resolve(handle_errors(&Resolvers.User_Resolver.update_user/3))
end

My scalar definition and graphql schema definition

    scalar :json, name: "Json" do
        description("""
        The `Json` scalar type represents arbitrary json string data, represented as UTF-8
        character sequences. The Json type is most often used to represent a free-form
        human-readable json string.
        """)
        serialize(&encode/1)
        parse(&decode/1)
    end

    # @spec decode(Absinthe.Blueprint.Input.String.t) :: {:ok, :string} | :error
    # @spec decode(Absinthe.Blueprint.Input.Null.t) :: {:ok, nil}
    defp decode(%Absinthe.Blueprint.Input.String{value: value}) do
        Logger.info "decoded input value:"
        case Poison.decode(value) do
        {:ok, result} -> {:ok, result}
        _ -> :error
        end
    end

    defp decode(%Absinthe.Blueprint.Input.Null{}) do
        {:ok, nil}
    end

    defp decode(_) do
        :error
    end

    defp encode(value), do: value

    object :user do
        field(:id, :id)
        field(:email, :string)
        field(:password, :string)
        field(:first_name, :string)
        field(:last_name, :string)
        field(:age, :integer)
        field(:client_store, :json)
    end

when sending the following queries:

    mutation updateUser{
      updateUser(client_store: "{"key":"value"}"){
        id
      }
    }

I receive a syntax error before: \"\\\":\\\"\"

    mutation updateUser{
      updateUser(client_store: "hello"){
        id
      }
    }

I receive a "Argument \"client_store\" has invalid value \"hello\"

Sending the GraphQL query through a unit test via Phoenix.ConnTest

query = """
    mutation updateUser{
      updateUser(client_store: "{\"key\":\"value\"}"){
        id
      }
    }
  """

res =
  context.conn
  |> put_req_header("content-type", "text")
  |> put_req_header("authorization", token)
  |> post("/api", query)
like image 331
Piranha Avatar asked Jan 06 '19 23:01

Piranha


1 Answers

The problem is with your mutation, you need to escape the quotes inside the string, like this:

mutation updateUser{
  updateUser(client_store: "{\"key\": \"value\"}"){
    id
  }
}

Otherwise, the interpretation is that the second quote marks the end of the string, so you basically have the string "{" followed by key":"value"}", which is syntatically invalid.

Note that when sending the mutation in an unit test, you are defining the mutation as a string already, so you need to further escape the quotes:

query = """
    mutation updateUser{
      updateUser(client_store: "{\\\"key\\\":\\\"value\\\"}"){
        id
      }
    }
  """

This may be a little tricky to understand at first, but printing it makes it clearer:

iex(1)> """
...(1)>     mutation updateUser{
...(1)>       updateUser(client_store: "{\"key\":\"value\"}"){
...(1)>         id
...(1)>       }
...(1)>     }
...(1)> """ |> IO.puts()
    mutation updateUser{
      updateUser(client_store: "{"key":"value"}"){
        id
      }
    }

iex(2)> """
...(2)>     mutation updateUser{
...(2)>       updateUser(client_store: "{\\\"key\\\":\\\"value\\\"}"){
...(2)>         id
...(2)>       }
...(2)>     }
...(2)> """ |> IO.puts() 
    mutation updateUser{
      updateUser(client_store: "{\"key\":\"value\"}"){
        id
      }
    }

So the last one works because when the string is evaluated, the result is the original mutation, which escapes the quotes.

like image 135
Gabriel Prá Avatar answered Nov 07 '22 10:11

Gabriel Prá