Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/warm-rails-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'posthog-ruby': patch
'posthog-rails': patch
---

Attribute events and HTTP requests from the Rails integration to `posthog-rails` with the Rails integration version.
17 changes: 15 additions & 2 deletions lib/posthog/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ def initialize(opts = {})
@api_key = opts[:api_key]
@disabled = @api_key.nil? || @api_key.empty?
@max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
@lib = opts[:_lib] || 'posthog-ruby'
@lib_version = (opts[:_lib_version] || PostHog::VERSION).to_s
@headers = Defaults::Request::HEADERS.merge('User-Agent' => "#{@lib}/#{@lib_version}")
@worker_mutex = Mutex.new
@shutdown_mutex = Mutex.new
@shutdown_condition = ConditionVariable.new
Expand All @@ -154,11 +157,12 @@ def initialize(opts = {})
elsif @sync_mode
nil
else
SendWorker.new(@queue, @api_key, opts)
SendWorker.new(@queue, @api_key, opts.merge(headers: @headers))
end
if @sync_mode
@transport = Transport.new(
api_host: opts[:host],
headers: @headers,
skip_ssl_verification: opts[:skip_ssl_verification],
retries: opts.key?(:max_retries) ? opts[:max_retries].to_i + 1 : 3,
compress_request: opts[:compress_request]
Expand Down Expand Up @@ -198,7 +202,8 @@ def initialize(opts = {})
opts[:on_error],
flag_definition_cache_provider: opts[:flag_definition_cache_provider],
feature_flag_request_max_retries: opts[:feature_flag_request_max_retries],
async_load: opts[:feature_flags_async_load] == true
async_load: opts[:feature_flags_async_load] == true,
user_agent: @headers['User-Agent']
)
end

Expand Down Expand Up @@ -362,6 +367,8 @@ def capture(attrs)
end

attrs[:is_server] = @is_server
attrs[:lib] = @lib
attrs[:lib_version] = @lib_version
message = FieldParser.parse_for_capture(attrs)
# Minimal events are built from the allowlist after full assembly so
# context properties and parser-added metadata can never leak in.
Expand Down Expand Up @@ -417,6 +424,8 @@ def identify(attrs)

symbolize_keys! attrs
attrs[:is_server] = @is_server
attrs[:lib] = @lib
attrs[:lib_version] = @lib_version
enqueue(FieldParser.parse_for_identify(attrs))
end

Expand All @@ -435,6 +444,8 @@ def group_identify(attrs)

symbolize_keys! attrs
attrs[:is_server] = @is_server
attrs[:lib] = @lib
attrs[:lib_version] = @lib_version
enqueue(FieldParser.parse_for_group_identify(attrs))
end

Expand All @@ -450,6 +461,8 @@ def alias(attrs)

symbolize_keys! attrs
attrs[:is_server] = @is_server
attrs[:lib] = @lib
attrs[:lib_version] = @lib_version
enqueue(FieldParser.parse_for_alias(attrs))
end

