Current section
Files
Jump to
Current section
Files
lib/api/api.ex
defmodule UrbitEx.API do
alias UrbitEx.{Airlock, Actions}
def init(url, code) do
%UrbitEx.Session{
url: url,
code: code,
channel: set_channel()
}
end
@spec login(%{:code => any, :url => binary, optional(any) => any}) :: %{
:code => any,
:cookie => binary,
:ship => binary | {integer, integer},
:url => binary,
optional(any) => any
}
def login(session) do
endpoint = "/~/login"
url = session.url <> endpoint
body = "password=#{session.code}"
{:ok, res} = Airlock.post(url, body)
{_, cookiestring} = Enum.find(res.headers, fn x -> match?({"set-cookie", _}, x) end)
[cookie | _] = cookiestring |> String.split(";")
# ships don't take the '~'
[ship] = Regex.run(~r/(?<=~)[a-z]+[^=]*/, cookie)
session
|> Map.put(:cookie, cookie)
|> Map.put(:ship, ship)
end
defp set_channel do
"/~/channel/#{System.os_time(:millisecond)}-#{
:crypto.strong_rand_bytes(3) |> Base.encode16(case: :lower)
}"
end
def open_channel(session) do
body = Actions.poke(session.ship, "hood", "helm-hi", "Opening airlock")
wrap_put(session, body)
session
end
def start_sse(session) do
{:ok, _res} = Airlock.sse(session.url, session.channel, session.cookie, session.last_sse)
session
end
def logout(session) do
body = %{id: increment_id(session), action: "delete"}
wrap_put(session, body)
session
end
## subscriptions to be a Map %{app: foo, path: bar}
def subscribe(session, subscription) do
body = Actions.subscribe(session.ship, subscription.app, subscription.path)
wrap_put(session, body)
session
end
defp wrap_put(session, body) do
session = session |> increment_id
body = Map.put(body, :id, session.last_action)
body = if need_ack?(session), do: [Actions.ack(session.last_sse) | [body]], else: [body]
Airlock.put(session.url, session.channel, session.cookie, body)
end
defp increment_id(session) do
Map.put(session, :last_action, session.last_action + 1)
end
defp need_ack?(session) do
session.last_ack < session.last_sse
end
end