document_provider.ex (3370B)
1 defmodule Absinthe.Plug.DocumentProvider do 2 @moduledoc """ 3 A document provider is a module that, given a GraphQL query, determines 4 what document should be executed and how the configured pipeline should be 5 applied to that document. 6 7 ## Configuring 8 9 Configuration of your document providers occurs on initialization of 10 `Absinthe.Plug`; see that module's documentation of the `:document_providers` 11 option for more details. 12 13 ## Making Your Own 14 15 `Absinthe.Plug.DocumentProvider` is a behaviour, and any module that 16 implements its callbacks can function as a document provider for 17 `Absinthe.Plug`. 18 19 See the documentation for the behaviour callbacks and the implementation of 20 the document providers that are defined in this package for more information. 21 22 - `Absinthe.Plug.DocumentProvider.Default` 23 - `Absinthe.Plug.DocumentProvider.Compiled` 24 """ 25 26 @typedoc """ 27 A configuration for a document provider, which can take two forms: 28 29 - `module` when options do not need to be passed to the document provider. 30 - `{module, Keyword.t}` when options are needed by the document provider. 31 """ 32 @type t :: module | {module, Keyword.t()} 33 34 @typedoc """ 35 When the request is not handled by this document provider (so processing should 36 continue to the next one): 37 38 {:cont, Absinthe.Plug.Request.Query.t} 39 40 When the request has been processed by this document provider: 41 42 {:halt, Absinthe.Plug.Request.Query.t} 43 44 Note that if no document providers set the request `document`, no document execution 45 will occur and an error will be returned to the client. 46 """ 47 @type result :: 48 {:halt, Absinthe.Plug.Request.Query.t()} | {:cont, Absinthe.Plug.Request.Query.t()} 49 50 @doc """ 51 Given a request, determine what part of its configured pipeline 52 should be applied during execution. 53 """ 54 @callback pipeline(Absinthe.Plug.Request.Query.t()) :: Absinthe.Pipeline.t() 55 56 @doc """ 57 Given a request, attempt to process it with this document provider. 58 59 ## Return Types 60 61 See the documentation for the `Absinthe.Plug.DocumentProvider.result` type. 62 """ 63 @callback process(Absinthe.Plug.Request.Query.t(), Keyword.t()) :: result 64 65 @doc false 66 @spec process([t], Absinthe.Plug.Request.Query.t()) :: Absinthe.Plug.Request.Query.t() 67 # Attempt to process an request through the given list of valid document providers 68 def process(document_providers, query) do 69 document_providers 70 |> normalize 71 |> Enum.reduce_while(query, fn {mod, opts} = provider, acc -> 72 case mod.process(acc, opts) do 73 {:halt, result} -> 74 {:halt, %{result | document_provider: provider}} 75 76 cont -> 77 cont 78 end 79 end) 80 end 81 82 @doc false 83 @spec pipeline(Absinthe.Plug.Request.Query.t()) :: Absinthe.Pipeline.t() 84 # Determine the remaining pipeline for request, based on the associated 85 # document provider. 86 def pipeline(%{document_provider: {mod, _}} = request) do 87 mod.pipeline(request) 88 end 89 90 # Normalize plain module references to document providers to the fully declared 91 # configuration that includes a keyword list. 92 @spec normalize([t]) :: [t] 93 defp normalize(document_providers) do 94 Enum.map(document_providers, &do_normalize/1) 95 end 96 97 @spec do_normalize(t) :: t 98 defp do_normalize(config) when is_tuple(config), do: config 99 defp do_normalize(config), do: {config, []} 100 end