Skip to content

Commit b5dcf4a

Browse files
committed
avoid raising on bad dsn
1 parent f0c6a5a commit b5dcf4a

File tree

4 files changed

+79
-34
lines changed

4 files changed

+79
-34
lines changed

lib/mix/tasks/sentry.send_test_event.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule Mix.Tasks.Sentry.SendTestEvent do
1414

1515
Application.ensure_all_started(:sentry)
1616

17-
Sentry.Client.get_dsn!
17+
Sentry.Client.get_dsn
1818
|> print_environment_info()
1919

2020

lib/sentry/client.ex

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ defmodule Sentry.Client do
3838

3939
require Logger
4040

41-
@type get_dsn :: {String.t, String.t, Integer.t}
41+
@type get_dsn :: {String.t, String.t, Integer.t} | :error
4242
@sentry_version 5
4343
@max_attempts 4
4444
@hackney_pool_name :sentry_pool
@@ -83,30 +83,39 @@ defmodule Sentry.Client do
8383
end
8484

8585
defp do_send_event(event, body, :async) do
86-
{endpoint, public_key, secret_key} = get_dsn!()
87-
auth_headers = authorization_headers(public_key, secret_key)
88-
{:ok, Task.Supervisor.async_nolink(Sentry.TaskSupervisor, fn ->
89-
try_request(:post, endpoint, auth_headers, body)
90-
|> maybe_call_after_send_event(event)
91-
end)}
86+
case get_headers_and_endpoint() do
87+
{endpoint, auth_headers} ->
88+
{:ok, Task.Supervisor.async_nolink(Sentry.TaskSupervisor, fn ->
89+
try_request(:post, endpoint, auth_headers, body)
90+
|> maybe_call_after_send_event(event)
91+
end)}
92+
_ ->
93+
:error
94+
end
9295
end
9396

9497
defp do_send_event(event, body, :sync) do
95-
{endpoint, public_key, secret_key} = get_dsn!()
96-
auth_headers = authorization_headers(public_key, secret_key)
97-
try_request(:post, endpoint, auth_headers, body)
98-
|> maybe_call_after_send_event(event)
98+
case get_headers_and_endpoint() do
99+
{endpoint, auth_headers} ->
100+
try_request(:post, endpoint, auth_headers, body)
101+
|> maybe_call_after_send_event(event)
102+
_ ->
103+
:error
104+
end
99105
end
100106

101107
defp do_send_event(event, body, :none) do
102-
{endpoint, public_key, secret_key} = get_dsn!()
103-
auth_headers = authorization_headers(public_key, secret_key)
104-
Task.Supervisor.start_child(Sentry.TaskSupervisor, fn ->
105-
try_request(:post, endpoint, auth_headers, body)
106-
|> maybe_call_after_send_event(event)
107-
end)
108-
109-
{:ok, ""}
108+
case get_headers_and_endpoint() do
109+
{endpoint, auth_headers} ->
110+
Task.Supervisor.start_child(Sentry.TaskSupervisor, fn ->
111+
try_request(:post, endpoint, auth_headers, body)
112+
|> maybe_call_after_send_event(event)
113+
end)
114+
115+
{:ok, ""}
116+
_ ->
117+
:error
118+
end
110119
end
111120

112121
defp try_request(method, url, headers, body, current_attempt \\ 1)
@@ -173,17 +182,23 @@ defmodule Sentry.Client do
173182

