diff --git a/lib/graphql/plug.ex b/lib/graphql/plug.ex index 3e00b57..00069c7 100644 --- a/lib/graphql/plug.ex +++ b/lib/graphql/plug.ex @@ -15,7 +15,8 @@ defmodule GraphQL.Plug do {mod, func} -> apply(mod, func, []) s -> s end - %{schema: schema} + root_value = Keyword.get(opts, :root_value, %{}) + %{:schema => schema, :root_value => root_value} end def call(conn, opts) do diff --git a/lib/graphql/plug/endpoint.ex b/lib/graphql/plug/endpoint.ex index d121a6d..ee0cfec 100644 --- a/lib/graphql/plug/endpoint.ex +++ b/lib/graphql/plug/endpoint.ex @@ -9,16 +9,18 @@ defmodule GraphQL.Plug.Endpoint do {mod, func} -> apply(mod, func, []) s -> s end - %{schema: schema} + root_value = Keyword.get(opts, :root_value, %{}) + %{:schema => schema, :root_value => root_value} end def call(%Conn{method: m} = conn, opts) when m in ["GET", "POST"] do - %{schema: schema} = conn.assigns[:graphql_options] || opts + %{:schema => schema, :root_value => root_value} = conn.assigns[:graphql_options] || opts query = query(conn) + evaluated_root_value = evaluate_root_value(conn, root_value) cond do - query && use_graphiql?(conn) -> handle_graphiql_call(conn, schema, query) - query -> handle_call(conn, schema, query) + query && use_graphiql?(conn) -> handle_graphiql_call(conn, schema, evaluated_root_value, query) + query -> handle_call(conn, schema, evaluated_root_value, query) true -> handle_error(conn, "Must provide query string.") end end @@ -27,14 +29,14 @@ defmodule GraphQL.Plug.Endpoint do handle_error(conn, "GraphQL only supports GET and POST requests.") end - defp handle_call(conn, schema, query) do + defp handle_call(conn, schema, root_value, query) do conn |> put_resp_content_type("application/json") - |> execute(schema, query) + |> execute(schema, root_value, query) end - defp handle_graphiql_call(conn, schema, query) do - {:ok, data} = GraphQL.execute(schema, query) + defp handle_graphiql_call(conn, schema, root_value, query) do + {:ok, data} = GraphQL.execute(schema, query, root_value) {:ok, result} = Poison.encode(%{data: data}) conn |> put_resp_content_type("text/html") @@ -48,8 +50,8 @@ defmodule GraphQL.Plug.Endpoint do |> send_resp(400, errors) end - defp execute(conn, schema, query) do - case GraphQL.execute(schema, query) do + defp execute(conn, schema, root_value, query) do + case GraphQL.execute(schema, query, root_value) do {:ok, data} -> case Poison.encode(%{data: data}) do {:ok, json} -> send_resp(conn, 200, json) @@ -63,6 +65,22 @@ defmodule GraphQL.Plug.Endpoint do end end + defp evaluate_root_value(conn, {mod, func}) do + apply(mod, func, [conn]) + end + + defp evaluate_root_value(conn, fn_root_value) when is_function(fn_root_value, 1) do + apply(fn_root_value, [conn]) + end + + defp evaluate_root_value(_, nil) do + %{} + end + + defp evaluate_root_value(_, root_value) do + root_value + end + defp query(%Conn{params: %{"query" => query}}) do if query && String.strip(query) != "", do: query, else: nil end diff --git a/mix.lock b/mix.lock index 01546f1..72164de 100644 --- a/mix.lock +++ b/mix.lock @@ -2,7 +2,7 @@ "cowlib": {:hex, :cowlib, "1.0.2"}, "earmark": {:hex, :earmark, "0.1.19"}, "ex_doc": {:hex, :ex_doc, "0.11.0"}, - "graphql": {:hex, :graphql, "0.0.9"}, + "graphql": {:hex, :graphql, "0.1.0"}, "plug": {:hex, :plug, "1.0.2"}, "poison": {:hex, :poison, "1.5.0"}, "ranch": {:hex, :ranch, "1.2.0"}} diff --git a/test/graphql/plug/endpoint_test.exs b/test/graphql/plug/endpoint_test.exs index 1112afa..e76a1af 100644 --- a/test/graphql/plug/endpoint_test.exs +++ b/test/graphql/plug/endpoint_test.exs @@ -19,6 +19,7 @@ defmodule GraphQL.Plug.EndpointTest do end def greeting(_, %{name: name}, _), do: "Hello, #{name}!" + def greeting(%{greeting: name}, _, _), do: "Hello, #{name}!" def greeting(_, _, _), do: greeting(%{}, %{name: "world"}, %{}) end @@ -34,6 +35,38 @@ defmodule GraphQL.Plug.EndpointTest do assert_query TestPlug, {:post, "/", query: "{greeting}"}, {200, success} end + test "root data can be set at request time using function reference" do + defmodule TestRootPlugWithFunctionReference do + use Plug.Builder + def root_eval(_conn), do: %{greeting: "Root"} + plug GraphQL.Plug.Endpoint, [schema: {TestSchema, :schema}, root_value: &TestRootPlugWithFunctionReference.root_eval/1 ] + end + + success = ~S({"data":{"greeting":"Hello, Root!"}}) + assert_query TestRootPlugWithFunctionReference, {:get, "/", query: "{greeting}"}, {200, success} + end + + test "root data can be hard coded at init time." do + defmodule TestRootPlugWithHardCodedData do + use Plug.Builder + plug GraphQL.Plug.Endpoint, [schema: {TestSchema, :schema}, root_value: %{greeting: "Hard Coded"}] + end + + success = ~S({"data":{"greeting":"Hello, Hard Coded!"}}) + assert_query TestRootPlugWithHardCodedData, {:get, "/", query: "{greeting}"}, {200, success} + end + + test "root data can be set using {module, fun} syntax" do + defmodule TestRootPlugWithMF do + use Plug.Builder + def root_eval(_conn), do: %{greeting: "MF"} + plug GraphQL.Plug.Endpoint, [schema: {TestSchema, :schema}, root_value: {TestRootPlugWithMF, :root_eval}] + end + + success = ~S({"data":{"greeting":"Hello, MF!"}}) + assert_query TestRootPlugWithMF, {:get, "/", query: "{greeting}"}, {200, success} + end + test "specify schema using {module, fun} syntax" do defmodule TestMFPlug do use Plug.Builder