class Optimizely::Project
Attributes
@api no-doc
@api no-doc
@api no-doc
@api no-doc
@api no-doc
@api no-doc
@api no-doc
Public Class Methods
Constructor for Projects.
@param datafile - JSON string representing the project. @param event_dispatcher
- Provides a dispatch_event method which if given a URL and params sends a request to it. @param logger - Optional component which provides a log method to log messages. By default nothing would be logged. @param error_handler
- Optional component which provides a handle_error method to handle exceptions.
By default all exceptions will be suppressed.
@param user_profile_service - Optional component which provides methods to store and retreive user profiles. @param skip_json_validation - Optional boolean param to skip JSON schema validation of the provided datafile. @params sdk_key - Optional string uniquely identifying the datafile corresponding to project and environment combination.
Must provide at least one of datafile or sdk_key.
@param config_manager
- Optional Responds to 'config' method. @param notification_center
- Optional Instance of NotificationCenter
. @param event_processor
- Optional Responds to process.
# File lib/optimizely.rb, line 66 def initialize( datafile = nil, event_dispatcher = nil, logger = nil, error_handler = nil, skip_json_validation = false, user_profile_service = nil, sdk_key = nil, config_manager = nil, notification_center = nil, event_processor = nil, default_decide_options = [] ) @logger = logger || NoOpLogger.new @error_handler = error_handler || NoOpErrorHandler.new @event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger, error_handler: @error_handler) @user_profile_service = user_profile_service @default_decide_options = [] if default_decide_options.is_a? Array @default_decide_options = default_decide_options.clone else @logger.log(Logger::DEBUG, 'Provided default decide options is not an array.') @default_decide_options = [] end begin validate_instantiation_options rescue InvalidInputError => e @logger = SimpleLogger.new @logger.log(Logger::ERROR, e.message) end @notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(@logger, @error_handler) @config_manager = if config_manager.respond_to?(:config) config_manager elsif sdk_key HTTPProjectConfigManager.new( sdk_key: sdk_key, datafile: datafile, logger: @logger, error_handler: @error_handler, skip_json_validation: skip_json_validation, notification_center: @notification_center ) else StaticProjectConfigManager.new(datafile, @logger, @error_handler, skip_json_validation) end @decision_service = DecisionService.new(@logger, @user_profile_service) @event_processor = if event_processor.respond_to?(:process) event_processor else ForwardingEventProcessor.new(@event_dispatcher, @logger, @notification_center) end end
Public Instance Methods
Buckets visitor and sends impression event to Optimizely
.
@param experiment_key - Experiment which needs to be activated. @param user_id - String ID for user. @param attributes - Hash representing user attributes and values to be recorded.
@return [Variation Key] representing the variation the user will be bucketed in. @return [nil] if experiment is not Running, if user is not in experiment, or if datafile is invalid.
# File lib/optimizely.rb, line 303 def activate(experiment_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('activate').message) return nil end return nil unless Optimizely::Helpers::Validator.inputs_valid?( { experiment_key: experiment_key, user_id: user_id }, @logger, Logger::ERROR ) config = project_config variation_key = get_variation_with_config(experiment_key, user_id, attributes, config) if variation_key.nil? @logger.log(Logger::INFO, "Not activating user '#{user_id}'.") return nil end # Create and dispatch impression event experiment = config.get_experiment_from_key(experiment_key) send_impression( config, experiment, variation_key, '', experiment_key, true, Optimizely::DecisionService::DECISION_SOURCES['EXPERIMENT'], user_id, attributes ) variation_key end
# File lib/optimizely.rb, line 806 def close return if @stopped @stopped = true @config_manager.stop! if @config_manager.respond_to?(:stop!) @event_processor.stop! if @event_processor.respond_to?(:stop!) end
Create a context of the user for which decision APIs will be called.
A user context will be created successfully even when the SDK is not fully configured yet.
@param user_id - The user ID to be used for bucketing. @param attributes - A Hash representing user attribute names and values.
@return [OptimizelyUserContext] An OptimizelyUserContext
associated with this OptimizelyClient. @return [nil] If user attributes are not in valid format.
# File lib/optimizely.rb, line 135 def create_user_context(user_id, attributes = nil) # We do not check for is_valid here as a user context can be created successfully # even when the SDK is not fully configured. # validate user_id return nil unless Optimizely::Helpers::Validator.inputs_valid?( { user_id: user_id }, @logger, Logger::ERROR ) # validate attributes return nil unless user_inputs_valid?(attributes) user_context = OptimizelyUserContext.new(self, user_id, attributes) user_context end
# File lib/optimizely.rb, line 153 def decide(user_context, key, decide_options = []) # raising on user context as it is internal and not provided directly by the user. raise if user_context.class != OptimizelyUserContext reasons = [] # check if SDK is ready unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide').message) reasons.push(OptimizelyDecisionMessage::SDK_NOT_READY) return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons) end # validate that key is a string unless key.is_a?(String) @logger.log(Logger::ERROR, 'Provided key is invalid') reasons.push(format(OptimizelyDecisionMessage::FLAG_KEY_INVALID, key)) return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons) end # validate that key maps to a feature flag config = project_config feature_flag = config.get_feature_flag_from_key(key) unless feature_flag @logger.log(Logger::ERROR, "No feature flag was found for key '#{key}'.") reasons.push(format(OptimizelyDecisionMessage::FLAG_KEY_INVALID, key)) return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons) end # merge decide_options and default_decide_options if decide_options.is_a? Array decide_options += @default_decide_options else @logger.log(Logger::DEBUG, 'Provided decide options is not an array. Using default decide options.') decide_options = @default_decide_options end # Create Optimizely Decision Result. user_id = user_context.user_id attributes = user_context.user_attributes variation_key = nil feature_enabled = false rule_key = nil flag_key = key all_variables = {} decision_event_dispatched = false experiment = nil decision_source = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] decision, reasons_received = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes, decide_options) reasons.push(*reasons_received) # Send impression event if Decision came from a feature test and decide options doesn't include disableDecisionEvent if decision.is_a?(Optimizely::DecisionService::Decision) experiment = decision.experiment rule_key = experiment['key'] variation = decision['variation'] variation_key = variation['key'] feature_enabled = variation['featureEnabled'] decision_source = decision.source end unless decide_options.include? OptimizelyDecideOption::DISABLE_DECISION_EVENT if decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes) decision_event_dispatched = true end end # Generate all variables map if decide options doesn't include excludeVariables unless decide_options.include? OptimizelyDecideOption::EXCLUDE_VARIABLES feature_flag['variables'].each do |variable| variable_value = get_feature_variable_for_variation(key, feature_enabled, variation, variable, user_id) all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger) end end should_include_reasons = decide_options.include? OptimizelyDecideOption::INCLUDE_REASONS # Send notification @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], Helpers::Constants::DECISION_NOTIFICATION_TYPES['FLAG'], user_id, (attributes || {}), flag_key: flag_key, enabled: feature_enabled, variables: all_variables, variation_key: variation_key, rule_key: rule_key, reasons: should_include_reasons ? reasons : [], decision_event_dispatched: decision_event_dispatched ) OptimizelyDecision.new( variation_key: variation_key, enabled: feature_enabled, variables: all_variables, rule_key: rule_key, flag_key: flag_key, user_context: user_context, reasons: should_include_reasons ? reasons : [] ) end
# File lib/optimizely.rb, line 257 def decide_all(user_context, decide_options = []) # raising on user context as it is internal and not provided directly by the user. raise if user_context.class != OptimizelyUserContext # check if SDK is ready unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide_all').message) return {} end keys = [] project_config.feature_flags.each do |feature_flag| keys.push(feature_flag['key']) end decide_for_keys(user_context, keys, decide_options) end
# File lib/optimizely.rb, line 274 def decide_for_keys(user_context, keys, decide_options = []) # raising on user context as it is internal and not provided directly by the user. raise if user_context.class != OptimizelyUserContext # check if SDK is ready unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide_for_keys').message) return {} end enabled_flags_only = (!decide_options.nil? && (decide_options.include? OptimizelyDecideOption::ENABLED_FLAGS_ONLY)) || (@default_decide_options.include? OptimizelyDecideOption::ENABLED_FLAGS_ONLY) decisions = {} keys.each do |key| decision = decide(user_context, key, decide_options) decisions[key] = decision unless enabled_flags_only && !decision.enabled end decisions end
Get values of all the variables in the feature flag and returns them in a Dict
@param feature_flag_key - String key of feature flag @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [Dict] the Dict containing all the varible values @return [nil] if the feature flag is not found.
# File lib/optimizely.rb, line 718 def get_all_feature_variables(feature_flag_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_all_feature_variables').message) return nil end return nil unless Optimizely::Helpers::Validator.inputs_valid?( { feature_flag_key: feature_flag_key, user_id: user_id }, @logger, Logger::ERROR ) return nil unless user_inputs_valid?(attributes) config = project_config feature_flag = config.get_feature_flag_from_key(feature_flag_key) unless feature_flag @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.") return nil end decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes) variation = decision ? decision['variation'] : nil feature_enabled = variation ? variation['featureEnabled'] : false all_variables = {} feature_flag['variables'].each do |variable| variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id) all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger) end source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] source_info = { experiment_key: decision.experiment['key'], variation_key: variation['key'] } source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] end @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id, (attributes || {}), feature_key: feature_flag_key, feature_enabled: feature_enabled, source: source_string, variable_values: all_variables, source_info: source_info || {} ) all_variables end
Gets keys of all feature flags which are enabled for the user.
@param user_id - ID for user. @param attributes - Dict representing user attributes. @return [feature flag keys] A List of feature flag keys that are enabled for the user.
# File lib/optimizely.rb, line 550 def get_enabled_features(user_id, attributes = nil) enabled_features = [] unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_enabled_features').message) return enabled_features end return enabled_features unless Optimizely::Helpers::Validator.inputs_valid?( { user_id: user_id }, @logger, Logger::ERROR ) return enabled_features unless user_inputs_valid?(attributes) config = project_config config.feature_flags.each do |feature| enabled_features.push(feature['key']) if is_feature_enabled( feature['key'], user_id, attributes ) == true end enabled_features end
Get the value of the specified variable in the feature flag.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [*] the type-casted variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 587 def get_feature_variable(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, nil, user_id, attributes ) variable_value end
Get the Boolean value of the specified variable in the feature flag.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the string value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [Boolean] the boolean variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 665 def get_feature_variable_boolean(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_boolean').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['BOOLEAN'], user_id, attributes ) variable_value end
Get the Double value of the specified variable in the feature flag.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the string value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [Boolean] the double variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 692 def get_feature_variable_double(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_double').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['DOUBLE'], user_id, attributes ) variable_value end
Get the Integer value of the specified variable in the feature flag.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the string value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [Integer] variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 784 def get_feature_variable_integer(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_integer').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['INTEGER'], user_id, attributes ) variable_value end
Get the Json value of the specified variable in the feature flag in a Dict.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the string value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [Dict] the Dict containing variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 639 def get_feature_variable_json(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_json').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['JSON'], user_id, attributes ) variable_value end
Get the String value of the specified variable in the feature flag.
@param feature_flag_key - String key of feature flag the variable belongs to @param variable_key - String key of variable for which we are getting the string value @param user_id - String user ID @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [String] the string variable value. @return [nil] if the feature flag or variable are not found.
# File lib/optimizely.rb, line 613 def get_feature_variable_string(feature_flag_key, variable_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_string').message) return nil end variable_value = get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['STRING'], user_id, attributes ) variable_value end
Gets the forced variation for a given user and experiment.
@param experiment_key - String - Key identifying the experiment. @param user_id - String - The user ID to be used for bucketing.
@return [String] The forced variation key.
# File lib/optimizely.rb, line 393 def get_forced_variation(experiment_key, user_id) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_forced_variation').message) return nil end return nil unless Optimizely::Helpers::Validator.inputs_valid?( { experiment_key: experiment_key, user_id: user_id }, @logger, Logger::ERROR ) config = project_config forced_variation_key = nil forced_variation, = @decision_service.get_forced_variation(config, experiment_key, user_id) forced_variation_key = forced_variation['key'] if forced_variation forced_variation_key end
# File lib/optimizely.rb, line 814 def get_optimizely_config # Get OptimizelyConfig object containing experiments and features data # Returns Object # # OptimizelyConfig Object Schema # { # 'experimentsMap' => { # 'my-fist-experiment' => { # 'id' => '111111', # 'key' => 'my-fist-experiment' # 'variationsMap' => { # 'variation_1' => { # 'id' => '121212', # 'key' => 'variation_1', # 'variablesMap' => { # 'age' => { # 'id' => '222222', # 'key' => 'age', # 'type' => 'integer', # 'value' => '0', # } # } # } # } # } # }, # 'featuresMap' => { # 'awesome-feature' => { # 'id' => '333333', # 'key' => 'awesome-feature', # 'experimentsMap' => Object, # 'variablesMap' => Object, # } # }, # 'revision' => '13', # } # unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_optimizely_config').message) return nil end # config_manager might not contain optimizely_config if its supplied by the consumer # Generating a new OptimizelyConfig object in this case as a fallback if @config_manager.respond_to?(:optimizely_config) @config_manager.optimizely_config else OptimizelyConfig.new(project_config).config end end
Gets variation where visitor will be bucketed.
@param experiment_key - Experiment for which visitor variation needs to be determined. @param user_id - String ID for user. @param attributes - Hash representing user attributes.
@return [variation key] where visitor will be bucketed. @return [nil] if experiment is not Running, if user is not in experiment, or if datafile is invalid.
# File lib/optimizely.rb, line 344 def get_variation(experiment_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_variation').message) return nil end return nil unless Optimizely::Helpers::Validator.inputs_valid?( { experiment_key: experiment_key, user_id: user_id }, @logger, Logger::ERROR ) config = project_config get_variation_with_config(experiment_key, user_id, attributes, config) end
Determine whether a feature is enabled. Sends an impression event if the user is bucketed into an experiment using the feature.
@param feature_flag_key - String unique key of the feature. @param user_id - String ID of the user. @param attributes - Hash representing visitor attributes and values which need to be recorded.
@return [True] if the feature is enabled. @return [False] if the feature is disabled. @return [False] if the feature is not found.
# File lib/optimizely.rb, line 470 def is_feature_enabled(feature_flag_key, user_id, attributes = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('is_feature_enabled').message) return false end return false unless Optimizely::Helpers::Validator.inputs_valid?( { feature_flag_key: feature_flag_key, user_id: user_id }, @logger, Logger::ERROR ) return false unless user_inputs_valid?(attributes) config = project_config feature_flag = config.get_feature_flag_from_key(feature_flag_key) unless feature_flag @logger.log(Logger::ERROR, "No feature flag was found for key '#{feature_flag_key}'.") return false end decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes) feature_enabled = false source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] if decision.is_a?(Optimizely::DecisionService::Decision) variation = decision['variation'] feature_enabled = variation['featureEnabled'] if decision.source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] source_info = { experiment_key: decision.experiment['key'], variation_key: variation['key'] } # Send event if Decision came from a feature test. send_impression( config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes ) elsif decision.source == Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] && config.send_flag_decisions send_impression( config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes ) end end if decision.nil? && config.send_flag_decisions send_impression( config, nil, '', feature_flag_key, '', feature_enabled, source_string, user_id, attributes ) end @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE'], user_id, (attributes || {}), feature_key: feature_flag_key, feature_enabled: feature_enabled, source: source_string, source_info: source_info || {} ) if feature_enabled == true @logger.log(Logger::INFO, "Feature '#{feature_flag_key}' is enabled for user '#{user_id}'.") return true end @logger.log(Logger::INFO, "Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'.") false end
# File lib/optimizely.rb, line 801 def is_valid config = project_config config.is_a?(Optimizely::ProjectConfig) end
Force a user into a variation for a given experiment.
@param experiment_key - String - key identifying the experiment. @param user_id - String - The user ID to be used for bucketing. @param variation_key - The variation key specifies the variation which the user will
be forced into. If nil, then clear the existing experiment-to-variation mapping.
@return [Boolean] indicates if the set completed successfully.
# File lib/optimizely.rb, line 371 def set_forced_variation(experiment_key, user_id, variation_key) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('set_forced_variation').message) return nil end input_values = {experiment_key: experiment_key, user_id: user_id} input_values[:variation_key] = variation_key unless variation_key.nil? return false unless Optimizely::Helpers::Validator.inputs_valid?(input_values, @logger, Logger::ERROR) config = project_config @decision_service.set_forced_variation(config, experiment_key, user_id, variation_key) end
Send conversion event to Optimizely
.
@param event_key - Event
key representing the event which needs to be recorded. @param user_id - String ID for user. @param attributes - Hash representing visitor attributes and values which need to be recorded. @param event_tags - Hash representing metadata associated with the event.
# File lib/optimizely.rb, line 422 def track(event_key, user_id, attributes = nil, event_tags = nil) unless is_valid @logger.log(Logger::ERROR, InvalidProjectConfigError.new('track').message) return nil end return nil unless Optimizely::Helpers::Validator.inputs_valid?( { event_key: event_key, user_id: user_id }, @logger, Logger::ERROR ) return nil unless user_inputs_valid?(attributes, event_tags) config = project_config event = config.get_event_from_key(event_key) unless event @logger.log(Logger::INFO, "Not tracking user '#{user_id}' for event '#{event_key}'.") return nil end user_event = UserEventFactory.create_conversion_event(config, event, user_id, attributes, event_tags) @event_processor.process(user_event) @logger.log(Logger::INFO, "Tracking event '#{event_key}' for user '#{user_id}'.") if @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:TRACK]).positive? log_event = EventFactory.create_log_event(user_event, @logger) @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:TRACK], event_key, user_id, attributes, event_tags, log_event ) end nil end
Private Instance Methods
# File lib/optimizely.rb, line 1036 def attributes_valid?(attributes) unless Helpers::Validator.attributes_valid?(attributes) @logger.log(Logger::ERROR, 'Provided attributes are in an invalid format.') @error_handler.handle_error(InvalidAttributeFormatError) return false end true end
# File lib/optimizely.rb, line 902 def get_feature_variable_for_type(feature_flag_key, variable_key, variable_type, user_id, attributes = nil) # Get the variable value for the given feature variable and cast it to the specified type # The default value is returned if the feature flag is not enabled for the user. # # feature_flag_key - String key of feature flag the variable belongs to # variable_key - String key of variable for which we are getting the string value # variable_type - String requested type for feature variable # user_id - String user ID # attributes - Hash representing visitor attributes and values which need to be recorded. # # Returns the type-casted variable value. # Returns nil if the feature flag or variable or user ID is empty # in case of variable type mismatch return nil unless Optimizely::Helpers::Validator.inputs_valid?( { feature_flag_key: feature_flag_key, variable_key: variable_key, user_id: user_id, variable_type: variable_type }, @logger, Logger::ERROR ) return nil unless user_inputs_valid?(attributes) config = project_config feature_flag = config.get_feature_flag_from_key(feature_flag_key) unless feature_flag @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.") return nil end variable = config.get_feature_variable(feature_flag, variable_key) # Error message logged in DatafileProjectConfig- get_feature_flag_from_key return nil if variable.nil? # If variable_type is nil, set it equal to variable['type'] variable_type ||= variable['type'] # Returns nil if type differs if variable['type'] != variable_type @logger.log(Logger::WARN, "Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.") return nil end decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes) variation = decision ? decision['variation'] : nil feature_enabled = variation ? variation['featureEnabled'] : false variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id) variable_value = Helpers::VariableType.cast_value_to_type(variable_value, variable_type, @logger) source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] source_info = { experiment_key: decision.experiment['key'], variation_key: variation['key'] } source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] end @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}), feature_key: feature_flag_key, feature_enabled: feature_enabled, source: source_string, variable_key: variable_key, variable_type: variable_type, variable_value: variable_value, source_info: source_info || {} ) variable_value end
# File lib/optimizely.rb, line 981 def get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id) # Helper method to get the non type-casted value for a variable attached to a # feature flag. Returns appropriate variable value depending on whether there # was a matching variation, feature was enabled or not or varible was part of the # available variation or not. Also logs the appropriate message explaining how it # evaluated the value of the variable. # # feature_flag_key - String key of feature flag the variable belongs to # feature_enabled - Boolean indicating if feature is enabled or not # variation - varition returned by decision service # user_id - String user ID # # Returns string value of the variable. config = project_config variable_value = variable['defaultValue'] if variation if feature_enabled == true variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']] variable_id = variable['id'] if variation_variable_usages&.key?(variable_id) variable_value = variation_variable_usages[variable_id]['value'] @logger.log(Logger::INFO, "Got variable value '#{variable_value}' for variable '#{variable['key']}' of feature flag '#{feature_flag_key}'.") else @logger.log(Logger::DEBUG, "Variable value is not defined. Returning the default variable value '#{variable_value}' for variable '#{variable['key']}'.") end else @logger.log(Logger::DEBUG, "Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'. Returning the default variable value '#{variable_value}'.") end else @logger.log(Logger::INFO, "User '#{user_id}' was not bucketed into experiment or rollout for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.") end variable_value end
# File lib/optimizely.rb, line 867 def get_variation_with_config(experiment_key, user_id, attributes, config) # Gets variation where visitor will be bucketed. # # experiment_key - Experiment for which visitor variation needs to be determined. # user_id - String ID for user. # attributes - Hash representing user attributes. # config - Instance of DatfileProjectConfig # # Returns [variation key] where visitor will be bucketed. # Returns [nil] if experiment is not Running, if user is not in experiment, or if datafile is invalid. experiment = config.get_experiment_from_key(experiment_key) return nil if experiment.nil? experiment_id = experiment['id'] return nil unless user_inputs_valid?(attributes) variation_id, = @decision_service.get_variation(config, experiment_id, user_id, attributes) variation = config.get_variation_from_id(experiment_key, variation_id) unless variation_id.nil? variation_key = variation['key'] if variation decision_notification_type = if config.feature_experiment?(experiment_id) Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_TEST'] else Helpers::Constants::DECISION_NOTIFICATION_TYPES['AB_TEST'] end @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], decision_notification_type, user_id, (attributes || {}), experiment_key: experiment_key, variation_key: variation_key ) variation_key end
# File lib/optimizely.rb, line 1113 def project_config @config_manager.config end
# File lib/optimizely.rb, line 1068 def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil) if experiment.nil? experiment = { 'id' => '', 'key' => '', 'layerId' => '', 'status' => '', 'variations' => [], 'trafficAllocation' => [], 'audienceIds' => [], 'audienceConditions' => [], 'forcedVariations' => {} } end experiment_id = experiment['id'] experiment_key = experiment['key'] variation_id = '' variation_id = config.get_variation_id_from_key_by_experiment_id(experiment_id, variation_key) if experiment_id != '' metadata = { flag_key: flag_key, rule_key: rule_key, rule_type: rule_type, variation_key: variation_key, enabled: enabled } user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, metadata, user_id, attributes) @event_processor.process(user_event) return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive? @logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.") experiment = nil if experiment_id == '' variation = nil variation = config.get_variation_from_id_by_experiment_id(experiment_id, variation_id) unless experiment.nil? log_event = EventFactory.create_log_event(user_event, @logger) @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE], experiment, user_id, attributes, variation, log_event ) end
# File lib/optimizely.rb, line 1021 def user_inputs_valid?(attributes = nil, event_tags = nil) # Helper method to validate user inputs. # # attributes - Dict representing user attributes. # event_tags - Dict representing metadata associated with an event. # # Returns boolean True if inputs are valid. False otherwise. return false if !attributes.nil? && !attributes_valid?(attributes) return false if !event_tags.nil? && !event_tags_valid?(event_tags) true end
# File lib/optimizely.rb, line 1054 def validate_instantiation_options raise InvalidInputError, 'logger' unless Helpers::Validator.logger_valid?(@logger) unless Helpers::Validator.error_handler_valid?(@error_handler) @error_handler = NoOpErrorHandler.new raise InvalidInputError, 'error_handler' end return if Helpers::Validator.event_dispatcher_valid?(@event_dispatcher) @event_dispatcher = EventDispatcher.new(logger: @logger, error_handler: @error_handler) raise InvalidInputError, 'event_dispatcher' end