Repository: elixir-grpc/grpc (elixir)

Issue:
GRPC.Server.Adapters.Cowboy.Handler.state document is missing
CI complaint about Handler.state documentation is missing

![FA19CA9F-26F5-420A-B11A-2D31C713471B](https://user-images.githubusercontent.com/484530/180588674-57d1ce75-63be-4f05-82a9-95b7f5e07dbd.png)

found from PR #236 


Pull request:
chore: resolve some doc warnings
closes #249 

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6387ff1..b3b0f39 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,10 +25,7 @@ jobs:
           path: deps
           key: v1-${{ matrix.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
       - name: Install Dependencies
-        run: |
-          mix local.rebar --force
-          mix local.hex --force
-          mix deps.get
+        run: mix deps.get 1>/dev/null
       - name: Check format
         run: mix format --check-formatted
 
@@ -58,10 +55,7 @@ jobs:
           path: deps
           key: v1-${{ matrix.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
       - name: Install Dependencies
-        run: |
-          mix local.rebar --force
-          mix local.hex --force
-          mix deps.get
+        run: mix deps.get 1>/dev/null
       - name: Run Tests
         run: mix test
 
@@ -83,10 +77,7 @@ jobs:
           path: deps
           key: v1-${{ matrix.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
       - name: Install Dependencies
-        run: |
-          mix local.rebar --force
-          mix local.hex --force
-          mix deps.get
+        run: mix deps.get 1>/dev/null
         working-directory: ./interop
       - name: Run interop tests
         run: mix run script/run.exs --rounds 64
@@ -119,10 +110,7 @@ jobs:
           path: deps
           key: v1-${{ matrix.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
       - name: Install Dependencies
-        run: |
-          mix local.rebar --force
-          mix local.hex --force
-          mix deps.get
+        run: mix deps.get 1>/dev/null
         working-directory: ./interop
       - name: Run interop tests
         run: mix run script/run.exs --rounds 64
@@ -156,7 +144,7 @@ jobs:
         with:
           otp-version: ${{matrix.otp}}
           elixir-version: ${{matrix.elixir}}
-      - run: mix deps.get
+      - run: mix deps.get 1>/dev/null
       - run: mix dialyzer --format short
 
   check_release:
@@ -176,10 +164,7 @@ jobs:
           path: deps
           key: v1-${{ matrix.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
       - name: Install Dependencies
-        run: |
-          mix local.rebar --force
-          mix local.hex --force
-          mix deps.get
+        run: mix deps.get 1>/dev/null
       - name: Build hex
         run: mix hex.build
       - name: Generate docs
diff --git a/README.md b/README.md
index 9ec1122..6438b3d 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 [![Hex.pm](https://img.shields.io/hexpm/v/grpc.svg)](https://hex.pm/packages/grpc)
 [![Travis Status](https://travis-ci.org/elixir-grpc/grpc.svg?branch=master)](https://travis-ci.org/elixir-grpc/grpc)
 [![GitHub actions Status](https://github.com/elixir-grpc/grpc/workflows/CI/badge.svg)](https://github.com/elixir-grpc/grpc/actions)
-[![Inline docs](http://inch-ci.org/github/elixir-grpc/grpc.svg?branch=master)](http://inch-ci.org/github/elixir-grpc/grpc)
 
 An Elixir implementation of [gRPC](http://www.grpc.io/).
 
diff --git a/interop/lib/interop/app.ex b/interop/lib/interop/app.ex
index 6911c6c..033f347 100644
--- a/interop/lib/interop/app.ex
+++ b/interop/lib/interop/app.ex
@@ -2,11 +2,7 @@ defmodule Interop.App do
   use Application
 
   def start(_type, _args) do
-    import Supervisor.Spec
-
-    children = [
-      supervisor(GRPC.Server.Supervisor, [{Interop.Endpoint, 10000}])
-    ]
+    children = [{GRPC.Server.Supervisor, {Interop.Endpoint, 10000}}]
 
     GRPCPrometheus.ServerInterceptor.setup()
     GRPCPrometheus.ClientInterceptor.setup()
diff --git a/interop/script/run.exs b/interop/script/run.exs
index 655a5d0..18a41bc 100644
--- a/interop/script/run.exs
+++ b/interop/script/run.exs
@@ -10,7 +10,7 @@ require Logger
 
 Logger.configure(level: level)
 
-Logger.warn("Rounds: #{rounds}; concurrency: #{concurrency}; port: #{port}")
+Logger.info("Rounds: #{rounds}; concurrency: #{concurrency}; port: #{port}")
 
 alias Interop.Client
 
@@ -18,7 +18,7 @@ alias Interop.Client
 
 stream = Task.async_stream(1..concurrency, fn _cli ->
   ch = Client.connect("127.0.0.1", port, interceptors: [GRPCPrometheus.ClientInterceptor, GRPC.Logger.Client])
-  
+
   for _ <- 1..rounds do
     Client.empty_unary!(ch)
     Client.cacheable_unary!(ch)
@@ -60,5 +60,5 @@ end)
 # end
 # Helper.flush()
 
-Logger.warn("Succeed!")
+Logger.info("Succeed!")
 :ok = GRPC.Server.stop_endpoint(Interop.Endpoint)
diff --git a/lib/grpc/client/adapters/gun.ex b/lib/grpc/client/adapters/gun.ex
index a1e46b5..827cb76 100644
--- a/lib/grpc/client/adapters/gun.ex
+++ b/lib/grpc/client/adapters/gun.ex
@@ -1,10 +1,11 @@
 defmodule GRPC.Client.Adapters.Gun do
-  @moduledoc false
+  @moduledoc """
+  A client (`b:GRPC.Client.Adapter`) adapter using `:gun`.
 
-  @behaviour GRPC.Client.Adapter
+  `conn_pid` and `stream_ref` are stored in `GRPC.Server.Stream`.
+  """
 
-  # A client adapter using Gun.
-  # conn_pid and stream_ref is stored in `GRPC.Server.Stream`.
+  @behaviour GRPC.Client.Adapter
 
   @default_transport_opts [nodelay: true]
   @default_http2_opts %{settings_timeout: :infinity}
diff --git a/lib/grpc/message.ex b/lib/grpc/message.ex
index a4e372d..2ba3b22 100644
--- a/lib/grpc/message.ex
+++ b/lib/grpc/message.ex
@@ -1,23 +1,22 @@
 defmodule GRPC.Message do
+  @moduledoc """
+  Transform data between encoded protobuf and HTTP/2 body of gRPC.
+
+  gRPC body format is:
+
+    http://www.grpc.io/docs/guides/wire.html
+    Delimited-Message -> Compressed-Flag Message-Length Message
+    Compressed-Flag -> 0 / 1 # encoded as 1 byte unsigned integer
+    Message-Length -> {length of Message} # encoded as 4 byte unsigned integer
+    Message -> *{binary octet}
+  """
   use Bitwise, only_operators: true
   @max_message_length 1 <<< (32 - 1)
 
   alias GRPC.RPCError
 
-  @moduledoc false
-
-  # Transform data between encoded protobuf and HTTP/2 body of gRPC.
-  #
-  # gRPC body format is:
-  #
-  #     # http://www.grpc.io/docs/guides/wire.html
-  #     Delimited-Message -> Compressed-Flag Message-Length Message
-  #     Compressed-Flag -> 0 / 1 # encoded as 1 byte unsigned integer
-  #     Message-Length -> {length of Message} # encoded as 4 byte unsigned integer
-  #     Message -> *{binary octet}
-
   @doc """
-  Transform protobuf data to gRPC body
+  Transforms Protobuf data into a gRPC body binary.
 
   ## Options
 
@@ -66,7 +65,7 @@ defmodule GRPC.Message do
   end
 
   @doc """
-  Transform gRPC body to protobuf data
+  Transforms gRPC body into Protobuf data.
 
   ## Examples
 
@@ -80,7 +79,7 @@ defmodule GRPC.Message do
   end
 
   @doc """
-  Transform gRPC body to protobuf data with compressing
+  Transform gRPC body into Protobuf data with compression.
 
   ## Examples
 
@@ -144,7 +143,7 @@ defmodule GRPC.Message do
   end
 
   @doc """
-  Get message data from data buffer
+  Get message data from data buffer.
 
   ## Examples
 
diff --git a/lib/grpc/server/adapter.ex b/lib/grpc/server/adapter.ex
index 8ba43bc..f2f3009 100644
--- a/lib/grpc/server/adapter.ex
+++ b/lib/grpc/server/adapter.ex
@@ -3,14 +3,20 @@ defmodule GRPC.Server.Adapter do
   HTTP server adapter for GRPC.
   """
 
-  alias GRPC.Server.Adapters.Cowboy.Handler
+  @type state :: %{
+          pid: pid,
+          handling_timer: reference | nil,
+          resp_trailers: map,
+          compressor: atom | nil,
+          pending_reader: nil
+        }
 
   @callback start(atom(), %{String.t() => [module()]}, non_neg_integer(), Keyword.t()) ::
               {atom(), any(), non_neg_integer()}
 
   @callback stop(atom(), %{String.t() => [module()]}) :: :ok | {:error, :not_found}
 
-  @callback send_reply(Handler.state(), binary(), Keyword.t()) :: any()
+  @callback send_reply(state(), binary(), Keyword.t()) :: any()
 
-  @callback send_headers(Handler.state(), map()) :: any()
+  @callback send_headers(state(), map()) :: any()
 end
diff --git a/lib/grpc/server/adapters/cowboy.ex b/lib/grpc/server/adapters/cowboy.ex
index 81a01e3..4a80c33 100644
--- a/lib/grpc/server/adapters/cowboy.ex
+++ b/lib/grpc/server/adapters/cowboy.ex
@@ -1,13 +1,11 @@
 defmodule GRPC.Server.Adapters.Cowboy do
-  @moduledoc false
+  @moduledoc """
+  A server (`b:GRPC.Server.Adapter`) adapter using `:cowboy`.
 
-  @behaviour GRPC.Server.Adapter
-
-  # A server(`GRPC.Server`) adapter using Cowboy.
-  # Cowboy req will be stored in `:payload` of `GRPC.Server.Stream`.
+  Cowboy requests will be stored in the `:payload` field of the `GRPC.Server.Stream`.
+  """
 
-  # Waiting for this is released on hex https://github.com/ninenines/ranch/pull/227
-  @dialyzer {:nowarn_function, running_info: 4}
+  @behaviour GRPC.Server.Adapter
 
   require Logger
   alias GRPC.Server.Adapters.Cowboy.Handler
@@ -80,12 +78,12 @@ defmodule GRPC.Server.Adapters.Cowboy do
     :cowboy.stop_listener(servers_name(endpoint, servers))
   end
 
-  @spec read_body(GRPC.Server.Adapters.Cowboy.Handler.state()) :: {:ok, binary()}
+  @spec read_body(GRPC.Server.Adapter.state()) :: {:ok, binary()}
   def read_body(%{pid: pid}) do
     Handler.read_full_body(pid)
   end
 
-  @spec reading_stream(GRPC.Server.Adapters.Cowboy.Handler.state()) :: Enumerable.t()
+  @spec reading_stream(GRPC.Server.Adapter.state()) :: Enumerable.t()
   def reading_stream(%{pid: pid}) do
     Stream.unfold(%{pid: pid, need_more: true, buffer: <<>>}, fn acc -> read_stream(acc) end)
   end
@@ -229,8 +227,8 @@ defmodule GRPC.Server.Adapters.Cowboy do
 
     addr_str =
       case addr do
-        :local ->
-          port
+        :undefined ->
+          raise "undefined address for ranch server"
 
         addr ->
           "#{:inet.ntoa(addr)}:#{port}"
diff --git a/lib/grpc/server/adapters/cowboy/handler.ex b/lib/grpc/server/adapters/cowboy/handler.ex
index 73e2223..433b6fd 100644
--- a/lib/grpc/server/adapters/cowboy/handler.ex
+++ b/lib/grpc/server/adapters/cowboy/handler.ex
@@ -10,13 +10,6 @@ defmodule GRPC.Server.Adapters.Cowboy.Handler do
 
   @adapter GRPC.Server.Adapters.Cowboy
   @default_trailers HTTP2.server_trailers()
-  @type state :: %{
-          pid: pid,
-          handling_timer: reference | nil,
-          resp_trailers: map,
-          compressor: atom | nil,
-          pending_reader: nil
-        }
 
   @spec init(map(), {atom(), %{String.t() => [module()]}, map()}) :: {:cowboy_loop, map(), map()}
   def init(req, {endpoint, servers, opts} = state) do
diff --git a/lib/grpc/server/supervisor.ex b/lib/grpc/server/supervisor.ex
index b2480ae..a09539b 100644
--- a/lib/grpc/server/supervisor.ex
+++ b/lib/grpc/server/supervisor.ex
@@ -1,6 +1,4 @@
 defmodule GRPC.Server.Supervisor do
-  use Supervisor
-
   @moduledoc """
   A simple supervisor to start your servers.
 
@@ -10,10 +8,8 @@ defmodule GRPC.Server.Supervisor do
         use Application
 
         def start(_type, _args) do
-          import Supervisor.Spec
-
           children = [
-            supervisor(GRPC.Server.Supervisor, [{Your.Endpoint, 50051(, opts)}])
+            {GRPC.Server.Supervisor, {Your.Endpoint, 50051, opts}}
           ]
 
           opts = [strategy: :one_for_one, name: __MODULE__]
@@ -23,12 +19,16 @@ defmodule GRPC.Server.Supervisor do
 
       # config.exs
       config :grpc, start_server: true
-      or
+
+  or
+
       run `mix grpc.server` on local
 
-  View `child_spec/3` for opts.
+  or you can pass `start_server: true` as an option for the supervisor (as started above)
   """
 
+  use Supervisor
+
   @default_adapter GRPC.Server.Adapters.Cowboy
   require Logger
 
@@ -47,11 +47,17 @@ defmodule GRPC.Server.Supervisor do
   def init({endpoint, port, opts}) do
     check_deps_version()
 
+    # The code is repeated because it is concise and this way we can make it lazy
     children =
-      if Application.get_env(:grpc, :start_server, false) do
-        [child_spec(endpoint, port, opts)]
-      else
-        []
+      cond do
+        opts[:start_server] ->
+          [child_spec(endpoint, port, opts)]
+
+        not Keyword.has_key?(opts, :start_server) and Application.get_env(:grpc, :start_server) ->
+          [child_spec(endpoint, port, opts)]
+
+        :otherwise ->
+          []
       end
 
     Supervisor.init(children, strategy: :one_for_one)
@@ -64,6 +70,9 @@ defmodule GRPC.Server.Supervisor do
 
     * `:cred` - a credential created by functions of `GRPC.Credential`,
       an insecure server will be created without this option
+    * `:start_server` - determines if the server will be started.
+      If present, has more precedence then the `config :gprc, :start_server`
+      config value (i.e. `start_server: false` will not start the server in any case).
   """
   @spec child_spec(atom() | [atom()], integer(), Keyword.t()) :: Supervisor.Spec.spec()
   def child_spec(endpoint, port, opts \\ [])
diff --git a/mix.exs b/mix.exs
index ade000c..7b9f3a9 100644
--- a/mix.exs
+++ b/mix.exs
@@ -46,7 +46,6 @@ defmodule GRPC.Mixfile do
       {:cowlib, "~> 2.11"},
       {:protobuf, "~> 0.10", only: [:dev, :test]},
       {:ex_doc, "~> 0.28", only: :dev},
-      {:inch_ex, "~> 2.0", only: [:dev, :test]},
       {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}
     ]
   end
diff --git a/mix.lock b/mix.lock
index 71da64c..43cfde4 100644
--- a/mix.lock
+++ b/mix.lock
@@ -7,7 +7,6 @@
   "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
   "ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
   "gun": {:hex, :grpc_gun, "2.0.1", "221b792df3a93e8fead96f697cbaf920120deacced85c6cd3329d2e67f0871f8", [:rebar3], [{:cowlib, "~> 2.11", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "795a65eb9d0ba16697e6b0e1886009ce024799e43bb42753f0c59b029f592831"},
-  "inch_ex": {:hex, :inch_ex, "2.0.0", "24268a9284a1751f2ceda569cd978e1fa394c977c45c331bb52a405de544f4de", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "96d0ec5ecac8cf63142d02f16b7ab7152cf0f0f1a185a80161b758383c9399a8"},
   "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
   "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
   "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},


Test patch:
diff --git a/test/grpc/integration/connection_test.exs b/test/grpc/integration/connection_test.exs
index 2416b77..bdd25cf 100644
--- a/test/grpc/integration/connection_test.exs
+++ b/test/grpc/integration/connection_test.exs
@@ -5,14 +5,6 @@ defmodule GRPC.Integration.ConnectionTest do
   @key_path Path.expand("./tls/server1.key", :code.priv_dir(:grpc))
   @ca_path Path.expand("./tls/ca.pem", :code.priv_dir(:grpc))
 
-  defmodule FeatureServer do
-    use GRPC.Server, service: Routeguide.RouteGuide.Service
-
-    def get_feature(point, _stream) do
-      Routeguide.Feature.new(location: point, name: "#{point.latitude},#{point.longitude}")
-    end
-  end
-
   test "reconnection works" do
     server = FeatureServer
     {:ok, _, port} = GRPC.Server.start(server, 0)
diff --git a/test/grpc/server/supervisor_test.exs b/test/grpc/server/supervisor_test.exs
new file mode 100644
index 0000000..251be37
--- /dev/null
+++ b/test/grpc/server/supervisor_test.exs
@@ -0,0 +1,57 @@
+defmodule GRPC.Server.SupervisorTest do
+  use ExUnit.Case, async: false
+
+  alias GRPC.Server.Supervisor
+
+  defmodule MockEndpoint do
+    def __meta__(_), do: [FeatureServer]
+  end
+
+  setup_all do
+    start_server = Application.get_env(:grpc, :start_server)
+
+    on_exit(fn -> Application.put_env(:grpc, :start_server, start_server) end)
+
+    :ok
+  end
+
+  describe "init/1" do
+    test "does not start children if no opts and global env is false" do
+      Application.put_env(:grpc, :start_server, false)
+      assert {:ok, {%{strategy: :one_for_one}, []}} = Supervisor.init({MockEndpoint, 1234})
+    end
+
+    test "does not start children if opts sets false and global env is true" do
+      Application.put_env(:grpc, :start_server, true)
+
+      assert {:ok, {%{strategy: :one_for_one}, []}} =
+               Supervisor.init({MockEndpoint, 1234, start_server: false})
+    end
+
+    test "does not start children if opts sets true and global env is false" do
+      Application.put_env(:grpc, :start_server, false)
+
+      endpoint_str = "#{Macro.to_string(MockEndpoint)}"
+
+      assert {:ok,
+              {%{strategy: :one_for_one},
+               [
+                 {{:ranch_listener_sup, ^endpoint_str}, _, :permanent, :infinity, :supervisor,
+                  [:ranch_listener_sup]}
+               ]}} = Supervisor.init({MockEndpoint, 1234, start_server: true})
+    end
+
+    test "does not start children if opts sets true and global env is true" do
+      Application.put_env(:grpc, :start_server, true)
+
+      endpoint_str = "#{Macro.to_string(MockEndpoint)}"
+
+      assert {:ok,
+              {%{strategy: :one_for_one},
+               [
+                 {{:ranch_listener_sup, ^endpoint_str}, _, :permanent, :infinity, :supervisor,
+                  [:ranch_listener_sup]}
+               ]}} = Supervisor.init({MockEndpoint, 1234, start_server: true})
+    end
+  end
+end
diff --git a/test/support/feature_server.ex b/test/support/feature_server.ex
new file mode 100644
index 0000000..3951ea8
--- /dev/null
+++ b/test/support/feature_server.ex
@@ -0,0 +1,7 @@
+defmodule FeatureServer do
+  use GRPC.Server, service: Routeguide.RouteGuide.Service
+
+  def get_feature(point, _stream) do
+    Routeguide.Feature.new(location: point, name: "#{point.latitude},#{point.longitude}")
+  end
+end