-
Notifications
You must be signed in to change notification settings - Fork 214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define behaviours for client and server adapters #200
Define behaviours for client and server adapters #200
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started looking into this for issue #199.
Since there was already your pull request opened, perhaps it's best we focus on it instead!
lib/grpc/adapter/client.ex
Outdated
|
||
@callback disconnect(channel) :: {:ok, channel} | {:error, any} | ||
|
||
@type stream :: Stream.t() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use Stream.t()
directly instead of adding this proxy type. Is there any reason for this path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity, I would also move all @type
definitions to the top of the module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, the only reason about using this proxy type is because I did it to all the types. Also the idea is that in this way it could be used in the specs of the definitions. But I don't have a strong reason to do that. How do you consider using the proxy type or using it directly inline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the in-line type is better for readability:
# Module defs
defmodule T do
@type t :: integer
end
defmodule M do
@type custom :: T.t()
@spec f(x :: T.t(), y :: custom) :: T.t()
def f(x, y), do: x + y
end
# iex calls
iex(1)> t M
@type custom() :: T.t()
iex(2)> h M.f
def f(x, y)
@spec f(x :: T.t(), y :: custom()) :: T.t()
When exploring the API via IEx, the proxy type will force the user to call t module
to get the typedefs and discover it is indeed a proxy
lib/grpc/adapter/client.ex
Outdated
alias GRPC.Client.Stream | ||
alias GRPC.Channel | ||
|
||
@type opts :: any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to use opts :: keyword()
instead (perhaps even avoiding a custom type, inlining this wherever needed).
This would warrant some changes to the current implementation, but provides a better interface
lib/grpc/adapter/client.ex
Outdated
@type message :: binary() | ||
@callback send_request(stream, message, opts) :: stream | ||
|
||
@type headers :: [{String.t(), String.t()}] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation would need to change, as I think it currently returns a string-map
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totally right
lib/grpc/adapter/client.ex
Outdated
@callback send_request(stream, message, opts) :: stream | ||
|
||
@type headers :: [{String.t(), String.t()}] | ||
@type fin :: :fin | :nofin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A typedoc would be great here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool! 😄
lib/grpc/adapter/client.ex
Outdated
@@ -0,0 +1,27 @@ | |||
defmodule GRPC.ClientAdapter do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defmodule GRPC.ClientAdapter do | |
defmodule GRPC.Client do |
I think ClientAdapter
is kinda redundant since the "adapter" part is actually what implements the behaviour specified here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that, if we want to follow the same criteria with the GRPC.Server
, we wont be able because there is already another file (lib/grpc/server.ex
) with the same module name. WDYT about using GRPC.Adapter.Client
and GRPC.Adapter.Server
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think GRPC.Ports.Client/Server
might be a better naming pattern (from the Ports and Adapters design pattern), and then we could have GRPC.Adapters.Client.Gun
or something like that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just my opinion, curently we have lib/grpc/client
and lib/grpc/server
which provides client code and server code separately. How about we move the adapter behaviour to inside those folders? Like:
# For server adapter behaviour.
lib/grpc/server/adapter.ex
# For client adapter behaviour.
lib/grpc/client/adapter.ex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to prefer grouping all behaviours under a given namespace to make it easy to find all of them (so adapters.xxx), but I'm not set too hard on either option :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about grouping the code by context, something like this:
- /client/
- stream.ex
- adapter.ex
- adapter/
- gun.ex
- /server.ex
- /server/
- stream.ex
- supervisor.ex
- adapter.ex
- adapter/
- cowboy.ex
- cowboy/
- handler.ex
WDYT? You guys have a bigger picture and will work on the project more than me 😅 , so let me know which one suits better to you 😄
BTW, @polvalente if you are in a rush to merge this, please, go ahead and take the PR if you want!! In the end, I can only take this from time to time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No rush at all! I think this discussion is somewhat important and relevant to this PR as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the suggested tree, but I would have the adapters
directory instead of adapter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the suggested tree, but I would have the
adapters
directory instead ofadapter
Nice! Agree with that! I'll push a commit with that ASAP 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this? WDYT?
lib/grpc/adapter/client.ex
Outdated
@@ -0,0 +1,27 @@ | |||
defmodule GRPC.ClientAdapter do | |||
@moduledoc false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a moduledoc here is really important so that this module is discoverable in the docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree!
lib/grpc/adapter/server.ex
Outdated
@@ -0,0 +1,24 @@ | |||
defmodule GRPC.ServerAdapter do | |||
@moduledoc false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically the same considerations as the client behaviour
Also, disclaimer: I'm not a maintainer for the library and have just added my 2 cents because I'm interested in having this library work in production |
Hi Paulo, it's a pleasure to speak with you again. **Note, some of the PRs open here I already include in Falco. But we are not yet ready for production.I'm sharing this just for your knowledge. |
@sleipnir that's awesome to know! For sure I'll take a look into it! |
@avillen let me know if you want me to take over this PR |
hi @polvalente!! thanks for your review 😄 I can take care of apply your suggestions, but where are you saying to apply those changes, on this repo or in this one https://github.com/eigr/falco? |
Here! I'm a maintainer for the repo now. I'm in touch with the people at falco and at another fork as well, so we can bring the best changes from the forks here |
Nice to hear that! I'll take care of it along the week 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@polvalente I was reading your suggestions and I totally agree with them, I just have a few smaill questions
lib/grpc/adapter/client.ex
Outdated
@@ -0,0 +1,27 @@ | |||
defmodule GRPC.ClientAdapter do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that, if we want to follow the same criteria with the GRPC.Server
, we wont be able because there is already another file (lib/grpc/server.ex
) with the same module name. WDYT about using GRPC.Adapter.Client
and GRPC.Adapter.Server
?
lib/grpc/adapter/client.ex
Outdated
|
||
@callback disconnect(channel) :: {:ok, channel} | {:error, any} | ||
|
||
@type stream :: Stream.t() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, the only reason about using this proxy type is because I did it to all the types. Also the idea is that in this way it could be used in the specs of the definitions. But I don't have a strong reason to do that. How do you consider using the proxy type or using it directly inline?
lib/grpc/adapter/client.ex
Outdated
@type message :: binary() | ||
@callback send_request(stream, message, opts) :: stream | ||
|
||
@type headers :: [{String.t(), String.t()}] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totally right
lib/grpc/adapter/client.ex
Outdated
@@ -0,0 +1,27 @@ | |||
defmodule GRPC.ClientAdapter do | |||
@moduledoc false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree!
lib/grpc/adapter/client.ex
Outdated
@callback send_request(stream, message, opts) :: stream | ||
|
||
@type headers :: [{String.t(), String.t()}] | ||
@type fin :: :fin | :nofin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool! 😄
…thub.com:avillen/grpc into define-behaviours-for-client-and-server-adapters
lib/grpc/server.ex
Outdated
def start_endpoint(endpoint, port, opts \\ []) do | ||
servers = endpoint.__meta__(:servers) | ||
servers = GRPC.Server.servers_to_map(servers) | ||
adapter = Keyword.get(opts, :adapter, GRPC.Adapter.Cowboy) | ||
adapter = Keyword.get(opts, :adapter, GRPC.Server.Adapters.Cowboy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you replace all of these Keyword.get(a, b, c) with Keyword.get(a, b) || c?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just the ones this PR touches is fine, though
lib/grpc/server.ex
Outdated
@type endpoint :: Adapter.endpoint() | ||
@type server_port :: Adapter.server_port() | ||
@type servers_list :: module | [module] | ||
@type servers_map :: Adapter.servers_map() | ||
@type stream :: Adapter.stream() | ||
@type headers :: Adapter.headers() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's avoid proxy types
lib/grpc/server/adapter.ex
Outdated
@type reply :: binary() | ||
@callback send_reply(state, reply, opts) :: any() | ||
|
||
@type headers :: map() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we rework headers into keyword() on a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say yes! I can take care of it 😄
lib/grpc/stub.ex
Outdated
@@ -98,6 +98,8 @@ defmodule GRPC.Stub do | |||
end | |||
end | |||
|
|||
@type channel :: GRPC.ClientAdapter.channel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
proxy type
lib/grpc/stub.ex
Outdated
@@ -125,7 +127,7 @@ defmodule GRPC.Stub do | |||
* `:accepted_compressors` - tell servers accepted compressors, this can be used without `:compressor` | |||
* `:headers` - headers to attach to each request | |||
""" | |||
@spec connect(String.t(), Keyword.t()) :: {:ok, GRPC.Channel.t()} | {:error, any} | |||
@spec connect(String.t(), Keyword.t()) :: {:ok, channel} | {:error, any} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use GRPC.Channel.t() here as it was before
lib/grpc/client/adapter.ex
Outdated
alias GRPC.Client.Stream | ||
alias GRPC.Channel | ||
|
||
@type opts :: any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a keyword
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think currently the code passes maps as options. We need to rework both this and header maps to become keywords and lists of tuples instead
@polvalente @wingyplus I think I have address all your suggestions, but pretty sure I'm missing something 😅 Do you mind taking a look again? Those missing things I think that we can take care of it in another PR: |
lib/grpc/server/adapter.ex
Outdated
|
||
alias GRPC.Server.Adapters.Cowboy.Handler | ||
|
||
@callback start(atom, %{String.t() => [module]}, non_neg_integer(), Keyword.t()) :: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be atom() instead of just atom?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
module might be the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@callback start(atom, %{String.t() => [module]}, non_neg_integer(), Keyword.t()) :: | |
@callback start(atom(), %{String.t() => [module()]}, non_neg_integer(), Keyword.t()) :: |
lib/grpc/server/adapter.ex
Outdated
alias GRPC.Server.Adapters.Cowboy.Handler | ||
|
||
@callback start(atom, %{String.t() => [module]}, non_neg_integer(), Keyword.t()) :: | ||
{atom, any, non_neg_integer} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{atom, any, non_neg_integer} | |
{atom(), any(), non_neg_integer()} |
lib/grpc/server/adapter.ex
Outdated
@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} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@callback stop(atom, %{String.t() => [module]}) :: :ok | {:error, :not_found} | |
@callback stop(atom(), %{String.t() => [module()]}) :: :ok | {:error, :not_found} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can standardize on having parens :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree and done (at least everywhere I see 😂 ), but I think that we should think about how we can add a validation about that 😅
Just a quick look from my mobile:
|
@wingyplus I think it is done 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
Hi 👋
To give some context, I was trying to add tests to my integration using
grpc
. I found that you can set the adapter that you want on both client and server, and that makes it easier to test my code. I'm a big fan ofMox
, I really think that simplifies the way to add tests, but we need abehaviour
over we can create the mock.This MR aims to create behaviours for both clients and servers, but for my personal use-case, with the
client
one would be enough. With this, an incomplete test example could be:I'm not a person with a lot of knowledge of both
grpc
and this library itself. This MR could be considered in WIP because and not pretty sure if I have properly defined thetypespecs
, or be closed without any problem if you consider.Cheers and thanks for the library! Awesome job!