diff --git a/lib/grpc/credential.ex b/lib/grpc/credential.ex index db15d3685..0205d909e 100644 --- a/lib/grpc/credential.ex +++ b/lib/grpc/credential.ex @@ -23,6 +23,7 @@ defmodule GRPC.Credential do Creates credential. """ def new(opts) do - %__MODULE__{ssl: Keyword.get(opts, :ssl) || []} + opts = Keyword.validate!(opts, [:ssl]) + %__MODULE__{ssl: opts[:ssl] || []} end end diff --git a/lib/grpc/endpoint.ex b/lib/grpc/endpoint.ex index dadf09be2..c7b7fb57c 100644 --- a/lib/grpc/endpoint.ex +++ b/lib/grpc/endpoint.ex @@ -39,7 +39,21 @@ defmodule GRPC.Endpoint do |> init_interceptors() servers = Module.get_attribute(env.module, :servers) - servers = Enum.map(servers, fn {ss, opts} -> {ss, parse_run_opts(opts, %{})} end) + + servers = + Enum.map(servers, fn {ss, opts} -> + opts = Keyword.validate!(opts, [:interceptors]) + + run_args = + if interceptors = opts[:interceptors] do + %{interceptors: interceptors} + else + %{} + end + + {ss, run_args} + end) + server_interceptors = server_interceptors(servers, %{}) servers = parse_servers(servers) @@ -100,16 +114,6 @@ defmodule GRPC.Endpoint do |> 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} -> diff --git a/lib/grpc/server.ex b/lib/grpc/server.ex index 3e8e17f3f..08f308e3d 100644 --- a/lib/grpc/server.ex +++ b/lib/grpc/server.ex @@ -122,11 +122,19 @@ defmodule GRPC.Server do defmacro __using__(opts) do quote bind_quoted: [opts: opts], location: :keep do + opts = + Keyword.validate!(opts, [ + :service, + codecs: [GRPC.Codec.Proto, GRPC.Codec.WebText, GRPC.Codec.JSON], + compressors: [], + http_transcode: false + ]) + service_mod = opts[:service] service_name = service_mod.__meta__(:name) - codecs = opts[:codecs] || [GRPC.Codec.Proto, GRPC.Codec.WebText, GRPC.Codec.JSON] - compressors = opts[:compressors] || [] - http_transcode = opts[:http_transcode] || false + codecs = opts[:codecs] + compressors = opts[:compressors] + http_transcode = opts[:http_transcode] codecs = if http_transcode, do: [GRPC.Codec.JSON | codecs], else: codecs @@ -404,9 +412,10 @@ defmodule GRPC.Server do @spec start_endpoint(atom(), non_neg_integer(), Keyword.t()) :: {atom(), any(), non_neg_integer()} def start_endpoint(endpoint, port, opts \\ []) do + opts = Keyword.validate!(opts, adapter: GRPC.Server.Adapters.Cowboy) + adapter = opts[:adapter] servers = endpoint.__meta__(:servers) servers = GRPC.Server.servers_to_map(servers) - adapter = Keyword.get(opts, :adapter) || GRPC.Server.Adapters.Cowboy adapter.start(endpoint, servers, port, opts) end @@ -422,7 +431,8 @@ defmodule GRPC.Server do @doc false @spec stop(module() | [module()], Keyword.t()) :: any() def stop(servers, opts \\ []) do - adapter = Keyword.get(opts, :adapter) || GRPC.Server.Adapters.Cowboy + opts = Keyword.validate!(opts, adapter: GRPC.Server.Adapters.Cowboy) + adapter = opts[:adapter] servers = GRPC.Server.servers_to_map(servers) adapter.stop(nil, servers) end @@ -430,7 +440,8 @@ defmodule GRPC.Server do @doc false @spec stop_endpoint(atom(), Keyword.t()) :: any() def stop_endpoint(endpoint, opts \\ []) do - adapter = Keyword.get(opts, :adapter) || GRPC.Server.Adapters.Cowboy + opts = Keyword.validate!(opts, adapter: GRPC.Server.Adapters.Cowboy) + adapter = opts[:adapter] servers = endpoint.__meta__(:servers) servers = GRPC.Server.servers_to_map(servers) adapter.stop(endpoint, servers) diff --git a/lib/grpc/server/interceptors/cors.ex b/lib/grpc/server/interceptors/cors.ex index 4b5b5d48b..d75b4fa91 100644 --- a/lib/grpc/server/interceptors/cors.ex +++ b/lib/grpc/server/interceptors/cors.ex @@ -41,18 +41,10 @@ defmodule GRPC.Server.Interceptors.CORS do # This is not a full-on Macro context, so binary concatenations and # variables are handled before this step. - # TODO: use Keyword.validate! once we drop support for Elixir < 1.13 - - {allow_origin, opts} = Keyword.pop(opts, :allow_origin) - {allow_headers, opts} = Keyword.pop(opts, :allow_headers) - - if opts != [] do - raise ArgumentError, - "valid keys are [:allow_origin, :allow_headers], got: #{inspect(opts)}" - end + opts = Keyword.validate!(opts, [:allow_origin, allow_headers: nil]) allow_origin = - case allow_origin do + case opts[:allow_origin] do {:&, [], [{:/, [], [_signature, 2]}]} = fun -> fun @@ -65,7 +57,7 @@ defmodule GRPC.Server.Interceptors.CORS do end allow_headers = - case allow_headers do + case opts[:allow_headers] do {:&, [], [{:/, [], [_signature, 2]}]} = fun -> fun diff --git a/lib/grpc/server/interceptors/logger.ex b/lib/grpc/server/interceptors/logger.ex index 3b93310f7..4b46cdcc8 100644 --- a/lib/grpc/server/interceptors/logger.ex +++ b/lib/grpc/server/interceptors/logger.ex @@ -24,8 +24,7 @@ defmodule GRPC.Server.Interceptors.Logger do @impl true def init(opts) do - level = Keyword.get(opts, :level) || :info - [level: level] + Keyword.validate!(opts, level: :info) end @impl true diff --git a/lib/grpc/server/supervisor.ex b/lib/grpc/server/supervisor.ex index 5d83e36e8..51cf5b551 100644 --- a/lib/grpc/server/supervisor.ex +++ b/lib/grpc/server/supervisor.ex @@ -60,6 +60,8 @@ defmodule GRPC.Server.Supervisor do through the :start_server option for the GRPC.Server.Supervisor" end + opts = Keyword.validate!(opts, [:endpoint, :servers, :start_server, :port]) + endpoint_or_servers = case {opts[:endpoint], opts[:servers]} do {endpoint, servers} diff --git a/lib/grpc/service.ex b/lib/grpc/service.ex index 7162a83e0..e8ceda2b1 100644 --- a/lib/grpc/service.ex +++ b/lib/grpc/service.ex @@ -35,6 +35,8 @@ defmodule GRPC.Service do """ defmacro __using__(opts) do + opts = Keyword.validate!(opts, [:name, :protoc_gen_elixir_version]) + quote do import GRPC.Service, only: [rpc: 4, rpc: 3, stream: 1] diff --git a/lib/grpc/stub.ex b/lib/grpc/stub.ex index 2c02c791c..c2e100d23 100644 --- a/lib/grpc/stub.ex +++ b/lib/grpc/stub.ex @@ -58,6 +58,8 @@ defmodule GRPC.Stub do require Logger defmacro __using__(opts) do + opts = Keyword.validate!(opts, [:service]) + quote bind_quoted: [opts: opts] do service_mod = opts[:service] service_name = service_mod.__meta__(:name) @@ -198,15 +200,27 @@ defmodule GRPC.Stub do through the :adapter option for GRPC.Stub.connect/3" end - adapter = Keyword.get(opts, :adapter) || GRPC.Client.Adapters.Gun - - cred = Keyword.get(opts, :cred) + opts = + Keyword.validate!(opts, + cred: nil, + adapter: GRPC.Client.Adapters.Gun, + adapter_opts: [], + interceptors: [], + codec: GRPC.Codec.Proto, + compressor: nil, + accepted_compressors: [], + headers: [] + ) + + adapter = opts[:adapter] + + cred = opts[:cred] scheme = if cred, do: @secure_scheme, else: @insecure_scheme - interceptors = (Keyword.get(opts, :interceptors) || []) |> init_interceptors - codec = Keyword.get(opts, :codec) || GRPC.Codec.Proto - compressor = Keyword.get(opts, :compressor) - accepted_compressors = Keyword.get(opts, :accepted_compressors) || [] - headers = Keyword.get(opts, :headers) || [] + interceptors = init_interceptors(opts[:interceptors]) + codec = opts[:codec] + compressor = opts[:compressor] + accepted_compressors = opts[:accepted_compressors] + headers = opts[:headers] accepted_compressors = if compressor do @@ -215,7 +229,7 @@ defmodule GRPC.Stub do accepted_compressors end - adapter_opts = opts[:adapter_opts] || [] + adapter_opts = opts[:adapter_opts] unless is_list(adapter_opts) do raise ArgumentError, ":adapter_opts must be a keyword list if present" diff --git a/mix.exs b/mix.exs index aa9ccda2e..7e029c20c 100644 --- a/mix.exs +++ b/mix.exs @@ -1,13 +1,13 @@ defmodule GRPC.Mixfile do use Mix.Project - @version "0.9.0" + @version "0.10.0" def project do [ app: :grpc, version: @version, - elixir: "~> 1.12", + elixir: "~> 1.15", elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod,