Packages
timber
2.5.3
3.1.2
3.1.1
3.1.0
3.0.0
3.0.0-alpha.3
3.0.0-alpha.2
3.0.0-alpha.1
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.1
2.3.0
2.2.1
2.2.0
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc7
2.0.0-rc6
2.0.0-rc5
2.0.0-rc4
2.0.0-rc3
2.0.0-rc2
2.0.0-rc1
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
🌲 Great Elixir Logging Made Easy. Official Timber.io Integration.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/timber/install/api.ex
defmodule Mix.Tasks.Timber.Install.API do
@moduledoc false
alias __MODULE__.{InvalidAPIKeyError, BadResponseError, CommunicationError,
MalformedAPIResponseError}
alias Mix.Tasks.Timber.Install.{Config, IOHelper, Messages}
@enforce_keys [:api_key, :session_id]
defstruct [:api_key, :session_id]
#
# Setup
#
def start do
Config.http_client().start()
end
def new(api_key) do
session_id =
32
|> :crypto.strong_rand_bytes()
|> Base.encode16(case: :lower)
|> binary_part(0, 32)
%__MODULE__{api_key: api_key, session_id: session_id}
end
#
# Endpoints
#
def application!(api) do
request!(api, :get, "/installer/application")
end
def event!(api, name, data \\ nil) do
request!(api, :post, "/installer/events", body: %{event: %{name: name, data: data}})
end
def has_logs!(api) do
request!(api, :get, "/installer/has_logs")
end
def has_logs?(api) do
case has_logs!(api) do
{204, _body} -> true
_ -> false
end
end
def wait_for_logs(api, iteration \\ 0)
def wait_for_logs(api, iteration) do
excessive_threshold = 30
cond do
iteration == 0 -> event!(api, :waiting_for_logs)
iteration == excessive_threshold -> event!(api, :excessive_log_waiting)
true -> :ok
end
IO.ANSI.format(["\r", :clear_line])
|> IOHelper.write()
Messages.waiting_for_logs()
|> Messages.action_starting()
|> IOHelper.write()
case has_logs!(api) do
{202, _body} ->
rem = rem(iteration, 3)
Messages.spinner(rem)
|> IOHelper.write()
if iteration > excessive_threshold do
" (Having trouble? We'd love to help: support@timber.io)"
|> IOHelper.write()
end
:timer.sleep(1000)
wait_for_logs(api, iteration + 1)
{204, _body} ->
Messages.success()
|> IOHelper.puts(:green)
:ok
end
end
#
# Utility
#
defp request!(%{api_key: api_key, session_id: session_id}, method, path, opts \\ []) do
headers =
[{'X-Installer-Session-Id', String.to_charlist(session_id)}]
|> add_authorization_header(api_key)
url = Config.api_url() <> path
case Config.http_client().request(method, headers, url, opts) do
{:ok, status, body} when status in 200..299 ->
{status, decode_body("#{body}", url)}
{:ok, status, _body} when status in [401, 403] -> raise(InvalidAPIKeyError)
{:ok, status, body} -> raise(BadResponseError, url: url, body: body, status: status)
{:error, reason} -> raise(CommunicationError, url: url, reason: reason)
end
end
defp add_authorization_header(headers, nil), do: headers
defp add_authorization_header(headers, api_key) do
encoded_api_key = Base.encode64(api_key)
[{'Authorization', 'Basic #{encoded_api_key}'} | headers]
end
defp decode_body("", _url), do: ""
defp decode_body(body, url) do
case Poison.decode(body) do
{:ok, %{"data" => data}} -> data
_other -> raise(MalformedAPIResponseError, url: url, body: body)
end
end
#
# Errors
#
defmodule BadResponseError do
defexception [:message]
def exception(opts) do
url = Keyword.fetch!(opts, :url)
body = Keyword.fetch!(opts, :body)
status = Keyword.fetch!(opts, :status)
message =
"""
Uh oh! We got a bad response (#{status}) from #{url}.
The response received was:
#{body}
"""
%__MODULE__{message: message}
end
end
defmodule CommunicationError do
defexception [:message]
def exception(opts) do
url = Keyword.fetch!(opts, :url)
error = Keyword.fetch!(opts, :error)
message =
"""
Uh oh! We encountered an error communicating with #{url}.
The error is:
#{error}
"""
%__MODULE__{message: message}
end
end
defmodule InvalidAPIKeyError do
defexception [:message]
def exception(_opts) do
message =
"""
Uh oh! The API key supplied is invalid. Please ensure
that you copied the key properly.
#{Messages.obtain_key_instructions()}
"""
%__MODULE__{message: message}
end
end
defmodule MalformedAPIResponseError do
defexception [:message]
def exception(opts) do
url = Keyword.fetch!(opts, :url)
body = Keyword.fetch!(opts, :body)
message =
"""
We received a malformed response from #{url}.
The response we received was:
#{inspect(body)}
Please try again.
"""
%__MODULE__{message: message}
end
end
end