Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions lib/posthog/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ defmodule PostHog.Config do
],
api_key: [
type: :string,
required: true,
default: "",
doc: """
Your PostHog Project API key. Find it in your project's settings under the Project ID section.
If omitted or empty after trimming whitespace, PostHog starts in disabled/no-op mode.
"""
],
api_client_module: [
Expand Down Expand Up @@ -184,15 +185,24 @@ defmodule PostHog.Config do

with {:ok, validated} <-
NimbleOptions.validate(normalized_options, @compiled_configuration_schema) do
api_key_blank? = blank_api_key?(validated)
log_blank_api_key(validated)

config = Map.new(validated)
client = config.api_client_module.client(config.api_key, config.api_host)

client =
if api_key_blank? do
nil
else
config.api_client_module.client(config.api_key, config.api_host)
end

global_properties = Map.merge(config.global_properties, @system_global_properties)

final_config =
config
|> Map.put(:api_client, client)
|> Map.put(:enabled, not api_key_blank?)
|> Map.put(
:in_app_modules,
config.in_app_otp_apps |> Enum.flat_map(&Application.spec(&1, :modules)) |> MapSet.new()
Expand All @@ -219,11 +229,21 @@ defmodule PostHog.Config do
normalized_options
end
end)
|> then(fn normalized_options ->
if disabled_options?(normalized_options) and
not Keyword.has_key?(normalized_options, :api_host) do
Keyword.put(normalized_options, :api_host, @default_api_host)
else
normalized_options
end
end)
end

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

defp disabled_options?(options), do: Keyword.get(options, :api_key, "") == ""

defp normalize_api_host(api_host) when is_binary(api_host) do
api_host
|> String.trim()
Expand All @@ -235,10 +255,12 @@ defmodule PostHog.Config do

defp normalize_api_host(api_host), do: api_host

defp blank_api_key?(validated), do: validated[:api_key] == ""

defp log_blank_api_key(validated) do
if validated[:api_key] == "" do
if blank_api_key?(validated) do
Logger.error(
"posthog api_key is empty after trimming whitespace; check your project API key"
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
)
end
end
Expand Down
21 changes: 17 additions & 4 deletions lib/posthog/sender.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,28 @@ defmodule PostHog.Sender do
supervisor_name
|> PostHog.Registry.config()
|> case do
%{enabled: false} ->
:ok

%{test_mode: true} ->
PostHog.Test.remember_event(supervisor_name, event)

_ ->
senders =
supervisor_name
|> PostHog.Registry.registry_name()
|> Registry.select([{{{__MODULE__, :_}, :"$1", :"$2"}, [], [{{:"$2", :"$1"}}]}])
send_to_sender(event, supervisor_name)
end
end

defp send_to_sender(event, supervisor_name) do
senders =
supervisor_name
|> PostHog.Registry.registry_name()
|> Registry.select([{{{__MODULE__, :_}, :"$1", :"$2"}, [], [{{:"$2", :"$1"}}]}])

case senders do
[] ->
:ok

senders ->
# Pick the first available sender, otherwise random busy one.
senders
|> Keyword.get_lazy(:available, fn ->
Expand Down
4 changes: 4 additions & 0 deletions lib/posthog/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ defmodule PostHog.Supervisor do
Supervisor.init(children, strategy: :one_for_one)
end

defp sources(%{enabled: false}), do: []

defp sources(config) do
if config.enable_source_code_context do
opts = [
Expand All @@ -53,6 +55,8 @@ defmodule PostHog.Supervisor do
end
end

defp senders(%{enabled: false}), do: []

defp senders(config) do
pool_size = Map.get(config, :sender_pool_size, max(System.schedulers_online(), 2))

Expand Down
41 changes: 41 additions & 0 deletions test/posthog/application_config_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule PostHog.ApplicationConfigTest do
use ExUnit.Case, async: false

import ExUnit.CaptureLog
import Mox

setup :verify_on_exit!

setup do
previous_env = Application.get_all_env(:posthog)

on_exit(fn ->
:posthog
|> Application.get_all_env()
|> Keyword.keys()
|> Enum.each(&Application.delete_env(:posthog, &1))

Enum.each(previous_env, fn {key, value} ->
Application.put_env(:posthog, key, value)
end)
end)
end

test "read! does not raise when enabled and api_key is missing" do
Application.put_env(:posthog, :enable, true)
Application.put_env(:posthog, :api_client_module, PostHog.API.Mock)
Application.delete_env(:posthog, :api_key)
Application.delete_env(:posthog, :api_host)

log =
capture_log(fn ->
assert {%{enable: true}, %{api_key: "", enabled: false, api_client: nil} = config} =
PostHog.Config.read!()

assert config.api_host == "https://us.i.posthog.com"
end)

assert log =~
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
end
end
45 changes: 38 additions & 7 deletions test/posthog/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,42 @@ defmodule PostHog.ConfigTest do
assert config.api_host == "https://us.i.posthog.com"
end

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

%PostHog.API.Client{client: :stub_client, module: PostHog.API.Mock}
end)
assert config.api_key == ""
assert config.api_host == "https://us.i.posthog.com"
assert config.enabled == false
assert config.api_client == nil
end)

assert log =~
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
end

test "validate disables PostHog when api_key is empty" do
log =
capture_log(fn ->
assert {:ok, config} =
PostHog.Config.validate(
api_key: "",
api_host: "https://us.i.posthog.com",
api_client_module: PostHog.API.Mock
)

assert config.api_key == ""
assert config.enabled == false
assert config.api_client == nil
end)

assert log =~
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
end

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

assert config.api_key == ""
assert config.enabled == false
assert config.api_client == nil
end)

assert log =~ "posthog api_key is empty after trimming whitespace; check your project API key"
assert log =~
"posthog api_key is empty after trimming whitespace; PostHog will start in disabled/no-op mode"
end
end
27 changes: 27 additions & 0 deletions test/posthog/supervisor_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule PostHog.SupervisorTest do
use ExUnit.Case, async: true

import ExUnit.CaptureLog

@supervisor_name __MODULE__

test "disabled config starts without senders and captures as no-op" do
{config, _log} =
with_log(fn ->
PostHog.Config.validate!(
api_key: " \n\t ",
api_host: "https://us.i.posthog.com",
supervisor_name: @supervisor_name
)
end)

start_link_supervised!({PostHog.Supervisor, config})

assert [] =
@supervisor_name
|> PostHog.Registry.registry_name()
|> Registry.select([{{{PostHog.Sender, :_}, :"$1", :"$2"}, [], [:"$1"]}])

assert :ok = PostHog.bare_capture(@supervisor_name, "disabled capture", "distinct_id", %{})
end
end
Loading