defmodule Grapix.GraphqlOps do @moduledoc """ Utilities for manipulating GraphQL documents. """ alias Grapix.QuerySpec alias Absinthe.{Pipeline, Phase} require Logger @default_opts [vars: %{}, context: %{}] @doc """ Execute an executable GraphQL document against the specified schema. As a convenience, atom keys in the vars map are morphified into string keys. """ @spec execute(Grapix.QuerySpec.t(), [keyword()]) :: {:ok | :error, any} def execute( %QuerySpec{doc: doc, op_name: op_name, selection: selection, schema: schema}, opts \\ [] ) do %{vars: vars, context: context} = Keyword.merge(@default_opts, opts) |> Enum.into(%{}) # Logger.warn "[graphql_ops/execute] #{inspect(%{doc: doc, op_name: op_name, selection: sel})}" Absinthe.run(doc, schema, operation_name: op_name, variables: Morphix.stringmorphify!(vars), context: context ) |> extract_result(selection) end # Absinthe runner doesn't signal parsing errors at the top-level status defp extract_result({:ok, %{errors: errors}}, selection) do Logger.error("[graphql_ops/extract_result '#{selection}'] #{inspect(errors, pretty: true)} ") {:error, errors} end defp extract_result({:ok, gql_result}, selection) do # Logger.warn "[graphql_ops/extract_result '#{selection}'] #{inspect(gql_result, pretty: true)} " {:ok, get_in(gql_result, [:data, selection])} end @doc """ Validates an executable GraphQL document against the specified schema. Validation includes verifying the presence and types of input variables. """ @spec validate_query_spec(Grapix.QuerySpec.t(), %{}) :: {:ok | :error, any} def validate_query_spec(%QuerySpec{doc: doc, op_name: op_name, schema: schema}, vars \\ %{}) do options = [operation_name: op_name, variables: vars] pipeline = schema |> Pipeline.for_document(options) # |> IO.inspect(label: "[graphql_ops/validate_query_spec pipeline]", pretty: true) |> Pipeline.without(Phase.Subscription.SubscribeSelf) |> Pipeline.without(Phase.Document.Execution.Resolution) # |> IO.inspect(label: "[graphql_ops/validate_query_spec pipeline]", pretty: true) case Pipeline.run(doc, pipeline) do {:ok, %{execution: %{validation_errors: []}}, _} -> {:ok, []} {:ok, %{execution: %{validation_errors: validation_errors}}, _} -> {:error, validation_errors} other -> {:error, other} end end end