diff options
author | Hubert Chathi <hubert@uhoreg.ca> | 2020-04-24 20:19:27 -0400 |
---|---|---|
committer | Hubert Chathi <hubert@uhoreg.ca> | 2020-04-24 20:19:27 -0400 |
commit | 68463ae802e5acd59a4f2369427c87c961415c2e (patch) | |
tree | f91b3e5d666b0c774f5a4de445ef9e731f54a15a | |
parent | add methods for paginating room history, and manually syncing (diff) |
use a protocol for transforming HTTP responses
19 files changed, 217 insertions, 122 deletions
diff --git a/lib/polyjuice/client.ex b/lib/polyjuice/client.ex index ef68a2a..a1d63c3 100644 --- a/lib/polyjuice/client.ex +++ b/lib/polyjuice/client.ex @@ -119,7 +119,6 @@ defmodule Polyjuice.Client do headers: headers, url: url, body: body, - transform: transform, auth_required: auth_required } = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, base_url) @@ -143,7 +142,13 @@ defmodule Polyjuice.Client do ) Logger.debug("status code #{status_code}") - transform.(status_code, resp_headers, body) + + Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + status_code, + resp_headers, + body + ) end end diff --git a/lib/polyjuice/client/endpoint.ex b/lib/polyjuice/client/endpoint.ex index 97eb277..1b6b7b5 100644 --- a/lib/polyjuice/client/endpoint.ex +++ b/lib/polyjuice/client/endpoint.ex @@ -31,15 +31,13 @@ defmodule Polyjuice.Client.Endpoint do headers: [{String.t(), String.t()}], url: String.t(), body: String.t(), - transform: (integer, [{String.t(), String.t()}, ...], String.t() -> any), auth_required: true | false } - @enforce_keys [:method, :headers, :url, :transform] + @enforce_keys [:method, :headers, :url] defstruct [ :method, :headers, :url, - :transform, body: "", auth_required: true ] @@ -58,5 +56,16 @@ defmodule Polyjuice.Client.Endpoint do base_url :: String.t() ) :: Polyjuice.Client.Endpoint.HttpSpec.t() def http_spec(endpoint_args, base_url) + + @doc """ + Transform the HTTP result into a return value. + """ + @spec transform_http_result( + endpoint_args :: __MODULE__.t(), + status_code :: integer(), + headers :: [{String.t(), String.t()}, ...], + body :: String.t() + ) :: any + def transform_http_result(endpoint_args, status_code, headers, body) end end diff --git a/lib/polyjuice/client/endpoint/get_rooms_messages.ex b/lib/polyjuice/client/endpoint/get_rooms_messages.ex index 1983a2b..bceea3f 100644 --- a/lib/polyjuice/client/endpoint/get_rooms_messages.ex +++ b/lib/polyjuice/client/endpoint/get_rooms_messages.ex @@ -74,19 +74,18 @@ defmodule Polyjuice.Client.Endpoint.GetRoomsMessages do headers: [ {"Accept", "application/json"} ], - url: to_string(url), - transform: &Polyjuice.Client.Endpoint.GetRoomsMessages.transform/3 + url: to_string(url) } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, Poison.decode!(body)} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, Poison.decode!(body)} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/get_sync.ex b/lib/polyjuice/client/endpoint/get_sync.ex index 79e9553..c30e680 100644 --- a/lib/polyjuice/client/endpoint/get_sync.ex +++ b/lib/polyjuice/client/endpoint/get_sync.ex @@ -80,19 +80,18 @@ defmodule Polyjuice.Client.Endpoint.GetSync do headers: [ {"Accept", "application/json"} ], - url: to_string(url), - transform: &Polyjuice.Client.Endpoint.GetSync.transform/3 + url: to_string(url) } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, Poison.decode!(body)} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, Poison.decode!(body)} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/post_join.ex b/lib/polyjuice/client/endpoint/post_join.ex index 4e967e8..bdc8c0a 100644 --- a/lib/polyjuice/client/endpoint/post_join.ex +++ b/lib/polyjuice/client/endpoint/post_join.ex @@ -72,19 +72,18 @@ defmodule Polyjuice.Client.Endpoint.PostJoin do {"Content-Type", "application/json"} ], url: to_string(url), - body: body, - transform: &Polyjuice.Client.Endpoint.PostJoin.transform/3 + body: body } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, body |> Poison.decode!() |> Map.get("room_id")} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, body |> Poison.decode!() |> Map.get("room_id")} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/post_login.ex b/lib/polyjuice/client/endpoint/post_login.ex index bb068e2..e374b41 100644 --- a/lib/polyjuice/client/endpoint/post_login.ex +++ b/lib/polyjuice/client/endpoint/post_login.ex @@ -80,20 +80,18 @@ defmodule Polyjuice.Client.Endpoint.PostLogin do ) |> to_string(), body: body, - transform: &Polyjuice.Client.Endpoint.PostLogin.transform/3, auth_required: false } end - end - @doc false - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, Poison.decode!(body)} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, Poison.decode!(body)} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/post_logout.ex b/lib/polyjuice/client/endpoint/post_logout.ex index 7523797..e29c28b 100644 --- a/lib/polyjuice/client/endpoint/post_logout.ex +++ b/lib/polyjuice/client/endpoint/post_logout.ex @@ -41,20 +41,18 @@ defmodule Polyjuice.Client.Endpoint.PostLogout do "#{Polyjuice.Client.prefix_r0()}/logout" ) |> to_string(), - body: "{}", - transform: &Polyjuice.Client.Endpoint.PostLogout.transform/3 + body: "{}" } end - end - @doc false - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - :ok + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/post_rooms_receipt.ex b/lib/polyjuice/client/endpoint/post_rooms_receipt.ex index 49344d0..45cff8e 100644 --- a/lib/polyjuice/client/endpoint/post_rooms_receipt.ex +++ b/lib/polyjuice/client/endpoint/post_rooms_receipt.ex @@ -59,20 +59,18 @@ defmodule Polyjuice.Client.Endpoint.PostRoomsReceipt do }" ) |> to_string(), - body: "{}", - transform: &Polyjuice.Client.Endpoint.PostRoomsReceipt.transform/3 + body: "{}" } end - end - @doc false - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/post_user_filter.ex b/lib/polyjuice/client/endpoint/post_user_filter.ex index ee426a9..39b0f4a 100644 --- a/lib/polyjuice/client/endpoint/post_user_filter.ex +++ b/lib/polyjuice/client/endpoint/post_user_filter.ex @@ -53,19 +53,18 @@ defmodule Polyjuice.Client.Endpoint.PostUserFilter do "#{Polyjuice.Client.prefix_r0()}/user/#{e.(user_id)}/filter" ) |> to_string(), - body: body, - transform: &Polyjuice.Client.Endpoint.PostUserFilter.transform/3 + body: body } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, body |> Poison.decode!() |> Map.get("filter_id")} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, body |> Poison.decode!() |> Map.get("filter_id")} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/put_rooms_send.ex b/lib/polyjuice/client/endpoint/put_rooms_send.ex index 4bfc411..717624d 100644 --- a/lib/polyjuice/client/endpoint/put_rooms_send.ex +++ b/lib/polyjuice/client/endpoint/put_rooms_send.ex @@ -61,19 +61,18 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsSend do }" ) |> to_string(), - body: body, - transform: &Polyjuice.Client.Endpoint.PutRoomsSend.transform/3 + body: body } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, body |> Poison.decode!() |> Map.get("event_id")} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, body |> Poison.decode!() |> Map.get("event_id")} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/lib/polyjuice/client/endpoint/put_rooms_state.ex b/lib/polyjuice/client/endpoint/put_rooms_state.ex index c5f6fa1..98481ed 100644 --- a/lib/polyjuice/client/endpoint/put_rooms_state.ex +++ b/lib/polyjuice/client/endpoint/put_rooms_state.ex @@ -61,19 +61,18 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsState do }" ) |> to_string(), - body: body, - transform: &Polyjuice.Client.Endpoint.PutRoomsSend.transform/3 + body: body } end - end - def transform(status_code, _resp_headers, body) do - case status_code do - 200 -> - {:ok, body |> Poison.decode!() |> Map.get("event_id")} + def transform_http_result(_req, status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, body |> Poison.decode!() |> Map.get("event_id")} - _ -> - {:error, status_code, body} + _ -> + {:error, status_code, body} + end end end end diff --git a/test/polyjuice/client/endpoint/post_join_test.exs b/test/polyjuice/client/endpoint/post_join_test.exs index ae69d8c..969c74f 100644 --- a/test/polyjuice/client/endpoint/post_join_test.exs +++ b/test/polyjuice/client/endpoint/post_join_test.exs @@ -31,7 +31,7 @@ defmodule Polyjuice.Client.Endpoint.PostJoinTest do } do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil, body: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert %{http_spec | body: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: nil, headers: [ @@ -39,7 +39,6 @@ defmodule Polyjuice.Client.Endpoint.PostJoinTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/join/%21room" } @@ -56,9 +55,19 @@ defmodule Polyjuice.Client.Endpoint.PostJoinTest do } } - assert http_spec.transform.(200, [], ~s({"room_id":"!room"})) == {:ok, "!room"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + ~s({"room_id":"!room"}) + ) == {:ok, "!room"} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end with endpoint = %Polyjuice.Client.Endpoint.PostJoin{ @@ -67,7 +76,7 @@ defmodule Polyjuice.Client.Endpoint.PostJoinTest do } do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: "{}", headers: [ @@ -75,12 +84,16 @@ defmodule Polyjuice.Client.Endpoint.PostJoinTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/join/%21room?server_name=example.com&server_name=example.org" } - assert http_spec.transform.(200, [], ~s({"room_id":"!room"})) == {:ok, "!room"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + ~s({"room_id":"!room"}) + ) == {:ok, "!room"} end end end diff --git a/test/polyjuice/client/endpoint/post_login_test.exs b/test/polyjuice/client/endpoint/post_login_test.exs index 8d816a1..4e1e6c3 100644 --- a/test/polyjuice/client/endpoint/post_login_test.exs +++ b/test/polyjuice/client/endpoint/post_login_test.exs @@ -27,7 +27,7 @@ defmodule Polyjuice.Client.Endpoint.PostLoginTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil, body: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert %{http_spec | body: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: false, body: nil, headers: [ @@ -35,7 +35,6 @@ defmodule Polyjuice.Client.Endpoint.PostLoginTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/login" } @@ -48,7 +47,8 @@ defmodule Polyjuice.Client.Endpoint.PostLoginTest do "password" => "12345" } - assert http_spec.transform.( + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, 200, [], ~s({"user_id":"@alice:example.com","access_token":"1234567890","device_id":"ABCDEF","well_known":{"m.homeserver":{"base_url":"https://example.com"},"m.identity_server":{"base_url":"https://example.com"}}}) @@ -69,6 +69,11 @@ defmodule Polyjuice.Client.Endpoint.PostLoginTest do } } - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client/endpoint/post_logout_test.exs b/test/polyjuice/client/endpoint/post_logout_test.exs index a7a10f5..185e415 100644 --- a/test/polyjuice/client/endpoint/post_logout_test.exs +++ b/test/polyjuice/client/endpoint/post_logout_test.exs @@ -20,7 +20,7 @@ defmodule Polyjuice.Client.Endpoint.PostLogoutTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: "{}", headers: [ @@ -28,16 +28,21 @@ defmodule Polyjuice.Client.Endpoint.PostLogoutTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/logout" } - assert http_spec.transform.( + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, 200, [], "{}" - ) == :ok + ) == {:ok} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client/endpoint/post_rooms_receipt_test.exs b/test/polyjuice/client/endpoint/post_rooms_receipt_test.exs index 7f68471..d97bc2c 100644 --- a/test/polyjuice/client/endpoint/post_rooms_receipt_test.exs +++ b/test/polyjuice/client/endpoint/post_rooms_receipt_test.exs @@ -23,7 +23,7 @@ defmodule Polyjuice.Client.Endpoint.PostRoomsReceiptTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: "{}", headers: [ @@ -31,13 +31,22 @@ defmodule Polyjuice.Client.Endpoint.PostRoomsReceiptTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/rooms/%21room_id/receipt/m.read/%24event_id" } - assert http_spec.transform.(200, [], "{}") == {:ok} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + "{}" + ) == {:ok} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client/endpoint/post_user_filter_test.exs b/test/polyjuice/client/endpoint/post_user_filter_test.exs index ed19407..efff197 100644 --- a/test/polyjuice/client/endpoint/post_user_filter_test.exs +++ b/test/polyjuice/client/endpoint/post_user_filter_test.exs @@ -27,7 +27,7 @@ defmodule Polyjuice.Client.Endpoint.PostUserfilterTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: ~s({"presence":{"types":[]}}), headers: [ @@ -35,12 +35,21 @@ defmodule Polyjuice.Client.Endpoint.PostUserfilterTest do {"Content-Type", "application/json"} ], method: :post, - transform: nil, url: "https://example.com/_matrix/client/r0/user/%40alice%3Aexample.com/filter" } - assert http_spec.transform.(200, [], ~s({"filter_id":"abc"})) == {:ok, "abc"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + ~s({"filter_id":"abc"}) + ) == {:ok, "abc"} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client/endpoint/put_rooms_send_test.exs b/test/polyjuice/client/endpoint/put_rooms_send_test.exs index af7c4b1..904934c 100644 --- a/test/polyjuice/client/endpoint/put_rooms_send_test.exs +++ b/test/polyjuice/client/endpoint/put_rooms_send_test.exs @@ -27,7 +27,7 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsSendTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: ~s({"body":"Hello World!"}), headers: [ @@ -35,13 +35,22 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsSendTest do {"Content-Type", "application/json"} ], method: :put, - transform: nil, url: "https://example.com/_matrix/client/r0/rooms/%21room_id/send/m.room.message/txn_id" } - assert http_spec.transform.(200, [], "{\"event_id\": \"$foo1\"}") == {:ok, "$foo1"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + "{\"event_id\": \"$foo1\"}" + ) == {:ok, "$foo1"} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client/endpoint/put_rooms_state_test.exs b/test/polyjuice/client/endpoint/put_rooms_state_test.exs index 3986f79..4be69d3 100644 --- a/test/polyjuice/client/endpoint/put_rooms_state_test.exs +++ b/test/polyjuice/client/endpoint/put_rooms_state_test.exs @@ -27,7 +27,7 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsStateTest do http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") - assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + assert http_spec == %Polyjuice.Client.Endpoint.HttpSpec{ auth_required: true, body: ~s({"name":"foo"}), headers: [ @@ -35,12 +35,21 @@ defmodule Polyjuice.Client.Endpoint.PutRoomsStateTest do {"Content-Type", "application/json"} ], method: :put, - transform: nil, url: "https://example.com/_matrix/client/r0/rooms/%21room_id/state/m.room.name/" } - assert http_spec.transform.(200, [], "{\"event_id\": \"$foo1\"}") == {:ok, "$foo1"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 200, + [], + "{\"event_id\": \"$foo1\"}" + ) == {:ok, "$foo1"} - assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + assert Polyjuice.Client.Endpoint.Proto.transform_http_result( + endpoint, + 500, + [], + "Aaah!" + ) == {:error, 500, "Aaah!"} end end diff --git a/test/polyjuice/client_test.exs b/test/polyjuice/client_test.exs index d8274af..ca512d2 100644 --- a/test/polyjuice/client_test.exs +++ b/test/polyjuice/client_test.exs @@ -96,10 +96,44 @@ defmodule Polyjuice.ClientTest do with client = %DummyClient{ response: { %Polyjuice.Client.Endpoint.PostLogout{}, - :ok + {:ok} } } do - :ok = Polyjuice.Client.log_out(client) + {:ok} = Polyjuice.Client.log_out(client) + end + end + + test "sync" do + with client = %DummyClient{ + response: { + %Polyjuice.Client.Endpoint.GetSync{}, + {:ok, %{}} + } + } do + {:ok, %{}} = Polyjuice.Client.sync(client) + end + + with client = %DummyClient{ + response: { + %Polyjuice.Client.Endpoint.GetSync{ + filter: %{}, + since: "token", + full_state: true, + set_presence: :offline, + timeout: 120 + }, + {:ok, %{}} + } + } do + {:ok, %{}} = + Polyjuice.Client.sync( + client, + filter: %{}, + since: "token", + full_state: true, + set_presence: :offline, + timeout: 120 + ) end end end |