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