-
Notifications
You must be signed in to change notification settings - Fork 208
/
endpoint.ex
122 lines (98 loc) · 3.28 KB
/
endpoint.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
defmodule GRPC.Endpoint do
@moduledoc """
GRPC endpoint for multiple servers and interceptors.
## Usage
defmodule Your.Endpoint do
use GRPC.Endpoint
intercept GRPC.Server.Interceptors.Logger, level: :info
intercept Other.Interceptor
run HelloServer, interceptors: [HelloHaltInterceptor]
run FeatureServer
end
Interceptors will be run around your rpc calls from top to bottom. And you can even set
interceptors for some of servers. In the above example, `[GRPC.Server.Interceptors.Logger, Other.Interceptor,
HelloHaltInterceptor]` will be run for `HelloServer`, and `[GRPC.Server.Interceptors.Logger, Other.Interceptor]`
will be run for `FeatureServer`.
"""
@doc false
defmacro __using__(_opts) do
quote do
import GRPC.Endpoint, only: [intercept: 1, intercept: 2, run: 1, run: 2]
Module.register_attribute(__MODULE__, :interceptors, accumulate: true)
Module.register_attribute(__MODULE__, :servers, accumulate: true)
@before_compile GRPC.Endpoint
end
end
@doc false
defmacro __before_compile__(env) do
interceptors =
Module.get_attribute(env.module, :interceptors)
|> Macro.escape()
|> Enum.reverse()
|> init_interceptors()
servers = Module.get_attribute(env.module, :servers)
servers = Enum.map(servers, fn {ss, opts} -> {ss, parse_run_opts(opts, %{})} end)
server_interceptors = server_interceptors(servers, %{})
servers = parse_servers(servers)
quote do
def __meta__(:interceptors), do: unquote(interceptors)
def __meta__(:servers), do: unquote(servers)
def __meta__(:server_interceptors), do: unquote(Macro.escape(server_interceptors))
end
end
defmacro intercept(name) do
quote do
@interceptors unquote(name)
end
end
@doc """
## Options
`opts` keyword will be passed to Interceptor's init/1
"""
defmacro intercept(name, opts) do
quote do
@interceptors {unquote(name), unquote(opts)}
end
end
@doc """
## Options
* `:interceptors` - custom interceptors for these servers
"""
defmacro run(servers, opts \\ []) do
quote do
@servers {unquote(servers), unquote(opts)}
end
end
defp server_interceptors([], acc), do: acc
defp server_interceptors([{servers, %{interceptors: interceptors}} | tail], acc0)
when is_list(interceptors) do
acc =
Enum.reduce(List.wrap(servers), acc0, fn server, acc ->
Map.put(acc, server, init_interceptors(interceptors))
end)
server_interceptors(tail, acc)
end
defp server_interceptors([_ | tail], acc) do
server_interceptors(tail, acc)
end
defp parse_servers(servers) do
servers
|> Enum.map(fn {server, _} -> server end)
|> List.flatten()
end
defp parse_run_opts([], acc), do: acc
defp parse_run_opts([{:interceptors, interceptors} | tail], acc) do
parse_run_opts(tail, Map.put(acc, :interceptors, interceptors))
end
defp parse_run_opts([{k, _} | _], _) do
raise ArgumentError, message: "Unknown option for GRPC.Endpoint.run/2: #{k}"
end
defp init_interceptors(interceptors) do
Enum.map(interceptors, fn
{interceptor, opts} ->
{interceptor, interceptor.init(opts)}
interceptor ->
{interceptor, interceptor.init([])}
end)
end
end