Expand Down
7 changes: 5 additions & 2 deletions lib/posthog/feature_flags.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class FeatureFlagsPoller
# @param async_load [Boolean] When true, flag definitions are fetched only on the poller thread: an
# immediate first tick at construction, then the regular polling cadence, which keeps retrying until a
# load succeeds.
# @param user_agent [String] User-Agent header sent with feature flag requests.
def initialize(
polling_interval,
secret_key,
Expand All @@ -53,7 +54,8 @@ def initialize(
on_error = nil,
flag_definition_cache_provider: nil,
feature_flag_request_max_retries: nil,
async_load: false
async_load: false,
user_agent: "posthog-ruby/#{PostHog::VERSION}"
)
@polling_interval = polling_interval || Defaults::FeatureFlags::POLLING_INTERVAL_SECONDS
@secret_key = secret_key
Expand All @@ -72,6 +74,7 @@ def initialize(
@flags_etag = Concurrent::AtomicReference.new(nil)
@flag_definitions_loaded_at = Concurrent::AtomicReference.new(nil)
@async_load = async_load
@user_agent = user_agent
# Server-controlled gate for minimal `$feature_flag_called` events, read
# from the top-level `minimal_flag_called_events` key of the local
# evaluation definitions payload. false when the server does not send it.
Expand Down Expand Up @@ -1326,7 +1329,7 @@ def _request_remote_config_payload(flag_key)
RETRYABLE_FLAGS_REQUEST_STATUS_CODES = [502, 504].freeze

def _request(uri, request_object, timeout = nil, include_etag: false, retry_status_codes: [])
request_object['User-Agent'] = "posthog-ruby/#{PostHog::VERSION}"
request_object['User-Agent'] = @user_agent
request_timeout = timeout || 10
backoff_policy = nil
attempts = 0
Expand Down
4 changes: 2 additions & 2 deletions lib/posthog/field_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ def parse_common_fields(fields)
is_server = fields.fetch(:is_server, true) != false

properties = {
'$lib' => 'posthog-ruby',
'$lib_version' => PostHog::VERSION.to_s
'$lib' => fields.fetch(:lib, 'posthog-ruby'),
'$lib_version' => fields.fetch(:lib_version, PostHog::VERSION.to_s)
}
properties['$is_server'] = true if is_server

Expand Down
1 change: 1 addition & 0 deletions lib/posthog/send_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def initialize(queue, api_key, options = {})
@pid = Process.pid
@transport_options = {
api_host: options[:host],
headers: options[:headers],
skip_ssl_verification: options[:skip_ssl_verification],
compress_request: options[:compress_request]
}
Expand Down
6 changes: 5 additions & 1 deletion posthog-rails/lib/posthog/rails/facade.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'posthog/rails/version'

module PostHog
module Rails
# Install the Rails singleton-style PostHog facade at load time so Rails app
Expand Down Expand Up @@ -35,7 +37,9 @@ def init(options = {})
# after replacement so repeated init calls do not leave background
# resources from the previous instance running.
previous_client = @client
@client = PostHog::Client.new(options)
@client = PostHog::Client.new(
options.merge(_lib: 'posthog-rails', _lib_version: PostHog::Rails::VERSION)
)
begin
previous_client&.shutdown
rescue StandardError => e
Expand Down
14 changes: 10 additions & 4 deletions spec/posthog/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,22 @@ def shutdown
end

it 'handles skip_ssl_verification' do
expect(PostHog::Transport).to receive(:new).with({ api_host: 'https://us.i.posthog.com',
expect(PostHog::Transport).to receive(:new).with({
api_host: 'https://us.i.posthog.com',
headers: PostHog::Defaults::Request::HEADERS,
skip_ssl_verification: true,
compress_request: nil })
compress_request: nil
})
expect { Client.new api_key: API_KEY, skip_ssl_verification: true }.to_not raise_error
end

it 'passes compress_request false to the transport' do
expect(PostHog::Transport).to receive(:new).with({ api_host: 'https://us.i.posthog.com',
expect(PostHog::Transport).to receive(:new).with({
api_host: 'https://us.i.posthog.com',
headers: PostHog::Defaults::Request::HEADERS,
skip_ssl_verification: nil,
compress_request: false })
compress_request: false
})
expect { Client.new api_key: API_KEY, compress_request: false }.to_not raise_error
end

Expand Down
31 changes: 31 additions & 0 deletions spec/posthog/rails/railtie_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,37 @@
expect(PostHog::Rails::Logs::Setup).to have_received(:remember_client_options)
.with(hash_including(api_key: 'phc_test', host: 'https://eu.i.posthog.com'))
end

it 'attributes every event type to the Rails integration' do
PostHog.init(api_key: 'phc_test', test_mode: true)

PostHog.capture(event: 'event', distinct_id: 'user')
PostHog.identify(distinct_id: 'user')
PostHog.alias(alias: 'anonymous', distinct_id: 'user')
PostHog.group_identify(group_type: 'organization', group_key: '5')

properties = 4.times.map { PostHog.client.dequeue_last_message[:properties] }
expect(properties).to all(
include(
'$lib' => 'posthog-rails',
'$lib_version' => PostHog::Rails::VERSION
)
)
end

it 'uses the Rails user agent for event and feature flag requests' do
batch_request = stub_request(:post, 'https://us.i.posthog.com/batch/').to_return(status: 200, body: '{}')
flags_request = stub_request(:post, 'https://us.i.posthog.com/flags/?v=2')
.to_return(status: 200, body: { flags: {} }.to_json)

PostHog.init(api_key: 'phc_test', sync_mode: true)
PostHog.capture(event: 'event', distinct_id: 'user')
PostHog.get_all_flags('user')

expected_user_agent = "posthog-rails/#{PostHog::Rails::VERSION}"
expect(batch_request.with(headers: { 'User-Agent' => expected_user_agent })).to have_been_requested
expect(flags_request.with(headers: { 'User-Agent' => expected_user_agent })).to have_been_requested
end
end

describe '.install_posthog_logs' do
Expand Down