Skip to content

Conversation

@zhukaihan
Copy link
Collaborator

Summary

Adds tracking options for assignment and exposure events for fetch calls.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: no

@promptless
Copy link

promptless bot commented Oct 28, 2025

📝 Documentation updates detected!

New suggestion: Document FetchOptions for Ruby Experiment SDK
Updated existing suggestions:

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for fetch options to control tracking behavior in the Amplitude Experiment SDK. The changes allow clients to specify whether to track assignment and exposure events via HTTP headers when fetching variants.

  • Introduces FetchOptions class with tracks_assignment and tracks_exposure properties
  • Updates client methods (fetch_v2, fetch_async_v2) to accept optional FetchOptions parameter
  • Adds logic to set tracking headers based on fetch options

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
lib/experiment/remote/fetch_options.rb New class to encapsulate tracking options for fetch requests
lib/experiment/remote/client.rb Updates fetch methods to accept and process fetch options, sets HTTP headers based on options
lib/amplitude-experiment.rb Adds require statement for the new FetchOptions class
spec/experiment/remote/client_spec.rb Adds test coverage for fetch options behavior and fixes async test synchronization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

client.fetch_async_v2(test_user, fetch_options) do |user, block_variants|
expect(user).to equal(test_user)
expect(block_variants.key?(variant_name)).to be_truthy
expect(block_variants.fetch(variant_name)).to eq(Variant.new(key: 'on', payload: 'payload'))
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected variant is inconsistent with the mocked response. The response response_with_key is defined as '{\"sdk-ci-test\":{\"key\":\"on\",\"payload\":\"payload\"}}', and fetch_v2 methods return variants with both key and value populated from the response. However, the expected variant at line 223 only sets key and payload, while the test at line 192 correctly expects Variant.new(key: 'on', payload: 'payload', value: 'on'). Line 223 should include value: 'on' to match the actual behavior.

Suggested change
expect(block_variants.fetch(variant_name)).to eq(Variant.new(key: 'on', payload: 'payload'))
expect(block_variants.fetch(variant_name)).to eq(Variant.new(key: 'on', payload: 'payload', value: 'on'))

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +146
unless fetch_options.nil?
unless fetch_options.tracks_assignment.nil?
headers['X-Amp-Exp-Track'] = fetch_options.tracks_assignment ? 'track' : 'no-track'
end
unless fetch_options.tracks_exposure.nil?
headers['X-Amp-Exp-Exposure-Track'] = fetch_options.tracks_exposure ? 'track' : 'no-track'
end
end
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The nested unless statements create unnecessary complexity. Consider simplifying by using early returns or guard clauses. For example: headers['X-Amp-Exp-Track'] = fetch_options.tracks_assignment ? 'track' : 'no-track' unless fetch_options&.tracks_assignment.nil? and headers['X-Amp-Exp-Exposure-Track'] = fetch_options.tracks_exposure ? 'track' : 'no-track' unless fetch_options&.tracks_exposure.nil?

Suggested change
unless fetch_options.nil?
unless fetch_options.tracks_assignment.nil?
headers['X-Amp-Exp-Track'] = fetch_options.tracks_assignment ? 'track' : 'no-track'
end
unless fetch_options.tracks_exposure.nil?
headers['X-Amp-Exp-Exposure-Track'] = fetch_options.tracks_exposure ? 'track' : 'no-track'
end
end
headers['X-Amp-Exp-Track'] = fetch_options.tracks_assignment ? 'track' : 'no-track' unless fetch_options&.tracks_assignment.nil?
headers['X-Amp-Exp-Exposure-Track'] = fetch_options.tracks_exposure ? 'track' : 'no-track' unless fetch_options&.tracks_exposure.nil?

Copilot uses AI. Check for mistakes.
expect(block_variants.fetch(variant_name)).to eq(expected_variant)
callback_called = true
end
sleep 1 until callback_called
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The busy-wait loop sleep 1 until callback_called can delay tests unnecessarily. Consider using a smaller sleep interval (e.g., sleep 0.01) or adding a timeout to prevent infinite loops if the callback is never called.

Suggested change
sleep 1 until callback_called
start_time = Time.now
timeout = 2 # seconds
until callback_called
sleep 0.01
if Time.now - start_time > timeout
raise "Timeout waiting for async callback"
end
end

Copilot uses AI. Check for mistakes.
expect(a_request(:post, server_url).with(headers: { 'X-Amp-Exp-Track' => 'track', 'X-Amp-Exp-Exposure-Track' => 'track' })).to have_been_made.once
callback_called = true
end
sleep 1 until callback_called
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The busy-wait loop sleep 1 until callback_called can delay tests unnecessarily. Consider using a smaller sleep interval (e.g., sleep 0.01) or adding a timeout to prevent infinite loops if the callback is never called.

Suggested change
sleep 1 until callback_called
timeout = 5 # seconds
start_time = Time.now
until callback_called
sleep 0.01
if Time.now - start_time > timeout
raise "Timeout waiting for callback to be called"
end
end

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants