Commit d8a2b7a3 authored by jejacks0n's avatar jejacks0n

Migrate configuration into ApplicationExperiment

- This makes the configuration easier to test, improves those tests, and
provides a base class that all future experiments can use.
parent 7c773d50
# frozen_string_literal: true
class ApplicationExperiment < Gitlab::Experiment
def publish(_result)
track(:assignment) # track that we've assigned a variant for this context
Gon.global.push({ experiment: { name => signature } }, true) # push to client
end
def track(action, **event_args)
return if excluded? # no events for opted out actors or excluded subjects
Gitlab::Tracking.event(name, action.to_s, **event_args.merge(
context: (event_args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0', signature
)
))
end
private
def resolve_variant_name
variant_names.first if Feature.enabled?(name, self, type: :experiment)
end
end
# frozen_string_literal: true
Gitlab::Experiment.configure do |config|
# Logic this project uses to resolve a variant for a given experiment.
#
# This can return an instance of any object that responds to `name`, or can
# return a variant name as a symbol or string.
#
# This block will be executed within the scope of the experiment instance,
# so can easily access experiment methods, like getting the name or context.
config.variant_resolver = lambda do |requested_variant|
# Return the variant if one was requested in code:
break requested_variant if requested_variant.present?
# Use Feature interface to determine the variant by passing the experiment,
# which responds to `flipper_id` and `session_id` to accommodate adapters.
variant_names.first if Feature.enabled?(name, self, type: :experiment)
end
# Tracking behavior can be implemented to link an event to an experiment.
#
# Similar to the variant_resolver, this is called within the scope of the
# experiment instance and so can access any methods on the experiment,
# such as name and signature.
config.tracking_behavior = lambda do |event, args|
Gitlab::Tracking.event(name, event.to_s, **args.merge(
context: (args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0', signature
)
))
end
# Called at the end of every experiment run, with the result.
#
# You may want to track that you've assigned a variant to a given context,
# or push the experiment into the client or publish results elsewhere, like
# into redis or postgres. Also called within the scope of the experiment
# instance.
config.publishing_behavior = lambda do |result|
# Track the event using our own configured tracking logic.
track(:assignment)
# Push the experiment knowledge into the front end. The signature contains
# the context key, and the variant that has been determined.
Gon.push({ experiment: { name => signature } }, true)
end
config.base_class = 'ApplicationExperiment'
end
......@@ -23,6 +23,7 @@ module Quality
dependencies
elastic
elastic_integration
experiments
factories
finders
frontend
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ApplicationExperiment do
subject { described_class.new(:stub) }
describe "publishing results" do
it "tracks the assignment" do
expect(subject).to receive(:track).with(:assignment)
subject.publish(nil)
end
it "pushes the experiment knowledge into the client using Gon.global" do
expect(Gon.global).to receive(:push).with(
{
experiment: {
'stub' => { # string key because it can be namespaced
experiment: 'stub',
key: 'e8f65fd8d973f9985dc7ea3cf1614ae1',
variant: 'control'
}
}
},
true
)
subject.publish(nil)
end
end
describe "tracking events", :snowplow do
before do
allow(Gitlab::Tracking).to receive(:event)
end
it "doesn't track if excluded" do
subject.exclude { true }
subject.track(:action)
expect_no_snowplow_event
end
it "tracks the event with the expected arguments and merged contexts" do
subject.track(:action, property: '_property_', context: [
SnowplowTracker::SelfDescribingJson.new('iglu:com.gitlab/fake/jsonschema/0-0-0', { data: '_data_' })
])
expect_snowplow_event(
category: 'stub',
action: 'action',
property: '_property_',
context: [
{
schema: 'iglu:com.gitlab/fake/jsonschema/0-0-0',
data: { data: '_data_' }
},
{
schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0',
data: { experiment: 'stub', key: 'e8f65fd8d973f9985dc7ea3cf1614ae1', variant: 'control' }
}
]
)
end
end
describe "variant resolution" do
it "returns nil when not rolled out" do
stub_feature_flags(stub: false)
expect(subject.variant.name).to eq('control')
end
context "when the rollout out to 100%" do
it "returns the first variant name" do
subject.try(:variant1) {}
subject.try(:variant2) {}
expect(subject.variant.name).to eq('variant1')
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment