diff options
author | Hubert Chathi <hubert@uhoreg.ca> | 2020-08-29 22:28:04 -0400 |
---|---|---|
committer | Hubert Chathi <hubert@uhoreg.ca> | 2020-08-29 22:43:12 -0400 |
commit | 126ff42d912dfea9e251e2b98bdcf8f295b8707e (patch) | |
tree | 5820307fc63e643e36b9534686c3585d5ad3b346 | |
parent | add module for handling .well-known (diff) |
make Polyjuice.Client.start_link return {:ok, pid}, like a normal start_link
add a function that returns a client struct from the pid, and add a stop
function to Polyjuice.Client.stop
-rw-r--r-- | lib/mix/tasks/polyjuice/login.ex | 6 | ||||
-rw-r--r-- | lib/mix/tasks/polyjuice/logout.ex | 6 | ||||
-rw-r--r-- | lib/polyjuice/client.ex | 91 | ||||
-rw-r--r-- | lib/polyjuice/client/low_level.ex | 5 | ||||
-rw-r--r-- | test/polyjuice/client/media_test.exs | 6 | ||||
-rw-r--r-- | test/polyjuice/client/room_test.exs | 2 | ||||
-rw-r--r-- | test/polyjuice/client/sync_test.exs | 26 | ||||
-rw-r--r-- | test/polyjuice/client_test.exs | 124 | ||||
-rw-r--r-- | test/support/dummy_client.ex | 22 |
9 files changed, 170 insertions, 118 deletions
diff --git a/lib/mix/tasks/polyjuice/login.ex b/lib/mix/tasks/polyjuice/login.ex index 3ae1234..f485ed5 100644 --- a/lib/mix/tasks/polyjuice/login.ex +++ b/lib/mix/tasks/polyjuice/login.ex @@ -51,16 +51,18 @@ defmodule Mix.Tasks.Polyjuice.Login do nil end - client = + {:ok, client_pid} = Polyjuice.Client.start_link( url, storage: storage, sync: false ) + client = Polyjuice.Client.get_client(client_pid) + ret = Polyjuice.Client.log_in_with_password(client, user_id, password) - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) if storage, do: Polyjuice.Client.Storage.close(storage) diff --git a/lib/mix/tasks/polyjuice/logout.ex b/lib/mix/tasks/polyjuice/logout.ex index 9ac65a3..71f0e4d 100644 --- a/lib/mix/tasks/polyjuice/logout.ex +++ b/lib/mix/tasks/polyjuice/logout.ex @@ -54,7 +54,7 @@ defmodule Mix.Tasks.Polyjuice.Logout do nil end - client = + {:ok, client_pid} = Polyjuice.Client.start_link( url, access_token: opts[:access_token], @@ -62,9 +62,11 @@ defmodule Mix.Tasks.Polyjuice.Logout do sync: false ) + client = Polyjuice.Client.get_client(client_pid) + ret = Polyjuice.Client.log_out(client) - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) if storage, do: Polyjuice.Client.Storage.close(storage) diff --git a/lib/polyjuice/client.ex b/lib/polyjuice/client.ex index 9f4aa8f..3f1668c 100644 --- a/lib/polyjuice/client.ex +++ b/lib/polyjuice/client.ex @@ -16,10 +16,7 @@ defmodule Polyjuice.Client do @moduledoc """ Matrix client functions. - To start a client, use `start_link/2`, and to stop it, use - `stop/3`. - - The struct in this module, or any struct that implements the + The client created by this module, or any client that implements the `Polyjuice.Client.API` protocol, can be used to connect to a Matrix server using the functions from submodules. @@ -31,7 +28,23 @@ defmodule Polyjuice.Client do The client defined in this module should work for most cases. If you want more control, you can use `Polyjuice.Client.LowLevel` instead. + To start a client with this module, create a process using `start_link/2`, + and then call `get_client/1` to get a struct that can be used with the above + modules. To stop the client, use `Polyjuice.Client.API.stop/3`. + """ + + @doc """ + Returns a specification to start the client. + + `arg` must be a list, where the first element is the base URL for the + homeserver, and the remainder of the list is options, as would be given to + `start_link/2`. For example: + + Polyjuice.Client.child_spec(["http://localhost:8008", sync: false]) + + """ + use Supervisor require Logger @typedoc """ @@ -81,14 +94,29 @@ defmodule Polyjuice.Client do will not start if there is no `storage` or `handler` provided. - `sync_filter`: the filter to use for the sync. Defaults to no filter. """ - @spec start_link(base_url :: String.t(), opts :: Keyword.t()) :: t() - def start_link(base_url, opts \\ []) when is_binary(base_url) and is_list(opts) do + @spec start_link(base_url :: String.t(), opts :: Keyword.t()) :: {:ok, pid} + def start_link(base_url, opts) when is_binary(base_url) and is_list(opts) do base_url = if(String.ends_with?(base_url, "/"), do: base_url, else: base_url <> "/") |> URI.parse() client_id = Agent.get_and_update(Polyjuice.Client.ID, &{&1, &1 + 1}) + Supervisor.start_link( + __MODULE__, + [client_id, base_url, opts], + name: process_name(client_id, :supervisor) + ) + end + + def start_link(base_url) when is_binary(base_url), do: start_link(base_url, []) + + def start_link([base_url | opts]) when is_binary(base_url) and is_list(opts) do + start_link(base_url, opts) + end + + @impl Supervisor + def init([client_id, base_url, opts]) do sync = Keyword.get(opts, :sync, true) storage = Keyword.get(opts, :storage) handler = Keyword.get(opts, :handler) @@ -153,7 +181,16 @@ defmodule Polyjuice.Client do %{ access_token: access_token, user_id: user_id, - device_id: device_id + device_id: device_id, + client: %__MODULE__{ + base_url: base_url, + id: client_id, + storage: storage, + handler: handler, + sync: sync, + opts: opts, + test: Keyword.get(opts, :test, false) + } } end, [name: process_name(client_id, :state)] @@ -165,29 +202,19 @@ defmodule Polyjuice.Client do ) ] - {:ok, _pid} = - Supervisor.start_link(children, - strategy: :rest_for_one, - name: process_name(client_id, :supervisor) - ) - - %__MODULE__{ - base_url: base_url, - id: client_id, - storage: storage, - handler: handler, - sync: sync, - opts: opts, - test: Keyword.get(opts, :test, false) - } + Supervisor.init(children, strategy: :rest_for_one) end @doc """ - Stop a client. + Get a struct that implements `Polyjuice.Client.API` from the pid given by + `start_link`. """ - @spec stop(Polyjuice.Client.t(), reason :: term, timeout()) :: :ok - def stop(%__MODULE__{id: id}, reason \\ :normal, timeout \\ :infinity) do - Supervisor.stop(process_name(id, :supervisor), reason, timeout) + def get_client(pid) do + agent_pid = + Supervisor.which_children(pid) + |> Enum.find_value(fn {id, pid, _, _} -> if id == Polyjuice.Client, do: pid end) + + Agent.get(agent_pid, &Map.fetch!(&1, :client)) end @doc false @@ -244,6 +271,12 @@ defmodule Polyjuice.Client do """ @spec transaction_id(client_api :: Polyjuice.Client.API.t()) :: String.t() def transaction_id(client_api) + + @doc """ + Stop the client. + """ + @spec stop(Polyjuice.Client.t(), reason :: term, timeout()) :: :ok + def stop(client_api, reason \\ :normal, timeout \\ :infinity) end defimpl Polyjuice.Client.API do @@ -369,6 +402,10 @@ defmodule Polyjuice.Client do def transaction_id(_) do "#{Node.self()}_#{:erlang.system_time(:millisecond)}_#{:erlang.unique_integer()}" end + + def stop(%{id: id}, reason \\ :normal, timeout \\ :infinity) do + Supervisor.stop(Polyjuice.Client.process_name(id, :supervisor), reason, timeout) + end end @doc false @@ -518,7 +555,7 @@ defmodule Polyjuice.Client do ) end - if client.sync do + if client.sync && client.handler do # make sure we don't already have a sync process running kill_sync(client.id) diff --git a/lib/polyjuice/client/low_level.ex b/lib/polyjuice/client/low_level.ex index 918805e..f41cf8c 100644 --- a/lib/polyjuice/client/low_level.ex +++ b/lib/polyjuice/client/low_level.ex @@ -109,6 +109,11 @@ defmodule Polyjuice.Client.LowLevel do def transaction_id(_) do "#{Node.self()}_#{:erlang.system_time(:millisecond)}_#{:erlang.unique_integer()}" end + + def stop(_, _, _) do + # don't need to do anything to stop it + :ok + end end @doc """ diff --git a/test/polyjuice/client/media_test.exs b/test/polyjuice/client/media_test.exs index 4c545ac..abe7b83 100644 --- a/test/polyjuice/client/media_test.exs +++ b/test/polyjuice/client/media_test.exs @@ -86,7 +86,7 @@ defmodule Polyjuice.Client.MediaTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.MediaTest.Httpd", access_token: "an_access_token", @@ -95,6 +95,8 @@ defmodule Polyjuice.Client.MediaTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + {:ok, url} = Polyjuice.Client.Media.upload(client, {:file, "mix.exs"}) assert url == "mxc://example.org/abcdefg" @@ -106,7 +108,7 @@ defmodule Polyjuice.Client.MediaTest do assert content_type == "text/plain" assert Enum.join(body) == "foo" - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after diff --git a/test/polyjuice/client/room_test.exs b/test/polyjuice/client/room_test.exs index 97180b0..6c631e0 100644 --- a/test/polyjuice/client/room_test.exs +++ b/test/polyjuice/client/room_test.exs @@ -332,7 +332,7 @@ defmodule Polyjuice.Client.RoomTest do } ] - DummyClient.MultiReq.destroy(client) + Polyjuice.Client.API.stop(client) end end end diff --git a/test/polyjuice/client/sync_test.exs b/test/polyjuice/client/sync_test.exs index ae86fa4..e1085d6 100644 --- a/test/polyjuice/client/sync_test.exs +++ b/test/polyjuice/client/sync_test.exs @@ -196,7 +196,7 @@ defmodule Polyjuice.Client.SyncTest do end end - defp handle_login(session_id, _env, {_, input}) do + defp handle_login(session_id, _env, _input) do :mod_esi.deliver( session_id, 'Content-Type: application/json\r\n\r\n{"user_id":"@alice:example.org","access_token":"an_access_token","device_id":"foo"}' @@ -235,7 +235,7 @@ defmodule Polyjuice.Client.SyncTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.SyncTest.Httpd", access_token: "an_access_token", @@ -246,6 +246,8 @@ defmodule Polyjuice.Client.SyncTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + assert_receive({:polyjuice_client, :sync_connected}) assert_receive( @@ -329,7 +331,7 @@ defmodule Polyjuice.Client.SyncTest do 1000 ) - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after @@ -362,7 +364,7 @@ defmodule Polyjuice.Client.SyncTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.SyncTest.Httpd", handler: self(), @@ -371,6 +373,8 @@ defmodule Polyjuice.Client.SyncTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + {:ok, _} = Polyjuice.Client.log_in_with_password(client, "@alice:example.org", "password") assert_receive({:polyjuice_client, :sync_connected}) @@ -384,7 +388,7 @@ defmodule Polyjuice.Client.SyncTest do |> Supervisor.which_children() |> Enum.find(fn {id, _, _, _} -> id == Polyjuice.Client.Sync end) == nil - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after @@ -417,7 +421,7 @@ defmodule Polyjuice.Client.SyncTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd.LoggedOut", access_token: "an_access_token", @@ -428,6 +432,8 @@ defmodule Polyjuice.Client.SyncTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + assert_receive({:polyjuice_client, :logged_out, {false}}) # make sure the sync has time to stop @@ -438,7 +444,7 @@ defmodule Polyjuice.Client.SyncTest do |> Enum.find_value(fn {id, pid, _, _} -> if id == Polyjuice.Client.Sync, do: pid end) == :undefined - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after @@ -471,7 +477,7 @@ defmodule Polyjuice.Client.SyncTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd.LoggedOut", access_token: "an_access_token", @@ -481,6 +487,8 @@ defmodule Polyjuice.Client.SyncTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + assert_receive({:polyjuice_client, :logged_out, {false}}) # make sure the sync has time to stop @@ -491,7 +499,7 @@ defmodule Polyjuice.Client.SyncTest do |> Enum.find_value(fn {id, pid, _, _} -> if id == Polyjuice.Client.Sync, do: pid end) == :undefined - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after diff --git a/test/polyjuice/client_test.exs b/test/polyjuice/client_test.exs index d11239c..d9f95a8 100644 --- a/test/polyjuice/client_test.exs +++ b/test/polyjuice/client_test.exs @@ -150,7 +150,7 @@ defmodule Polyjuice.ClientTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd/", access_token: "an_access_token", @@ -159,6 +159,8 @@ defmodule Polyjuice.ClientTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + # binary body assert Polyjuice.Client.API.call( client, @@ -187,7 +189,7 @@ defmodule Polyjuice.ClientTest do } ) == {:ok, %{"foo" => "bar"}} - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after @@ -219,7 +221,7 @@ defmodule Polyjuice.ClientTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd/", access_token: nil, @@ -229,18 +231,18 @@ defmodule Polyjuice.ClientTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + Polyjuice.Client.log_in_with_password(client, "@alice:example.org", "password") assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"m.id.user_login", "@alice:example.org", "foo"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "m.id.user_login", + user_id: "@alice:example.org", + device_id: "foo" + } assert_receive({:polyjuice_client, :logged_in, {"@alice:example.org", "foo", _}}) @@ -264,11 +266,7 @@ defmodule Polyjuice.ClientTest do assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token - } -> - access_token - end + &Map.fetch!(&1, :access_token) ) == nil Polyjuice.Client.log_in_with_password( @@ -281,14 +279,12 @@ defmodule Polyjuice.ClientTest do assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"m.id.thirdparty_login", "@alice:example.org", "foo"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "m.id.thirdparty_login", + user_id: "@alice:example.org", + device_id: "foo" + } Polyjuice.Client.log_in_with_password( client, @@ -300,14 +296,12 @@ defmodule Polyjuice.ClientTest do assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"m.id.phone_login", "@alice:example.org", "foo"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "m.id.phone_login", + user_id: "@alice:example.org", + device_id: "foo" + } Polyjuice.Client.log_in_with_password( client, @@ -321,16 +315,14 @@ defmodule Polyjuice.ClientTest do assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"ca.uhoreg.foo_login", "@alice:example.org", "foo"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "ca.uhoreg.foo_login", + user_id: "@alice:example.org", + device_id: "foo" + } - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after @@ -344,7 +336,7 @@ defmodule Polyjuice.ClientTest do # if we start a client and specify the access token, user ID, and device # ID, then those values get stored - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:8008/", access_token: "an_access_token", @@ -355,16 +347,16 @@ defmodule Polyjuice.ClientTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"an_access_token", "@alice:example.org", "a_device"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "an_access_token", + user_id: "@alice:example.org", + device_id: "a_device" + } assert Polyjuice.Client.Storage.kv_get(storage, "ca.uhoreg.polyjuice", "access_token") == "an_access_token" @@ -375,12 +367,12 @@ defmodule Polyjuice.ClientTest do assert Polyjuice.Client.Storage.kv_get(storage, "ca.uhoreg.polyjuice", "device_id") == "a_device" - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) # if we start a client and don't specify them, but they're stored, use the # stored values - client2 = + {:ok, client2_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:8008/", sync: false, @@ -388,16 +380,16 @@ defmodule Polyjuice.ClientTest do test: true ) + client2 = Polyjuice.Client.get_client(client2_pid) + assert Agent.get( Polyjuice.Client.process_name(client2.id, :state), - fn %{ - access_token: access_token, - user_id: user_id, - device_id: device_id - } -> - {access_token, user_id, device_id} - end - ) == {"an_access_token", "@alice:example.org", "a_device"} + &Map.take(&1, [:access_token, :user_id, :device_id]) + ) == %{ + access_token: "an_access_token", + user_id: "@alice:example.org", + device_id: "a_device" + } end test "invalidates token if server says we're logged out" do @@ -424,7 +416,7 @@ defmodule Polyjuice.ClientTest do port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port) - client = + {:ok, client_pid} = Polyjuice.Client.start_link( "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd.LoggedOut", access_token: "some_token", @@ -435,21 +427,19 @@ defmodule Polyjuice.ClientTest do test: true ) + client = Polyjuice.Client.get_client(client_pid) + Polyjuice.Client.Room.send_event(client, "!a_room:example.org", "m.room.message", %{}) assert Agent.get( Polyjuice.Client.process_name(client.id, :state), - fn %{ - access_token: access_token - } -> - access_token - end + &Map.fetch!(&1, :access_token) ) == nil assert Polyjuice.Client.Storage.kv_get(storage, "ca.uhoreg.polyjuice", "access_token") == nil - Polyjuice.Client.stop(client) + Polyjuice.Client.API.stop(client) :inets.stop(:httpd, httpd_pid) after diff --git a/test/support/dummy_client.ex b/test/support/dummy_client.ex index 7c1556e..bc0d202 100644 --- a/test/support/dummy_client.ex +++ b/test/support/dummy_client.ex @@ -32,6 +32,11 @@ defmodule DummyClient do end def transaction_id(_), do: "txn_id" + + def stop(_, _, _) do + # don't need to do anything to stop it + :ok + end end defmodule MultiReq do @@ -47,14 +52,6 @@ defmodule DummyClient do } end - def destroy(%{pid: pid}) do - # make sure we weren't expecting any more requests - remaining = Agent.get(pid, & &1) - assert remaining == [] - - Process.exit(pid, :kill) - end - defimpl Polyjuice.Client.API do def call(%{pid: pid}, endpoint) do {request, result} = @@ -78,6 +75,15 @@ defmodule DummyClient do end def transaction_id(_), do: "txn_id" + + def stop(%{pid: pid}, _, _) do + # make sure we weren't expecting any more requests + remaining = Agent.get(pid, & &1) + assert remaining == [] + + Process.exit(pid, :kill) + :ok + end end end end |