diff --git a/README.md b/README.md index 67ed514..4ecf4a4 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Elixir server SDK for [Jellyfish](https://github.com/jellyfish-dev/jellyfish). Currently it allows for: - making API calls to Jellyfish server (QoL wrapper for HTTP requests) +- listening to Jellyfish server notifications via WebSocket ## Installation @@ -24,10 +25,15 @@ end ## Usage -Make API calls to Jellyfish (authentication required, for more information see [Jellyfish docs](https://jellyfish-dev.github.io/jellyfish-docs/getting_started/authentication)): +Make API calls to Jellyfish (authentication required, for more information see [Jellyfish docs](https://jellyfish-dev.github.io/jellyfish-docs/getting_started/authentication)) +and receive server notifications: ```elixir -client = Jellyfish.Client.new("http://address-of-your-server.com", "your-jellyfish-token") +# start process responsible for receiving notifications +{:ok, _pid} = Jellyfish.Notifier.start(server_address: "ws://address-of-your-server.com", server_api_key: "your-jellyfish-token") + +# create HTTP client instance +client = Jellyfish.Client.new(server_address: "http://address-of-your-server.com", server_api_key: "your-jellyfish-token") # Create room {:ok, %Jellyfish.Room{id: room_id}} = Jellyfish.Room.create(client, max_peers: 10) @@ -38,6 +44,10 @@ room_id # Add peer {:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Jellyfish.Room.add_peer(client, room_id, "webrtc") +receive do + {:jellyfish, {:peer_connected, ^room_id, ^peer_id}} -> # handle the notification +end + # Delete peer :ok = Jellyfish.Room.delete_peer(client, room_id, peer_id) ``` diff --git a/lib/jellyfish/client.ex b/lib/jellyfish/client.ex index cd0d5ae..6cc42d7 100644 --- a/lib/jellyfish/client.ex +++ b/lib/jellyfish/client.ex @@ -17,6 +17,8 @@ defmodule Jellyfish.Client do For the list of supported Tesla adapters refer to [Tesla docs](https://hexdocs.pm/tesla/readme.html#adapters). """ + alias Jellyfish.Utils + @enforce_keys [ :http_client ] @@ -26,45 +28,46 @@ defmodule Jellyfish.Client do http_client: Tesla.Client.t() } - @doc """ - Creates a new instance of `t:Jellyfish.Client.t/0`. + @typedoc """ + Options needed to open connection to Jellyfish server. - ## Parameters + * `:server_address` - url or IP address of the Jellyfish server instance. + * `:server_api_token` - token used for authorizing HTTP requests and WebSocket connection. + It's the same token as the one configured in Jellyfish. + * `:secure?` - if `true`, use HTTPS and WSS instead of HTTP and WS, `false` by default. - * `address` - url or IP address of the Jellyfish server instance - * `server_api_token` - token used for authorizing HTTP requests. It's the same - token as the one configured in Jellyfish. + When an option is not explicily passed, value set in `config.exs` is used: + ``` + # in config.exs + config :jellyfish_server_sdk, + server_address: "you-jellyfish-server-address.com", + server_api_token: "your-jellyfish-token", + secure?: true + ``` """ - @spec new(String.t(), String.t()) :: t() - def new(address, server_api_token), do: build_client(address, server_api_token) + @type connection_options :: [ + server_address: String.t(), + server_api_token: String.t(), + secure?: boolean() + ] @doc """ Creates a new instance of `t:Jellyfish.Client.t/0`. - Uses token set in `config.exs`. To explicitly pass the token, see `new/2`. - ``` - # in config.exs - config :jellyfish_server_sdk, server_api_token: "your-jellyfish-token" - - client = Jellyfish.Client.new("http://address-of-your-server.com") - ``` - - See `new/2` for description of parameters. + For information about options, see `t:connection_options/0`. """ - @spec new(String.t()) :: t() - def new(address) do - server_api_token = Application.fetch_env!(:jellyfish_server_sdk, :server_api_token) - build_client(address, server_api_token) - end + @spec new(connection_options()) :: t() + def new(opts \\ []) do + {address, api_token, secure?} = Utils.get_options_or_defaults(opts) + address = if secure?, do: "https://#{address}", else: "http://#{address}" + adapter = Application.get_env(:jellyfish_server_sdk, :tesla_adapter, Tesla.Adapter.Mint) - defp build_client(address, server_api_token) do middleware = [ {Tesla.Middleware.BaseUrl, address}, - {Tesla.Middleware.BearerAuth, token: server_api_token}, + {Tesla.Middleware.BearerAuth, token: api_token}, Tesla.Middleware.JSON ] - adapter = Application.get_env(:jellyfish_server_sdk, :tesla_adapter, Tesla.Adapter.Mint) http_client = Tesla.client(middleware, adapter) %__MODULE__{http_client: http_client} diff --git a/lib/jellyfish/component.ex b/lib/jellyfish/component.ex index ed1d1bf..dca75b9 100644 --- a/lib/jellyfish/component.ex +++ b/lib/jellyfish/component.ex @@ -6,7 +6,7 @@ defmodule Jellyfish.Component do For more information refer to [Jellyfish documentation](https://www.membrane.stream) """ - alias Jellyfish.Exception.ResponseStructureError + alias Jellyfish.Exception.StructureError @enforce_keys [ :id, @@ -54,7 +54,7 @@ defmodule Jellyfish.Component do } _other -> - raise ResponseStructureError + raise StructureError end end end diff --git a/lib/jellyfish/exception.ex b/lib/jellyfish/exception.ex index 1df6363..d9c6c54 100644 --- a/lib/jellyfish/exception.ex +++ b/lib/jellyfish/exception.ex @@ -1,17 +1,31 @@ defmodule Jellyfish.Exception do @moduledoc false - defmodule ResponseStructureError do + defmodule StructureError do defexception [:message] @impl true def exception(_opts) do msg = """ - Received server response with unexpected structure. + Received server response or notification with unexpected structure. Make sure you are using correct combination of Jellyfish and SDK versions. """ %__MODULE__{message: msg} end end + + defmodule ProtocolPrefixError do + defexception [:message] + + @impl true + def exception(_opts) do + msg = """ + Passed address starts with protocol prefix, like "http://" or "https://", which is undesired. + To use SSL, set `secure?: true` option in `config.exs` or pass this option to called function. + """ + + %__MODULE__{message: msg} + end + end end diff --git a/lib/jellyfish/notifier.ex b/lib/jellyfish/notifier.ex new file mode 100644 index 0000000..d1619d9 --- /dev/null +++ b/lib/jellyfish/notifier.ex @@ -0,0 +1,154 @@ +defmodule Jellyfish.Notifier do + @moduledoc """ + Module defining a process responsible for establishing + WebSocket connection and receiving notifications form Jellyfish server. + + ``` + iex> {:ok, pid} = Jellyfish.Notifier.start(server_address: "address-of-jellyfish-server.com", server_api_token: "your-jellyfish-token") + {:ok, #PID<0.301.0>} + + # here add a room and a peer using functions from `Jellyfish.Room` module + # you should receive a notification after the peer established connection + + iex> flush() + {:jellyfish, + {:peer_connected, "21604fbe-8ac8-44e6-8474-98b5f50f1863", + "ae07f94e-0887-44c3-81d5-bfa9eac96252"}} + :ok + ``` + """ + + use WebSockex + + alias Jellyfish.{Client, Utils} + alias Jellyfish.Exception.StructureError + + @auth_timeout 2000 + + @doc """ + Starts the Notifier process and connects to Jellyfish. + + Acts like `start/1` but links to the calling process. + + See `start/1` for more information. + """ + @spec start_link(Client.connection_options()) :: {:ok, pid()} | {:error, term()} + def start_link(opts \\ []) do + connect(:start_link, opts) + end + + @doc """ + Starts the Notifier process and connects to Jellyfish. + + Received notifications are send to the calling process in + a form of `{:jellyfish, msg}`, where `msg` is + `type` or `{type, room_id}` or `{type, room_id, (peer/component)_id}`. + Refer to [Jellyfish docs](https://jellyfish-dev.github.io/jellyfish-docs/) to learn more about server notifications. + + For information about options, see `t:Jellyfish.Client.connection_options/0`. + """ + @spec start(Client.connection_options()) :: {:ok, pid()} | {:error, term()} + def start(opts \\ []) do + connect(:start, opts) + end + + @impl true + def handle_frame({:text, msg}, state) do + with {:ok, decoded_msg} <- Jason.decode(msg), + %{"type" => "controlMessage", "data" => data} <- decoded_msg, + {:ok, notification} <- decode_notification(data) do + # notification will be either {type, room_id, peer_id/component_id}, + # {type, room_id} or just type + send(state.receiver_pid, {:jellyfish, notification}) + else + _other -> raise StructureError + end + + {:ok, state} + end + + @impl true + def handle_cast(_msg, state) do + # ignore incoming messages + {:ok, state} + end + + @impl true + def terminate({:remote, 1000, "invalid token"}, state) do + send(state.receiver_pid, {:jellyfish, :invalid_token}) + end + + @impl true + def terminate(_reason, state) do + send(state.receiver_pid, {:jellyfish, :disconnected}) + end + + defp connect(fun, opts) do + {address, api_token, secure?} = Utils.get_options_or_defaults(opts) + address = if secure?, do: "wss://#{address}", else: "ws://#{address}" + state = %{receiver_pid: self()} + + auth_msg = + %{type: "controlMessage", data: %{type: "authRequest", token: api_token}} + |> Jason.encode!() + + with {:ok, pid} <- + apply(WebSockex, fun, ["#{address}/socket/server/websocket", __MODULE__, state]), + :ok <- WebSockex.send_frame(pid, {:text, auth_msg}) do + receive do + {:jellyfish, :authenticated} -> + {:ok, pid} + + {:jellyfish, :invalid_token} -> + {:error, :invalid_token} + after + @auth_timeout -> + Process.exit(pid, :normal) + {:error, :authentication_timeout} + end + else + {:error, _reason} = error -> error + end + end + + defp decode_notification(%{"type" => type, "roomId" => room_id, "id" => id}) do + decoded_type = + case type do + "authenticated" -> :authenticated + "peerConnected" -> :peer_connected + "peerDisconnected" -> :peer_disconected + "componentCrashed" -> :component_crashed + _other -> nil + end + + if is_nil(decoded_type) do + {:error, :invalid_type} + else + {:ok, {decoded_type, room_id, id}} + end + end + + defp decode_notification(%{"type" => type, "room_id" => id}) do + decoded_type = + case type do + "roomCrashed" -> :room_crashed + _other -> nil + end + + if is_nil(decoded_type), + do: {:error, :invalid_type}, + else: {:ok, {decoded_type, id}} + end + + defp decode_notification(%{"type" => type}) do + decoded_type = + case type do + "authenticated" -> :authenticated + _other -> nil + end + + if is_nil(decoded_type), do: {:error, :invalid_type}, else: {:ok, decoded_type} + end + + defp decode_notification(_other), do: {:error, :invalid_type} +end diff --git a/lib/jellyfish/peer.ex b/lib/jellyfish/peer.ex index 6d22021..9ab9ded 100644 --- a/lib/jellyfish/peer.ex +++ b/lib/jellyfish/peer.ex @@ -7,7 +7,7 @@ defmodule Jellyfish.Peer do For more information refer to [Jellyfish documentation](https://www.membrane.stream) """ - alias Jellyfish.Exception.ResponseStructureError + alias Jellyfish.Exception.StructureError @enforce_keys [ :id, @@ -49,7 +49,7 @@ defmodule Jellyfish.Peer do } _other -> - raise ResponseStructureError + raise StructureError end end end diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index 437375a..0fb77cb 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -25,7 +25,7 @@ defmodule Jellyfish.Room do alias Tesla.Env alias Jellyfish.{Client, Component, Peer} - alias Jellyfish.Exception.ResponseStructureError + alias Jellyfish.Exception.StructureError @enforce_keys [ :id, @@ -72,7 +72,7 @@ defmodule Jellyfish.Room do result <- Enum.map(data, &from_json/1) do {:ok, result} else - :error -> raise ResponseStructureError + :error -> raise StructureError error -> handle_response_error(error) end end @@ -88,7 +88,7 @@ defmodule Jellyfish.Room do result <- from_json(data) do {:ok, result} else - :error -> raise ResponseStructureError + :error -> raise StructureError error -> handle_response_error(error) end end @@ -108,7 +108,7 @@ defmodule Jellyfish.Room do result <- from_json(data) do {:ok, result} else - :error -> raise ResponseStructureError + :error -> raise StructureError error -> handle_response_error(error) end end @@ -140,7 +140,7 @@ defmodule Jellyfish.Room do result <- Peer.from_json(peer) do {:ok, result, token} else - :error -> raise ResponseStructureError + :error -> raise StructureError error -> handle_response_error(error) end end @@ -178,7 +178,7 @@ defmodule Jellyfish.Room do result <- Component.from_json(data) do {:ok, result} else - :error -> raise ResponseStructureError + :error -> raise StructureError error -> handle_response_error(error) end end @@ -215,13 +215,13 @@ defmodule Jellyfish.Room do } _other -> - raise ResponseStructureError + raise StructureError end end defp handle_response_error({:ok, %Env{body: %{"errors" => error}}}), do: {:error, "Request failed: #{error}"} - defp handle_response_error({:ok, %Env{body: _body}}), do: raise(ResponseStructureError) + defp handle_response_error({:ok, %Env{body: _body}}), do: raise(StructureError) defp handle_response_error({:error, reason}), do: {:error, reason} end diff --git a/lib/jellyfish/utils.ex b/lib/jellyfish/utils.ex new file mode 100644 index 0000000..742fe40 --- /dev/null +++ b/lib/jellyfish/utils.ex @@ -0,0 +1,25 @@ +defmodule Jellyfish.Utils do + @moduledoc false + + alias Jellyfish.Client + alias Jellyfish.Exception.ProtocolPrefixError + + @protocol_prefixes ["http://", "https://", "ws://", "wss://"] + + @spec get_options_or_defaults(Client.connection_options()) :: + {String.t(), String.t(), boolean()} + def get_options_or_defaults(opts) do + server_address = + opts[:server_address] || Application.fetch_env!(:jellyfish_server_sdk, :server_address) + + server_api_token = + opts[:server_api_token] || Application.fetch_env!(:jellyfish_server_sdk, :server_api_token) + + secure? = + Keyword.get(opts, :secure?, Application.get_env(:jellyfish_server_sdk, :secure?, false)) + + if String.starts_with?(server_address, @protocol_prefixes), do: raise(ProtocolPrefixError) + + {server_address, server_api_token, secure?} + end +end diff --git a/mix.exs b/mix.exs index 1a538b1..7bb2d34 100644 --- a/mix.exs +++ b/mix.exs @@ -51,6 +51,7 @@ defmodule Membrane.Template.Mixfile do {:tesla, "~> 1.5"}, {:mint, "~> 1.0"}, {:jason, "~> 1.4"}, + {:websockex, "~> 0.4.3"}, # Docs, credo, test coverage, dialyzer {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index a8302ba..c46837c 100644 --- a/mix.lock +++ b/mix.lock @@ -24,4 +24,5 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "tesla": {:hex, :tesla, "1.5.1", "f2ba04f5e6ace0f1954f1fb4375f55809a5f2ff491c18ccb09fbc98370d4280b", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2815d4f6550973d1ed65692d545d079174f6a1f8cb4775f6eb606cbc0666a9de"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, } diff --git a/test/jellyfish/client_test.exs b/test/jellyfish/client_test.exs index a9f2f26..2d8dd92 100644 --- a/test/jellyfish/client_test.exs +++ b/test/jellyfish/client_test.exs @@ -3,23 +3,80 @@ defmodule Jellyfish.ClientTest do alias Jellyfish.Client - @url "https://somemockurl.com" + @server_address "valid-address.com" + @server_api_token "valid-token" - describe "sdk" do - test "creates client struct" do - server_api_token = "mock_token" - client = Client.new(@url, server_api_token) + describe "creates client struct" do + test "with connection options passed explictly" do + address_with_prefix = "http://#{@server_address}" + + client = + Client.new( + server_address: @server_address, + server_api_token: @server_api_token, + secure?: false + ) assert %Client{ http_client: %Tesla.Client{ adapter: {Tesla.Adapter.Mint, :call, [[]]}, pre: [ - {Tesla.Middleware.BaseUrl, :call, [@url]}, - {Tesla.Middleware.BearerAuth, :call, [[token: ^server_api_token]]}, + {Tesla.Middleware.BaseUrl, :call, [^address_with_prefix]}, + {Tesla.Middleware.BearerAuth, :call, [[token: @server_api_token]]}, {Tesla.Middleware.JSON, :call, [[]]} ] } } = client end + + test "with connection options from config" do + :ok = + Application.put_all_env([ + { + :jellyfish_server_sdk, + [ + {:server_address, @server_address}, + {:server_api_token, @server_api_token}, + {:secure?, true} + ] + } + ]) + + addres_with_prefix = "https://#{@server_address}" + client = Client.new() + + assert %Client{ + http_client: %Tesla.Client{ + adapter: {Tesla.Adapter.Mint, :call, [[]]}, + pre: [ + {Tesla.Middleware.BaseUrl, :call, [^addres_with_prefix]}, + {Tesla.Middleware.BearerAuth, :call, [[token: @server_api_token]]}, + {Tesla.Middleware.JSON, :call, [[]]} + ] + } + } = client + + Application.delete_env(:jellyfish_server_sdk, :server_address) + Application.delete_env(:jellyfish_server_sdk, :server_api_token) + Application.delete_env(:jellyfish_server_sdk, :secure?) + end + + test "when address contains protocol prefix" do + address_with_prefix = "http://#{@server_address}" + + assert_raise( + Jellyfish.Exception.ProtocolPrefixError, + fn -> + Client.new(server_address: address_with_prefix, server_api_token: @server_api_token) + end + ) + end + + test "when options are not passed and config is not set" do + assert_raise( + ArgumentError, + fn -> Client.new() end + ) + end end end diff --git a/test/jellyfish/room_test.exs b/test/jellyfish/room_test.exs index 8d2c54b..772a767 100644 --- a/test/jellyfish/room_test.exs +++ b/test/jellyfish/room_test.exs @@ -7,8 +7,8 @@ defmodule Jellyfish.RoomTest do @server_api_token "testtoken" - @url "http://mockurl.com" - @invalid_url "http://invalid-url.com" + @url "mockurl.com" + @invalid_url "invalid-url.com" @component_id "mock_component_id" @component_type "hls" @@ -43,7 +43,7 @@ defmodule Jellyfish.RoomTest do end end) - %{client: Client.new(@url, @server_api_token)} + %{client: Client.new(server_address: @url, server_api_token: @server_api_token)} end describe "auth" do @@ -52,7 +52,7 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :post, - url: "#{@url}/room", + url: "http://#{@url}/room", body: ^valid_body } = env -> case Tesla.get_header(env, "authorization") do @@ -71,7 +71,7 @@ defmodule Jellyfish.RoomTest do end test "invalid token" do - client = Client.new(@url, "invalid" <> @server_api_token) + client = Client.new(server_address: @url, server_api_token: "invalid" <> @server_api_token) assert {:error, _reason} = Room.create(client, max_peers: @max_peers) end end @@ -84,14 +84,14 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :post, - url: "#{@url}/room", + url: "http://#{@url}/room", body: ^valid_body } -> json(%{"data" => build_room_json(true)}, status: 201) %{ method: :post, - url: "#{@url}/room", + url: "http://#{@url}/room", body: ^invalid_body } -> json(%{"errors" => @error_message}, status: 400) @@ -114,13 +114,13 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :delete, - url: "#{@url}/room/#{@room_id}" + url: "http://#{@url}/room/#{@room_id}" } -> text("", status: 204) %{ method: :delete, - url: "#{@url}/room/#{@invalid_room_id}" + url: "http://#{@url}/room/#{@invalid_room_id}" } -> json(%{"errors" => @error_message}, status: 404) end) @@ -140,13 +140,13 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :get, - url: "#{@url}/room" + url: "http://#{@url}/room" } -> json(%{"data" => [build_room_json(false)]}, status: 200) %{ method: :get, - url: "#{@invalid_url}/room" + url: "http://#{@invalid_url}/room" } -> %Tesla.Env{status: 404, body: nil} end) @@ -159,7 +159,7 @@ defmodule Jellyfish.RoomTest do test "when request is invalid" do middleware = [ - {Tesla.Middleware.BaseUrl, @invalid_url}, + {Tesla.Middleware.BaseUrl, "http://#{@invalid_url}"}, Tesla.Middleware.JSON ] @@ -167,7 +167,7 @@ defmodule Jellyfish.RoomTest do http_client = Tesla.client(middleware, adapter) invalid_client = %Client{http_client: http_client} - assert_raise Jellyfish.Exception.ResponseStructureError, fn -> + assert_raise Jellyfish.Exception.StructureError, fn -> Room.get_all(invalid_client) end end @@ -178,13 +178,13 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :get, - url: "#{@url}/room/#{@room_id}" + url: "http://#{@url}/room/#{@room_id}" } -> json(%{"data" => build_room_json(false)}, status: 200) %{ method: :get, - url: "#{@url}/room/#{@invalid_room_id}" + url: "http://#{@url}/room/#{@invalid_room_id}" } -> json(%{"errors" => @error_message}, status: 404) end) @@ -208,14 +208,14 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :post, - url: "#{@url}/room/#{@room_id}/component", + url: "http://#{@url}/room/#{@room_id}/component", body: ^valid_body } -> json(%{"data" => build_component_json()}, status: 201) %{ method: :post, - url: "#{@url}/room/#{@room_id}/component", + url: "http://#{@url}/room/#{@room_id}/component", body: ^invalid_body } -> json(%{"errors" => @error_message}, status: 400) @@ -238,13 +238,13 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :delete, - url: "#{@url}/room/#{@room_id}/component/#{@component_id}" + url: "http://#{@url}/room/#{@room_id}/component/#{@component_id}" } -> text("", status: 204) %{ method: :delete, - url: "#{@url}/room/#{@room_id}/component/#{@invalid_component_id}" + url: "http://#{@url}/room/#{@room_id}/component/#{@invalid_component_id}" } -> json(%{"errors" => @error_message}, status: 404) end) @@ -268,14 +268,14 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :post, - url: "#{@url}/room/#{@room_id}/peer", + url: "http://#{@url}/room/#{@room_id}/peer", body: ^valid_body } -> json(%{"data" => build_peer_json()}, status: 201) %{ method: :post, - url: "#{@url}/room/#{@room_id}/peer", + url: "http://#{@url}/room/#{@room_id}/peer", body: ^invalid_body } -> json(%{"errors" => @error_message}, status: 400) @@ -298,13 +298,13 @@ defmodule Jellyfish.RoomTest do mock(fn %{ method: :delete, - url: "#{@url}/room/#{@room_id}/peer/#{@peer_id}" + url: "http://#{@url}/room/#{@room_id}/peer/#{@peer_id}" } -> text("", status: 204) %{ method: :delete, - url: "#{@url}/room/#{@room_id}/peer/#{@invalid_peer_id}" + url: "http://#{@url}/room/#{@room_id}/peer/#{@invalid_peer_id}" } -> json(%{"errors" => @error_message}, status: 404) end)