Skip to content

Commit 3e45050

Browse files
committed
avoid raising on bad dsn
1 parent d4f5821 commit 3e45050

File tree

4 files changed

+86
-37
lines changed

4 files changed

+86
-37
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
maybe_send_event()

lib/sentry/client.ex

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ defmodule Sentry.Client do
3838

3939
require Logger
4040

41-
@type dsn :: {String.t(), String.t(), String.t()}
4241
@type send_event_result :: {:ok, Task.t() | String.t() | pid()} | :error | :unsampled
42+
@type dsn :: {String.t(), String.t(), String.t()} | :error
4343
@sentry_version 5
4444
@max_attempts 4
4545
@hackney_pool_name :sentry_pool
@@ -85,34 +85,43 @@ defmodule Sentry.Client do
8585
end
8686

8787
defp do_send_event(event, body, :async) do
88-
{endpoint, public_key, secret_key} = get_dsn!()
89-
auth_headers = authorization_headers(public_key, secret_key)
90-
91-
{:ok,
92-
Task.Supervisor.async_nolink(Sentry.TaskSupervisor, fn ->
93-
try_request(:post, endpoint, auth_headers, body)
94-
|> maybe_call_after_send_event(event)
95-
end)}
88+
case get_headers_and_endpoint() do
89+
{endpoint, auth_headers} ->
90+
{:ok,
91+
Task.Supervisor.async_nolink(Sentry.TaskSupervisor, fn ->
92+
try_request(:post, endpoint, auth_headers, body)
93+
|> maybe_call_after_send_event(event)
94+
end)}
95+
96+
_ ->
97+
:error
98+
end
9699
end
97100

98101
defp do_send_event(event, body, :sync) do
99-
{endpoint, public_key, secret_key} = get_dsn!()
100-
auth_headers = authorization_headers(public_key, secret_key)
102+
case get_headers_and_endpoint() do
103+
{endpoint, auth_headers} ->
104+
try_request(:post, endpoint, auth_headers, body)
105+
|> maybe_call_after_send_event(event)
101106

102-
try_request(:post, endpoint, auth_headers, body)
103-
|> maybe_call_after_send_event(event)
107+
_ ->
108+
:error
109+
end
104110
end
105111

106112
defp do_send_event(event, body, :none) do
107-
{endpoint, public_key, secret_key} = get_dsn!()
108-
auth_headers = authorization_headers(public_key, secret_key)
113+
case get_headers_and_endpoint() do
114+
{endpoint, auth_headers} ->
115+
Task.Supervisor.start_child(Sentry.TaskSupervisor, fn ->
116+
try_request(:post, endpoint, auth_headers, body)
117+
|> maybe_call_after_send_event(event)
118+
end)
109119

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)
120+
{:ok, ""}
114121

115-
{:ok, ""}
122+
_ ->
123+
:error
124+
end
116125
end
117126

118127
defp try_request(method, url, headers, body, current_attempt \\ 1)
@@ -193,19 +202,25 @@ defmodule Sentry.Client do
193202

194203
@doc """
195204
Get a Sentry DSN which is simply a URI.
205+
206+
{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
196207
"""
197-
@spec get_dsn! :: dsn
198-
def get_dsn! do
199-
# {PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
200-
%URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol} =
201-
URI.parse(Config.dsn())
202-
203-
[public_key, secret_key] = String.split(userinfo, ":", parts: 2)
204-
[_, binary_project_id] = String.split(path, "/")
205-
project_id = String.to_integer(binary_project_id)
206-
endpoint = "#{protocol}://#{host}:#{port}/api/#{project_id}/store/"
207-
208-
{endpoint, public_key, secret_key}
208+
@spec get_dsn :: dsn
209+
def get_dsn do
210+
dsn = Config.dsn()
211+
212+
with %URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol}
213+
when is_binary(path) <- URI.parse(dsn),
214+
[public_key, secret_key] <- String.split(userinfo, ":", parts: 2),
215+
[_, binary_project_id] <- String.split(path, "/"),
216+
{project_id, ""} <- Integer.parse(binary_project_id),
217+
endpoint <- "#{protocol}://#{host}:#{port}/api/#{project_id}/store/" do
218+
{endpoint, public_key, secret_key}
219+
else
220+
_ ->
221+
log_api_error("Cannot send event because of invalid DSN")
222+
:error
223+
end
209224
end
210225

211226
@spec maybe_call_after_send_event(send_event_result, Event.t()) :: Event.t()
@@ -289,6 +304,16 @@ defmodule Sentry.Client do
289304
end
290305
end
291306

307+
defp get_headers_and_endpoint do
308+
case get_dsn() do
309+
{endpoint, public_key, secret_key} ->
310+
{endpoint, authorization_headers(public_key, secret_key)}
311+
312+
_ ->
313+
:error
314+
end
315+
end
316+
292317
defp log_api_error(body) do
293318
Logger.warn(fn ->
294319
["Failed to send Sentry event.", ?\n, body]

test/client_test.exs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ 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

1313
assert Client.authorization_header(public_key, private_key) =~
1414
~r/^Sentry sentry_version=5, sentry_client=sentry-elixir\/#{
@@ -20,15 +20,39 @@ defmodule Sentry.ClientTest do
2020
modify_env(:sentry, dsn: "https://public:[email protected]/1")
2121

2222
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} =
23-
Sentry.Client.get_dsn!()
23+
Sentry.Client.get_dsn()
2424
end
2525

2626
test "get dsn with system config" do
2727
modify_env(:sentry, dsn: {:system, "SYSTEM_KEY"})
2828
modify_system_env(%{"SYSTEM_KEY" => "https://public:[email protected]/1"})
2929

3030
assert {"https://app.getsentry.com:443/api/1/store/", "public", "secret"} =
31-
Sentry.Client.get_dsn!()
31+
Sentry.Client.get_dsn()
32+
end
33+
34+
test "errors on bad public/secret keys" do
35+
modify_env(:sentry, dsn: "https://[email protected]/1")
36+
37+
capture_log(fn ->
38+
assert :error = Sentry.Client.get_dsn()
39+
end)
40+
end
41+
42+
test "errors on non-integer project_id" do
43+
modify_env(:sentry, dsn: "https://public:[email protected]/Mitchell")
44+
45+
capture_log(fn ->
46+
assert :error = Sentry.Client.get_dsn()
47+
end)
48+
end
49+
50+
test "errors on no project_id" do
51+
modify_env(:sentry, dsn: "https://public:[email protected]")
52+
53+
capture_log(fn ->
54+
assert :error = Sentry.Client.get_dsn()
55+
end)
3256
end
3357

3458
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

99
Sentry.Client.render_event(event)

0 commit comments

Comments
 (0)