From b34b75f08e84ba5e53cd0505c32f4ba41e21b6a4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Christopher=20Cot=C3=A9?= Date: Wed, 19 Feb 2020 13:13:46 -0600 Subject: [PATCH] use structs as interface to role functions --- lib/wampex.ex | 19 +++++---- lib/wampex/roles/callee.ex | 61 +++++++++++++++++++--------- lib/wampex/roles/caller.ex | 30 +++++++------- lib/wampex/roles/peer.ex | 35 ++++++++++++++--- lib/wampex/roles/publisher.ex | 30 +++++++------- lib/wampex/roles/subscriber.ex | 33 +++++++++++++--- lib/wampex/session.ex | 7 +++- test/support/test_callee.ex | 11 ++++-- test/support/test_subscriber.ex | 5 ++- test/wampex_test.exs | 70 +++++++++++++++++++++++++-------- 10 files changed, 210 insertions(+), 91 deletions(-) diff --git a/lib/wampex.ex b/lib/wampex.ex index 7e46a89..dacb7db 100644 --- a/lib/wampex.ex +++ b/lib/wampex.ex @@ -6,6 +6,8 @@ defmodule Wampex do alias Wampex.Session, as: Sess alias Wampex.Role.{Callee, Subscriber} + alias Wampex.Role.Callee.Register + alias Wampex.Role.Subscriber.Subscribe @type message_part :: integer() | binary() | map() | list() @type message :: nonempty_list(message_part()) @@ -64,18 +66,19 @@ defmodule Wampex do @spec transport_name(module()) :: module() def transport_name(name), do: Module.concat([name, Transport]) - @spec subscribe(name :: module(), topic :: binary(), timeout :: integer()) :: {:ok, integer()} - def subscribe(name, topic, timeout \\ 5000) do - {:ok, id} = Sess.send_request(session_name(name), Subscriber.subscribe(topic), timeout) - Registry.register(subscriber_registry_name(name), id, topic) + @spec subscribe(name :: module(), subscription :: Subscribe.t(), timeout :: integer()) :: + {:ok, integer()} + def subscribe(name, %Subscribe{topic: t} = sub, timeout \\ 5000) do + {:ok, id} = Sess.send_request(session_name(name), Subscriber.subscribe(sub), timeout) + Registry.register(subscriber_registry_name(name), id, t) {:ok, id} end - @spec register(name :: module(), procedure :: binary(), timeout :: integer()) :: + @spec register(name :: module(), register :: Register.t(), timeout :: integer()) :: {:ok, integer()} - def register(name, procedure, timeout \\ 5000) do - {:ok, id} = Sess.send_request(session_name(name), Callee.register(procedure), timeout) - Registry.register(callee_registry_name(name), id, procedure) + def register(name, %Register{procedure: p} = reg, timeout \\ 5000) do + {:ok, id} = Sess.send_request(session_name(name), Callee.register(reg), timeout) + Registry.register(callee_registry_name(name), id, p) {:ok, id} end diff --git a/lib/wampex/roles/callee.ex b/lib/wampex/roles/callee.ex index b834d68..cbbcd99 100644 --- a/lib/wampex/roles/callee.ex +++ b/lib/wampex/roles/callee.ex @@ -13,35 +13,58 @@ defmodule Wampex.Role.Callee do @invocation 68 @yield 70 - @impl true - def add(roles) do - Map.put(roles, :callee, %{}) + defmodule Register do + @moduledoc false + @enforce_keys [:procedure] + defstruct [:procedure, options: %{}] + + @type t :: %__MODULE__{ + procedure: binary(), + options: map() + } end - @spec register(procedure :: binary()) :: Wampex.message() - def register(procedure) do - [@register, %{}, procedure] + defmodule Unregister do + @moduledoc false + @enforce_keys [:registration_id] + defstruct [:registration_id] + + @type t :: %__MODULE__{ + registration_id: integer() + } end - @spec unregister(registration_id :: integer()) :: Wampex.message() - def unregister(registration_id) do - [@unregister, registration_id] + defmodule Yield do + @moduledoc false + @enforce_keys [:request_id] + defstruct [:request_id, :arg_list, :arg_kw, options: %{}] + + @type t :: %__MODULE__{ + request_id: integer(), + arg_list: nonempty_list(any()) | nil, + arg_kw: map() | nil, + options: map() + } + end + + @impl true + def add(roles) do + Map.put(roles, :callee, %{}) end - @spec yield(request_id :: integer()) :: Wampex.message() - def yield(request_id) do - [@yield, request_id, %{}] + @spec register(Register.t()) :: Wampex.message() + def register(%Register{procedure: p, options: opts}) do + [@register, opts, p] end - @spec yield(request_id :: integer(), arg_l :: nonempty_list(any())) :: Wampex.message() - def yield(request_id, arg_l) do - [@yield, request_id, %{}, arg_l] + @spec unregister(Unregister.t()) :: Wampex.message() + def unregister(%Unregister{registration_id: ri}) do + [@unregister, ri] end - @spec yield(request_id :: integer(), arg_l :: nonempty_list(any()), arg_kw :: map()) :: - Wampex.message() - def yield(request_id, arg_l, arg_kw) do - [@yield, request_id, %{}, arg_l, arg_kw] + @spec yield(Yield.t()) :: Wampex.message() + def yield(%Yield{request_id: ri, arg_list: al, arg_kw: akw, options: opts}) do + [@yield, ri, opts, al, akw] end @impl true diff --git a/lib/wampex/roles/caller.ex b/lib/wampex/roles/caller.ex index 5bd5f4d..2ab1771 100644 --- a/lib/wampex/roles/caller.ex +++ b/lib/wampex/roles/caller.ex @@ -9,25 +9,27 @@ defmodule Wampex.Role.Caller do @call 48 @result 50 + defmodule Call do + @moduledoc false + @enforce_keys [:procedure] + defstruct [:procedure, :arg_list, :arg_kw, options: %{}] + + @type t :: %__MODULE__{ + procedure: binary(), + arg_list: nonempty_list(any()) | nil, + arg_kw: map() | nil, + options: map() + } + end + @impl true def add(roles) do Map.put(roles, :caller, %{}) end - @spec call(procedure :: binary()) :: Wampex.message() - def call(procedure) do - [@call, %{}, procedure] - end - - @spec call(procedure :: binary(), arg_l :: nonempty_list(any())) :: Wampex.message() - def call(procedure, arg_l) do - [@call, %{}, procedure, arg_l] - end - - @spec call(procedure :: binary(), arg_l :: nonempty_list(any()), arg_kw :: map()) :: - Wampex.message() - def call(procedure, arg_l, arg_kw) do - [@call, %{}, procedure, arg_l, arg_kw] + @spec call(Call.t()) :: Wampex.message() + def call(%Call{procedure: p, arg_list: al, arg_kw: akw, options: opts}) do + [@call, opts, p, al, akw] end @impl true diff --git a/lib/wampex/roles/peer.ex b/lib/wampex/roles/peer.ex index a02a4be..4cc0fe2 100644 --- a/lib/wampex/roles/peer.ex +++ b/lib/wampex/roles/peer.ex @@ -14,17 +14,40 @@ defmodule Wampex.Role.Peer do @goodbye 6 @error 8 + defmodule Hello do + @moduledoc false + @enforce_keys [:realm, :roles] + defstruct [:realm, :roles, agent: "WAMPex"] + + @type t :: %__MODULE__{ + realm: binary(), + roles: nonempty_list(module()), + agent: binary() + } + end + + defmodule Goodbye do + @moduledoc false + @enforce_keys [:reason] + defstruct [:reason, options: %{}] + + @type t :: %__MODULE__{ + reason: binary(), + options: map() + } + end + @impl true def add(roles), do: roles - @spec hello(realm :: binary(), roles :: nonempty_list(module())) :: Wampex.message() - def hello(realm, roles) do - [@hello, realm, %{roles: Enum.reduce(roles, %{}, fn r, acc -> r.add(acc) end)}] + @spec hello(Hello.t()) :: Wampex.message() + def hello(%Hello{realm: r, roles: roles, agent: agent}) do + [@hello, r, %{agent: agent, roles: Enum.reduce(roles, %{}, fn r, acc -> r.add(acc) end)}] end - @spec goodbye(reason :: binary()) :: Wampex.message() - def goodbye(reason) do - [@goodbye, %{}, reason] + @spec goodbye(Goodbye.t()) :: Wampex.message() + def goodbye(%Goodbye{reason: r, options: opts}) do + [@goodbye, opts, r] end @impl true diff --git a/lib/wampex/roles/publisher.ex b/lib/wampex/roles/publisher.ex index 5e62588..d8ba01b 100644 --- a/lib/wampex/roles/publisher.ex +++ b/lib/wampex/roles/publisher.ex @@ -9,25 +9,27 @@ defmodule Wampex.Role.Publisher do @publish 16 @published 17 - @impl true - def add(roles) do - Map.put(roles, :publisher, %{}) - end + defmodule Publish do + @moduledoc false + @enforce_keys [:topic] + defstruct [:topic, :arg_list, :arg_kw, options: %{}] - @spec publish(topic :: binary()) :: Wampex.message() - def publish(topic) do - [@publish, %{}, topic] + @type t :: %__MODULE__{ + topic: binary(), + arg_list: nonempty_list(any()) | nil, + arg_kw: map() | nil, + options: map() + } end - @spec publish(topic :: binary(), arg_l :: nonempty_list(any())) :: Wampex.message() - def publish(topic, arg_l) do - [@publish, %{}, topic, arg_l] + @impl true + def add(roles) do + Map.put(roles, :publisher, %{}) end - @spec publish(topic :: binary(), arg_l :: nonempty_list(any()), arg_kw :: map()) :: - Wampex.message() - def publish(topic, arg_l, arg_kw) do - [@publish, %{}, topic, arg_l, arg_kw] + @spec publish(Publish.t()) :: Wampex.message() + def publish(%Publish{topic: t, options: opts, arg_list: al, arg_kw: akw}) do + [@publish, opts, t, al, akw] end @impl true diff --git a/lib/wampex/roles/subscriber.ex b/lib/wampex/roles/subscriber.ex index 814c3de..6a6419d 100644 --- a/lib/wampex/roles/subscriber.ex +++ b/lib/wampex/roles/subscriber.ex @@ -12,19 +12,40 @@ defmodule Wampex.Role.Subscriber do @unsubscribed 35 @event 36 + defmodule Subscribe do + @moduledoc false + @enforce_keys [:topic] + defstruct [:topic, options: %{}] + + @type t :: %__MODULE__{ + topic: binary(), + options: map() + } + end + + defmodule Unsubscribe do + @moduledoc false + @enforce_keys [:subscription_id] + defstruct [:subscription_id] + + @type t :: %__MODULE__{ + subscription_id: integer() + } + end + @impl true def add(roles) do Map.put(roles, :subscriber, %{}) end - @spec subscribe(topic :: binary()) :: Wampex.message() - def subscribe(topic) do - [@subscribe, %{}, topic] + @spec subscribe(Subscribe.t()) :: Wampex.message() + def subscribe(%Subscribe{topic: t, options: opts}) do + [@subscribe, opts, t] end - @spec unsubscribe(subscription_id :: integer()) :: Wampex.message() - def unsubscribe(subscription_id) do - [@unsubscribe, subscription_id] + @spec unsubscribe(Unsubscribe.t()) :: Wampex.message() + def unsubscribe(%Unsubscribe{subscription_id: si}) do + [@unsubscribe, si] end @impl true diff --git a/lib/wampex/session.ex b/lib/wampex/session.ex index e35fd7f..7803f79 100644 --- a/lib/wampex/session.ex +++ b/lib/wampex/session.ex @@ -9,6 +9,7 @@ defmodule Wampex.Session do alias StatesLanguage, as: SL alias Wampex.Realm alias Wampex.Role.Peer + alias Wampex.Role.Peer.Hello alias Wampex.Serializer.MessagePack alias __MODULE__, as: Sess alias Wampex.Transport.WebSocket @@ -106,7 +107,7 @@ defmodule Wampex.Session do data: %Sess{transport: tt, transport_pid: t, roles: roles, realm: %Realm{name: realm}} } = data ) do - tt.send_request(t, Peer.hello(realm, roles)) + tt.send_request(t, Peer.hello(%Hello{realm: realm, roles: roles})) {:ok, data, [{:next_event, :internal, :hello_sent}]} end @@ -227,10 +228,12 @@ defmodule Wampex.Session do defp do_send(r_id, tt, t, message) do {request_id, message} = maybe_inject_request_id(r_id, message) - tt.send_request(t, message) + tt.send_request(t, remove_nil_values(message)) request_id end + defp remove_nil_values(message), do: Enum.reject(message, &is_nil/1) + defp maybe_inject_request_id(r_id, [@yield | _] = message), do: {r_id, message} defp maybe_inject_request_id(r_id, message) do diff --git a/test/support/test_callee.ex b/test/support/test_callee.ex index 867582c..9810705 100644 --- a/test/support/test_callee.ex +++ b/test/support/test_callee.ex @@ -3,13 +3,14 @@ defmodule TestCallee do use GenServer require Logger alias Wampex.Role.Callee + alias Callee.{Register, Unregister, Yield} def start_link(test, name, device) do GenServer.start_link(__MODULE__, {test, name, device}) end def init({test, name, device}) do - {:ok, reg} = Wampex.register(name, "com.actuator.#{device}.light") + {:ok, reg} = Wampex.register(name, %Register{procedure: "com.actuator.#{device}.light"}) send(test, {:registered, reg}) {:ok, {test, name, reg}} end @@ -21,7 +22,11 @@ defmodule TestCallee do Wampex.cast_send_request( name, - Callee.yield(id, [:ok], %{color: Map.get(arg_kw, "color")}) + Callee.yield(%Yield{ + request_id: id, + arg_list: [:ok], + arg_kw: %{color: Map.get(arg_kw, "color")} + }) ) {:noreply, state} @@ -30,7 +35,7 @@ defmodule TestCallee do def terminate(_reason, {_test, name, reg}) do Wampex.send_request( name, - Callee.unregister(reg) + Callee.unregister(%Unregister{registration_id: reg}) ) :ok diff --git a/test/support/test_subscriber.ex b/test/support/test_subscriber.ex index 21afd29..2e061a6 100644 --- a/test/support/test_subscriber.ex +++ b/test/support/test_subscriber.ex @@ -3,13 +3,14 @@ defmodule TestSubscriber do use GenServer require Logger alias Wampex.Role.Subscriber + alias Subscriber.{Subscribe, Unsubscribe} def start_link(test, name, topic) do GenServer.start_link(__MODULE__, {test, name, topic}) end def init({test, name, topic}) do - {:ok, sub} = Wampex.subscribe(name, topic) + {:ok, sub} = Wampex.subscribe(name, %Subscribe{topic: topic}) send(test, {:subscribed, sub}) {:ok, {test, name, sub}} end @@ -23,7 +24,7 @@ defmodule TestSubscriber do def terminate(_r, {_test, name, sub}) do Wampex.send_request( name, - Subscriber.unsubscribe(sub) + Subscriber.unsubscribe(%Unsubscribe{subscription_id: sub}) ) :ok diff --git a/test/wampex_test.exs b/test/wampex_test.exs index 0a4163c..59354ca 100644 --- a/test/wampex_test.exs +++ b/test/wampex_test.exs @@ -4,6 +4,11 @@ defmodule WampexTest do alias Wampex.{Realm, Session} alias Wampex.Role.{Callee, Caller, Peer, Publisher, Subscriber} + alias Callee.{Register, Unregister, Yield} + alias Caller.Call + alias Peer.{Goodbye, Hello} + alias Publisher.Publish + alias Subscriber.{Subscribe, Unsubscribe} require Logger @url "ws://localhost:18080/ws" @@ -34,17 +39,24 @@ defmodule WampexTest do end test "Callee.register" do - assert [64, %{}, "test"] = Callee.register("test") + assert [64, %{}, "test"] = Callee.register(%Register{procedure: "test"}) end test "Callee.unregister" do - assert [66, 123_456] = Callee.unregister(123_456) + assert [66, 123_456] = Callee.unregister(%Unregister{registration_id: 123_456}) end test "Callee.yield" do - assert [70, 1234, %{}] = Callee.yield(1234) - assert [70, 1234, %{}, []] = Callee.yield(1234, []) - assert [70, 1234, %{}, [], %{}] = Callee.yield(1234, [], %{}) + assert [70, 1234, %{}, nil, nil] = Callee.yield(%Yield{request_id: 1234}) + assert [70, 1234, %{}, ["1"], nil] = Callee.yield(%Yield{request_id: 1234, arg_list: ["1"]}) + + assert [70, 1234, %{test: 1}, [2], %{}] = + Callee.yield(%Yield{ + request_id: 1234, + arg_list: [2], + arg_kw: %{}, + options: %{test: 1} + }) end test "Caller.add" do @@ -52,9 +64,16 @@ defmodule WampexTest do end test "Caller.call" do - assert [48, %{}, "test"] = Caller.call("test") - assert [48, %{}, "test", []] = Caller.call("test", []) - assert [48, %{}, "test", [], %{}] = Caller.call("test", [], %{}) + assert [48, %{}, "test", nil, nil] = Caller.call(%Call{procedure: "test"}) + assert [48, %{}, "test", [1], nil] = Caller.call(%Call{procedure: "test", arg_list: [1]}) + + assert [48, %{ok: 1}, "test", [1], %{test: 1}] = + Caller.call(%Call{ + procedure: "test", + arg_list: [1], + arg_kw: %{test: 1}, + options: %{ok: 1} + }) end test "Peer.add" do @@ -62,11 +81,12 @@ defmodule WampexTest do end test "Peer.hello" do - assert [1, "test", %{roles: %{callee: %{}}}] = Peer.hello("test", [Callee]) + assert [1, "test", %{agent: "WAMPex", roles: %{callee: %{}}}] = + Peer.hello(%Hello{realm: "test", roles: [Callee]}) end test "Peer.gooodbye" do - assert [6, %{}, "test"] = Peer.goodbye("test") + assert [6, %{}, "test"] = Peer.goodbye(%Goodbye{reason: "test"}) end test "Publisher.add" do @@ -74,9 +94,16 @@ defmodule WampexTest do end test "Publisher.publish" do - assert [16, %{}, "test"] = Publisher.publish("test") - assert [16, %{}, "test", []] = Publisher.publish("test", []) - assert [16, %{}, "test", [], %{}] = Publisher.publish("test", [], %{}) + assert [16, %{}, "test", nil, nil] = Publisher.publish(%Publish{topic: "test"}) + assert [16, %{}, "test", [1], nil] = Publisher.publish(%Publish{topic: "test", arg_list: [1]}) + + assert [16, %{test: 2}, "test", [1], %{test: 1}] = + Publisher.publish(%Publish{ + topic: "test", + arg_list: [1], + arg_kw: %{test: 1}, + options: %{test: 2} + }) end test "Subscriber.add" do @@ -84,11 +111,12 @@ defmodule WampexTest do end test "Subscriber.subscribe" do - assert [32, %{}, "test"] = Subscriber.subscribe("test") + assert [32, %{test: 1}, "test"] = + Subscriber.subscribe(%Subscribe{topic: "test", options: %{test: 1}}) end test "Subscriber.unsubscribe" do - assert [34, 1234] = Subscriber.unsubscribe(1234) + assert [34, 1234] = Subscriber.unsubscribe(%Unsubscribe{subscription_id: 1234}) end test "callee registration" do @@ -109,7 +137,11 @@ defmodule WampexTest do {:ok, ["ok"], %{"color" => "#FFFFFF"}} = Wampex.send_request( caller_name, - Caller.call("com.actuator.#{@device}.light", [1], %{color: "#FFFFFF"}) + Caller.call(%Call{ + procedure: "com.actuator.#{@device}.light", + arg_list: [1], + arg_kw: %{color: "#FFFFFF"} + }) ) assert_receive {:invocation, _, _, _, _, _} @@ -132,7 +164,11 @@ defmodule WampexTest do Wampex.cast_send_request( TestPublisher, - Publisher.publish("com.data.temp", [12.5, 45.6, 87.5], %{loc: "60645"}) + Publisher.publish(%Publish{ + topic: "com.data.temp", + arg_list: [12.5, 45.6, 87.5], + arg_kw: %{loc: "60645"} + }) ) assert_receive {:event, _, _, _, _, _} -- 2.45.3