Skip to content

Commit e5132ed

Browse files
committed
Disable SDK when API key is blank
1 parent 61d6231 commit e5132ed

6 files changed

Lines changed: 153 additions & 15 deletions

File tree

lib/posthog/config.ex

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ defmodule PostHog.Config do
1818
],
1919
api_key: [
2020
type: :string,
21-
required: true,
21+
default: "",
2222
doc: """
2323
Your PostHog Project API key. Find it in your project's settings under the Project ID section.
24+
If omitted or empty after trimming whitespace, PostHog starts in disabled/no-op mode.
2425
"""
2526
],
2627
api_client_module: [
@@ -184,15 +185,24 @@ defmodule PostHog.Config do
184185

185186
with {:ok, validated} <-
186187
NimbleOptions.validate(normalized_options, @compiled_configuration_schema) do
188+
api_key_blank? = blank_api_key?(validated)
187189
log_blank_api_key(validated)
188190

189191
config = Map.new(validated)
190-
client = config.api_client_module.client(config.api_key, config.api_host)
192+
193+
client =
194+
if api_key_blank? do
195+
nil
196+
else
197+
config.api_client_module.client(config.api_key, config.api_host)
198+
end
199+
191200
global_properties = Map.merge(config.global_properties, @system_global_properties)
192201

193202
final_config =
194203
config
195204
|> Map.put(:api_client, client)
205+
|> Map.put(:enabled, not api_key_blank?)
196206
|> Map.put(
197207
:in_app_modules,
198208
config.in_app_otp_apps |> Enum.flat_map(&Application.spec(&1, :modules)) |> MapSet.new()
@@ -219,11 +229,21 @@ defmodule PostHog.Config do
219229
normalized_options
220230
end
221231
end)
232+
|> then(fn normalized_options ->
233+
if disabled_options?(normalized_options) and
234+
not Keyword.has_key?(normalized_options, :api_host) do
235+
Keyword.put(normalized_options, :api_host, @default_api_host)
236+
else
237+
normalized_options
238+
end
239+
end)
222240
end
223241

224242
defp normalize_api_key(api_key) when is_binary(api_key), do: String.trim(api_key)
225243
defp normalize_api_key(api_key), do: api_key
226244

245+
defp disabled_options?(options), do: Keyword.get(options, :api_key, "") == ""
246+
227247
defp normalize_api_host(api_host) when is_binary(api_host) do
228248
api_host
229249
|> String.trim()
@@ -235,10 +255,12 @@ defmodule PostHog.Config do
235255

236256
defp normalize_api_host(api_host), do: api_host
237257

258+
defp blank_api_key?(validated), do: validated[:api_key] == ""
259+
238260
defp log_blank_api_key(validated) do
239-
if validated[:api_key] == "" do
261+
if blank_api_key?(validated) do
240262
Logger.error(
241-
"posthog api_key is empty after trimming whitespace; check your project API key"
263+
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
242264
)
243265
end
244266
end

lib/posthog/sender.ex

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,28 @@ defmodule PostHog.Sender do
3030
supervisor_name
3131
|> PostHog.Registry.config()
3232
|> case do
33+
%{enabled: false} ->
34+
:ok
35+
3336
%{test_mode: true} ->
3437
PostHog.Test.remember_event(supervisor_name, event)
3538

3639
_ ->
37-
senders =
38-
supervisor_name
39-
|> PostHog.Registry.registry_name()
40-
|> Registry.select([{{{__MODULE__, :_}, :"$1", :"$2"}, [], [{{:"$2", :"$1"}}]}])
40+
send_to_sender(event, supervisor_name)
41+
end
42+
end
43+
44+
defp send_to_sender(event, supervisor_name) do
45+
senders =
46+
supervisor_name
47+
|> PostHog.Registry.registry_name()
48+
|> Registry.select([{{{__MODULE__, :_}, :"$1", :"$2"}, [], [{{:"$2", :"$1"}}]}])
49+
50+
case senders do
51+
[] ->
52+
:ok
4153

54+
senders ->
4255
# Pick the first available sender, otherwise random busy one.
4356
senders
4457
|> Keyword.get_lazy(:available, fn ->

lib/posthog/supervisor.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ defmodule PostHog.Supervisor do
3737
Supervisor.init(children, strategy: :one_for_one)
3838
end
3939

40+
defp sources(%{enabled: false}), do: []
41+
4042
defp sources(config) do
4143
if config.enable_source_code_context do
4244
opts = [
@@ -53,6 +55,8 @@ defmodule PostHog.Supervisor do
5355
end
5456
end
5557

58+
defp senders(%{enabled: false}), do: []
59+
5660
defp senders(config) do
5761
pool_size = Map.get(config, :sender_pool_size, max(System.schedulers_online(), 2))
5862

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule PostHog.ApplicationConfigTest do
2+
use ExUnit.Case, async: false
3+
4+
import ExUnit.CaptureLog
5+
import Mox
6+
7+
setup :verify_on_exit!
8+
9+
setup do
10+
previous_env = Application.get_all_env(:posthog)
11+
12+
on_exit(fn ->
13+
:posthog
14+
|> Application.get_all_env()
15+
|> Keyword.keys()
16+
|> Enum.each(&Application.delete_env(:posthog, &1))
17+
18+
Enum.each(previous_env, fn {key, value} ->
19+
Application.put_env(:posthog, key, value)
20+
end)
21+
end)
22+
end
23+
24+
test "read! does not raise when enabled and api_key is missing" do
25+
Application.put_env(:posthog, :enable, true)
26+
Application.put_env(:posthog, :api_client_module, PostHog.API.Mock)
27+
Application.delete_env(:posthog, :api_key)
28+
Application.delete_env(:posthog, :api_host)
29+
30+
log =
31+
capture_log(fn ->
32+
assert {%{enable: true}, %{api_key: "", enabled: false, api_client: nil} = config} =
33+
PostHog.Config.read!()
34+
35+
assert config.api_host == "https://us.i.posthog.com"
36+
end)
37+
38+
assert log =~
39+
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
40+
end
41+
end

test/posthog/config_test.exs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,42 @@ defmodule PostHog.ConfigTest do
4343
assert config.api_host == "https://us.i.posthog.com"
4444
end
4545

46-
test "validate logs when api_key is blank after trimming whitespace" do
47-
expect(PostHog.API.Mock, :client, fn api_key, api_host ->
48-
assert api_key == ""
49-
assert api_host == "https://us.i.posthog.com"
46+
test "validate disables PostHog when api_key is missing" do
47+
log =
48+
capture_log(fn ->
49+
assert {:ok, config} =
50+
PostHog.Config.validate(api_client_module: PostHog.API.Mock)
5051

51-
%PostHog.API.Client{client: :stub_client, module: PostHog.API.Mock}
52-
end)
52+
assert config.api_key == ""
53+
assert config.api_host == "https://us.i.posthog.com"
54+
assert config.enabled == false
55+
assert config.api_client == nil
56+
end)
57+
58+
assert log =~
59+
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
60+
end
61+
62+
test "validate disables PostHog when api_key is empty" do
63+
log =
64+
capture_log(fn ->
65+
assert {:ok, config} =
66+
PostHog.Config.validate(
67+
api_key: "",
68+
api_host: "https://us.i.posthog.com",
69+
api_client_module: PostHog.API.Mock
70+
)
71+
72+
assert config.api_key == ""
73+
assert config.enabled == false
74+
assert config.api_client == nil
75+
end)
76+
77+
assert log =~
78+
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
79+
end
5380

81+
test "validate disables PostHog when api_key is blank after trimming whitespace" do
5482
log =
5583
capture_log(fn ->
5684
assert {:ok, config} =
@@ -61,8 +89,11 @@ defmodule PostHog.ConfigTest do
6189
)
6290

6391
assert config.api_key == ""
92+
assert config.enabled == false
93+
assert config.api_client == nil
6494
end)
6595

66-
assert log =~ "posthog api_key is empty after trimming whitespace; check your project API key"
96+
assert log =~
97+
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
6798
end
6899
end

test/posthog/supervisor_test.exs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule PostHog.SupervisorTest do
2+
use ExUnit.Case, async: true
3+
4+
import ExUnit.CaptureLog
5+
6+
@supervisor_name __MODULE__
7+
8+
test "disabled config starts without senders and captures as no-op" do
9+
{config, _log} =
10+
with_log(fn ->
11+
PostHog.Config.validate!(
12+
api_key: " \n\t ",
13+
api_host: "https://us.i.posthog.com",
14+
supervisor_name: @supervisor_name
15+
)
16+
end)
17+
18+
start_link_supervised!({PostHog.Supervisor, config})
19+
20+
assert [] =
21+
@supervisor_name
22+
|> PostHog.Registry.registry_name()
23+
|> Registry.select([{{{PostHog.Sender, :_}, :"$1", :"$2"}, [], [:"$1"]}])
24+
25+
assert :ok = PostHog.bare_capture(@supervisor_name, "disabled capture", "distinct_id", %{})
26+
end
27+
end

0 commit comments

Comments
 (0)