Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions lib/jellyfish/component/hls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ defmodule Jellyfish.Component.HLS do

@behaviour Jellyfish.Component.Deserializer

@type credentials :: %{
access_key_id: String.t(),
secret_access_key: String.t(),
region: String.t(),
bucket: String.t()
}
Comment thread
Rados13 marked this conversation as resolved.

@enforce_keys []
defstruct @enforce_keys ++
[
low_latency: false,
persistent: false,
target_window_duration: nil
target_window_duration: nil,
s3: nil
]

@type t :: %__MODULE__{
low_latency: boolean(),
persistent: boolean(),
target_window_duration: pos_integer() | nil
target_window_duration: pos_integer() | nil,
s3: credentials() | nil
}

@impl true
Expand Down
14 changes: 14 additions & 0 deletions lib/jellyfish/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,18 @@ defmodule Jellyfish.Exception do
%__MODULE__{message: msg}
end
end

defmodule OptionsError do
defexception [:message]

@impl true
def exception(_opts) do
msg = """
Passed component options that doesn't match function spec.
Look closely on `Jellyfish.Room.add_component/3` spec.
"""

%__MODULE__{message: msg}
end
end
end
27 changes: 25 additions & 2 deletions lib/jellyfish/room.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ defmodule Jellyfish.Room do
"""

alias Tesla.Env
alias Jellyfish.Component.{HLS, RTSP}
alias Jellyfish.{Client, Component, Peer, Utils}
alias Jellyfish.Exception.StructureError

@s3_keys [:access_key_id, :secret_access_key, :region, :bucket]

@enforce_keys [
:id,
:config,
Expand Down Expand Up @@ -197,15 +200,16 @@ defmodule Jellyfish.Room do
def add_component(client, room_id, component) do
component = if is_atom(component), do: struct!(component), else: component

with {:ok, %Env{status: 201, body: body}} <-
with :ok <- validate_component(component),
{:ok, %Env{status: 201, body: body}} <-
Tesla.post(
client.http_client,
"/room/#{room_id}/component",
%{
"type" => Component.string_from_options(component),
"options" =>
Map.from_struct(component)
|> Map.new(fn {k, v} -> {snake_case_to_camel_case(k), v} end)
|> map_snake_case_to_camel_case()
}
),
{:ok, data} <- Map.fetch(body, "data"),
Expand Down Expand Up @@ -253,6 +257,25 @@ defmodule Jellyfish.Room do
end
end

defp validate_component(%RTSP{}), do: :ok
defp validate_component(%HLS{s3: nil}), do: :ok
defp validate_component(%HLS{s3: %{} = s3}), do: validate_s3_credentials(s3)
defp validate_component(_component), do: {:error, :component_validation}

defp validate_s3_credentials(credentials) do
keys = Map.keys(credentials)

if @s3_keys -- keys == [] and keys -- @s3_keys == [],
do: :ok,
else: {:error, :component_validation}
end

defp map_snake_case_to_camel_case(%{} = map),
do:
Map.new(map, fn {k, v} -> {snake_case_to_camel_case(k), map_snake_case_to_camel_case(v)} end)

defp map_snake_case_to_camel_case(value), do: value

defp snake_case_to_camel_case(atom) do
[first | rest] = Atom.to_string(atom) |> String.split("_")
rest = rest |> Enum.map(&String.capitalize/1)
Expand Down
3 changes: 2 additions & 1 deletion lib/jellyfish/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Jellyfish.Utils do
@moduledoc false

alias Jellyfish.Client
alias Jellyfish.Exception.{ProtocolPrefixError, StructureError}
alias Jellyfish.Exception.{OptionsError, ProtocolPrefixError, StructureError}
alias Tesla.Env

@protocol_prefixes ["http://", "https://", "ws://", "wss://"]
Expand Down Expand Up @@ -36,5 +36,6 @@ defmodule Jellyfish.Utils do
do: {:error, "Request failed: #{error}"}

def handle_response_error({:ok, %Env{body: _body}}), do: raise(StructureError)
def handle_response_error({:error, :component_validation}), do: raise(OptionsError)
def handle_response_error({:error, reason}), do: {:error, reason}
end
32 changes: 30 additions & 2 deletions test/jellyfish/room_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Jellyfish.RoomTest do
use ExUnit.Case
doctest Jellyfish.Room
alias Jellyfish.Exception.OptionsError
alias Jellyfish.{Client, Component, Peer, Room}

@server_api_token "development"
Expand All @@ -14,6 +15,13 @@ defmodule Jellyfish.RoomTest do
target_window_duration: nil
}

@s3 %{
access_key_id: "access_key_id",
secret_access_key: "secret_access_key",
region: "region",
bucket: "bucket"
}

@rtsp_component_opts %Component.RTSP{
source_uri: "rtsp://ef36c6dff23ecc5bbe311cc880d95dc8.se:2137/does/not/matter"
}
Expand Down Expand Up @@ -156,12 +164,32 @@ defmodule Jellyfish.RoomTest do
assert %Component{type: Component.HLS, metadata: @hls_metadata} = component
end

test "when request is valid with s3 credentials", %{client: client, room_id: room_id} do
assert {:ok, component} =
Room.add_component(client, room_id, %{@hls_component_opts | s3: @s3})

assert %Component{type: Component.HLS, metadata: @hls_metadata} = component
end
Comment thread
Rados13 marked this conversation as resolved.

test "when request is invalid - wrong s3 credentials", %{client: client, room_id: room_id} do
assert_raise OptionsError, fn ->
Room.add_component(client, room_id, %{
@hls_component_opts
| s3: Map.delete(@s3, :bucket)
})
end

assert_raise OptionsError, fn ->
Room.add_component(client, room_id, %{@hls_component_opts | s3: []})
end
end

test "when request is invalid", %{client: client} do
assert_raise FunctionClauseError, fn ->
assert_raise OptionsError, fn ->
Room.add_component(client, @room_id, %InvalidComponentOpts{})
end

assert_raise FunctionClauseError, fn ->
assert_raise OptionsError, fn ->
Room.add_component(client, @room_id, InvalidComponentOpts)
end
end
Expand Down