174183
@doc """
175184
Get a Sentry DSN which is simply a URI.
185+
186+
{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
176187
"""
177-
@spec get_dsn! :: get_dsn
178-
def get_dsn! do
179-
# {PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
180-
%URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol} = URI.parse(Config.dsn())
181-
[public_key, secret_key] = String.split(userinfo, ":", parts: 2)
182-
[_, binary_project_id] = String.split(path, "/")
183-
project_id = String.to_integer(binary_project_id)
184-
endpoint = "#{protocol}://#{host}:#{port}/api/#{project_id}/store/"
185-
186-
{endpoint, public_key, secret_key}
188+
@spec get_dsn :: get_dsn
189+
def get_dsn do
190+
dsn = Config.dsn()
191+
with %URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol} when is_binary(path) <- URI.parse(dsn),
192+
[public_key, secret_key] <- String.split(userinfo, ":", parts: 2),
193+
[_, binary_project_id] <- String.split(path, "/"),
194+
{project_id, ""} <- Integer.parse(binary_project_id),
195+
endpoint <- "#{protocol}://#{host}:#{port}/api/#{project_id}/store/"
196+
do
197+
{endpoint, public_key, secret_key}
198+
else _ ->
199+
log_api_error("Cannot send event because of invalid DSN")
200+
:error
201+
end
187202
end
188203

189204
def maybe_call_after_send_event(result, event) do
@@ -254,6 +269,15 @@ defmodule Sentry.Client do
254269
end
255270
end
256271

272+
defp get_headers_and_endpoint do
273+
case get_dsn() do
274+
{endpoint, public_key, secret_key} ->
275+
{endpoint, authorization_headers(public_key, secret_key)}
276+
_ ->
277+
:error
278+
end
279+
end
280+
257281
defp log_api_error(body) do
258282
Logger.warn(fn ->
259283
["Failed to send Sentry event.", ?\n, body]

test/client_test.exs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,41 @@ defmodule Sentry.ClientTest do
88

99
test "authorization" do
1010
modify_env(:sentry, dsn: "https://public:[email protected]/1")
11-
{_endpoint, public_key, private_key} = Client.get_dsn!
11+
{_endpoint, public_key, private_key} = Client.get_dsn
1212
assert Client.authorization_header(public_key, private_key) =~ ~r/^Sentry sentry_version=5, sentry_client=sentry-elixir\/#{Application.spec(:sentry, :vsn)}, sentry_timestamp=\d{10}, sentry_key=public, sentry_secret=secret$/
1313
end
1414

1515
test "get dsn with default config" do
1616
modify_env(:sentry, dsn: "https://public:[email protected]/1")
17-
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} = Sentry.Client.get_dsn!
17+
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} = Sentry.Client.get_dsn
1818
end
1919

2020
test "get dsn with system config" do
2121
modify_env(:sentry, [dsn: {:system, "SYSTEM_KEY"}])
2222
modify_system_env(%{"SYSTEM_KEY" => "https://public:[email protected]/1"})
2323

24-
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} = Sentry.Client.get_dsn!
24+
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} = Sentry.Client.get_dsn
25+
end
26+
27+
test "errors on bad public/secret keys" do
28+
modify_env(:sentry, dsn: "https://[email protected]/1")
29+
capture_log(fn ->
30+
assert :error = Sentry.Client.get_dsn
31+
end)
32+
end
33+
34+
test "errors on non-integer project_id" do
35+
modify_env(:sentry, dsn: "https://public:[email protected]/Mitchell")
36+
capture_log(fn ->
37+
assert :error = Sentry.Client.get_dsn
38+
end)
39+
end
40+
41+
test "errors on no project_id" do
42+
modify_env(:sentry, dsn: "https://public:[email protected]")
43+
capture_log(fn ->
44+
assert :error = Sentry.Client.get_dsn
45+
end)
2546
end
2647

2748
test "logs api errors" do

test/support/test_client.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule Sentry.TestClient do
33
require Logger
44

55
def send_event(%Sentry.Event{} = event, _opts \\ []) do
6-
{endpoint, _public_key, _secret_key} = Sentry.Client.get_dsn!
6+
{endpoint, _public_key, _secret_key} = Sentry.Client.get_dsn
77
event = Sentry.Client.maybe_call_before_send_event(event)
88
Sentry.Client.render_event(event)
99
|> Poison.encode()

0 commit comments

Comments
 (0)