optimizely-sdk 3.3.2 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7a2fc40c97158de70c4ec1d0eee4db872cac0b5a
4
- data.tar.gz: f754bd8cd02ef852bf16cc555ceca5a03ebd39f3
2
+ SHA256:
3
+ metadata.gz: 84b914d98655786586e8f691c82529bb3a8f074a6c157aceff6c2ef0ab4a4e3e
4
+ data.tar.gz: 484164de6c112f23adc94568371a33f564a99dba714c3141cf156783cb638a5d
5
5
  SHA512:
6
- metadata.gz: dfafb0abdfa7df11a1e2bd57423b1a543158ecbc3de564f1500274070c00071b1e87edf79f94f4e95b39cedd913e554865e51a783542c8a111ddaca1afd4641b
7
- data.tar.gz: 6bb7cba088585ecb5caa429e159b609653563001c90a185c8c22f8b187edce064b8c291a360072facf203118f40b9c3e45cf27d02603d65a444918dbbc5db44e
6
+ metadata.gz: b56b571c9aed79bdf0fdce664fa8ae24c152e52ac6804f3621c1597701a0293e4e105e0ce2db482d75640e81d7c8d9d1db085d70d0aa8827290277c87c280f68
7
+ data.tar.gz: a68af4ec2b0f7d02411e543aae1ca8e141d0be7a992e2d57ac6cd3c40e54ed3437ff39df312907914a679c7b75853902d4ab016b562428ae543f0af50730cabf
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2019, Optimizely and contributors
4
+ # Copyright 2016-2020, Optimizely and contributors
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ require_relative 'optimizely/helpers/validator'
33
33
  require_relative 'optimizely/helpers/variable_type'
34
34
  require_relative 'optimizely/logger'
35
35
  require_relative 'optimizely/notification_center'
36
+ require_relative 'optimizely/optimizely_config'
36
37
 
37
38
  module Optimizely
38
39
  class Project
@@ -139,7 +140,10 @@ module Optimizely
139
140
 
140
141
  # Create and dispatch impression event
141
142
  experiment = config.get_experiment_from_key(experiment_key)
142
- send_impression(config, experiment, variation_key, user_id, attributes)
143
+ send_impression(
144
+ config, experiment, variation_key, '', experiment_key, true,
145
+ Optimizely::DecisionService::DECISION_SOURCES['EXPERIMENT'], user_id, attributes
146
+ )
143
147
 
144
148
  variation_key
145
149
  end
@@ -315,14 +319,23 @@ module Optimizely
315
319
  experiment_key: decision.experiment['key'],
316
320
  variation_key: variation['key']
317
321
  }
318
- # Send event if Decision came from an experiment.
319
- send_impression(config, decision.experiment, variation['key'], user_id, attributes)
320
- else
321
- @logger.log(Logger::DEBUG,
322
- "The user '#{user_id}' is not being experimented on in feature '#{feature_flag_key}'.")
322
+ # Send event if Decision came from a feature test.
323
+ send_impression(
324
+ config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes
325
+ )
326
+ elsif decision.source == Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] && config.send_flag_decisions
327
+ send_impression(
328
+ config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes
329
+ )
323
330
  end
324
331
  end
325
332
 
333
+ if decision.nil? && config.send_flag_decisions
334
+ send_impression(
335
+ config, nil, '', feature_flag_key, '', feature_enabled, source_string, user_id, attributes
336
+ )
337
+ end
338
+
326
339
  @notification_center.send_notifications(
327
340
  NotificationCenter::NOTIFICATION_TYPES[:DECISION],
328
341
  Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE'],
@@ -429,6 +442,32 @@ module Optimizely
429
442
  variable_value
430
443
  end
431
444
 
445
+ # Get the Json value of the specified variable in the feature flag in a Dict.
446
+ #
447
+ # @param feature_flag_key - String key of feature flag the variable belongs to
448
+ # @param variable_key - String key of variable for which we are getting the string value
449
+ # @param user_id - String user ID
450
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
451
+ #
452
+ # @return [Dict] the Dict containing variable value.
453
+ # @return [nil] if the feature flag or variable are not found.
454
+
455
+ def get_feature_variable_json(feature_flag_key, variable_key, user_id, attributes = nil)
456
+ unless is_valid
457
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_json').message)
458
+ return nil
459
+ end
460
+ variable_value = get_feature_variable_for_type(
461
+ feature_flag_key,
462
+ variable_key,
463
+ Optimizely::Helpers::Constants::VARIABLE_TYPES['JSON'],
464
+ user_id,
465
+ attributes
466
+ )
467
+
468
+ variable_value
469
+ end
470
+
432
471
  # Get the Boolean value of the specified variable in the feature flag.
433
472
  #
434
473
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -483,6 +522,71 @@ module Optimizely
483
522
  variable_value
484
523
  end
485
524
 
525
+ # Get values of all the variables in the feature flag and returns them in a Dict
526
+ #
527
+ # @param feature_flag_key - String key of feature flag
528
+ # @param user_id - String user ID
529
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
530
+ #
531
+ # @return [Dict] the Dict containing all the varible values
532
+ # @return [nil] if the feature flag is not found.
533
+
534
+ def get_all_feature_variables(feature_flag_key, user_id, attributes = nil)
535
+ unless is_valid
536
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_all_feature_variables').message)
537
+ return nil
538
+ end
539
+
540
+ return nil unless Optimizely::Helpers::Validator.inputs_valid?(
541
+ {
542
+ feature_flag_key: feature_flag_key,
543
+ user_id: user_id
544
+ },
545
+ @logger, Logger::ERROR
546
+ )
547
+
548
+ return nil unless user_inputs_valid?(attributes)
549
+
550
+ config = project_config
551
+
552
+ feature_flag = config.get_feature_flag_from_key(feature_flag_key)
553
+ unless feature_flag
554
+ @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.")
555
+ return nil
556
+ end
557
+
558
+ decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
559
+ variation = decision ? decision['variation'] : nil
560
+ feature_enabled = variation ? variation['featureEnabled'] : false
561
+ all_variables = {}
562
+
563
+ feature_flag['variables'].each do |variable|
564
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
565
+ all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger)
566
+ end
567
+
568
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
569
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
570
+ source_info = {
571
+ experiment_key: decision.experiment['key'],
572
+ variation_key: variation['key']
573
+ }
574
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
575
+ end
576
+
577
+ @notification_center.send_notifications(
578
+ NotificationCenter::NOTIFICATION_TYPES[:DECISION],
579
+ Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id, (attributes || {}),
580
+ feature_key: feature_flag_key,
581
+ feature_enabled: feature_enabled,
582
+ source: source_string,
583
+ variable_values: all_variables,
584
+ source_info: source_info || {}
585
+ )
586
+
587
+ all_variables
588
+ end
589
+
486
590
  # Get the Integer value of the specified variable in the feature flag.
487
591
  #
488
592
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -523,6 +627,57 @@ module Optimizely
523
627
  @event_processor.stop! if @event_processor.respond_to?(:stop!)
524
628
  end
525
629
 
630
+ def get_optimizely_config
631
+ # Get OptimizelyConfig object containing experiments and features data
632
+ # Returns Object
633
+ #
634
+ # OptimizelyConfig Object Schema
635
+ # {
636
+ # 'experimentsMap' => {
637
+ # 'my-fist-experiment' => {
638
+ # 'id' => '111111',
639
+ # 'key' => 'my-fist-experiment'
640
+ # 'variationsMap' => {
641
+ # 'variation_1' => {
642
+ # 'id' => '121212',
643
+ # 'key' => 'variation_1',
644
+ # 'variablesMap' => {
645
+ # 'age' => {
646
+ # 'id' => '222222',
647
+ # 'key' => 'age',
648
+ # 'type' => 'integer',
649
+ # 'value' => '0',
650
+ # }
651
+ # }
652
+ # }
653
+ # }
654
+ # }
655
+ # },
656
+ # 'featuresMap' => {
657
+ # 'awesome-feature' => {
658
+ # 'id' => '333333',
659
+ # 'key' => 'awesome-feature',
660
+ # 'experimentsMap' => Object,
661
+ # 'variablesMap' => Object,
662
+ # }
663
+ # },
664
+ # 'revision' => '13',
665
+ # }
666
+ #
667
+ unless is_valid
668
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_optimizely_config').message)
669
+ return nil
670
+ end
671
+
672
+ # config_manager might not contain optimizely_config if its supplied by the consumer
673
+ # Generating a new OptimizelyConfig object in this case as a fallback
674
+ if @config_manager.respond_to?(:optimizely_config)
675
+ @config_manager.optimizely_config
676
+ else
677
+ OptimizelyConfig.new(project_config).config
678
+ end
679
+ end
680
+
526
681
  private
527
682
 
528
683
  def get_variation_with_config(experiment_key, user_id, attributes, config)
@@ -597,8 +752,6 @@ module Optimizely
597
752
  # Error message logged in DatafileProjectConfig- get_feature_flag_from_key
598
753
  return nil if variable.nil?
599
754
 
600
- feature_enabled = false
601
-
602
755
  # If variable_type is nil, set it equal to variable['type']
603
756
  variable_type ||= variable['type']
604
757
  # Returns nil if type differs
@@ -606,43 +759,24 @@ module Optimizely
606
759
  @logger.log(Logger::WARN,
607
760
  "Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.")
608
761
  return nil
609
- else
610
- source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
611
- decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
612
- variable_value = variable['defaultValue']
613
- if decision
614
- variation = decision['variation']
615
- if decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
616
- source_info = {
617
- experiment_key: decision.experiment['key'],
618
- variation_key: variation['key']
619
- }
620
- source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
621
- end
622
- feature_enabled = variation['featureEnabled']
623
- if feature_enabled == true
624
- variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']]
625
- variable_id = variable['id']
626
- if variation_variable_usages&.key?(variable_id)
627
- variable_value = variation_variable_usages[variable_id]['value']
628
- @logger.log(Logger::INFO,
629
- "Got variable value '#{variable_value}' for variable '#{variable_key}' of feature flag '#{feature_flag_key}'.")
630
- else
631
- @logger.log(Logger::DEBUG,
632
- "Variable '#{variable_key}' is not used in variation '#{variation['key']}'. Returning the default variable value '#{variable_value}'.")
633
- end
634
- else
635
- @logger.log(Logger::DEBUG,
636
- "Feature '#{feature_flag_key}' for variation '#{variation['key']}' is not enabled. Returning the default variable value '#{variable_value}'.")
637
- end
638
- else
639
- @logger.log(Logger::INFO,
640
- "User '#{user_id}' was not bucketed into any variation for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.")
641
- end
642
762
  end
643
763
 
764
+ decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
765
+ variation = decision ? decision['variation'] : nil
766
+ feature_enabled = variation ? variation['featureEnabled'] : false
767
+
768
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
644
769
  variable_value = Helpers::VariableType.cast_value_to_type(variable_value, variable_type, @logger)
645
770
 
771
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
772
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
773
+ source_info = {
774
+ experiment_key: decision.experiment['key'],
775
+ variation_key: variation['key']
776
+ }
777
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
778
+ end
779
+
646
780
  @notification_center.send_notifications(
647
781
  NotificationCenter::NOTIFICATION_TYPES[:DECISION],
648
782
  Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}),
@@ -658,6 +792,46 @@ module Optimizely
658
792
  variable_value
659
793
  end
660
794
 
795
+ def get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
796
+ # Helper method to get the non type-casted value for a variable attached to a
797
+ # feature flag. Returns appropriate variable value depending on whether there
798
+ # was a matching variation, feature was enabled or not or varible was part of the
799
+ # available variation or not. Also logs the appropriate message explaining how it
800
+ # evaluated the value of the variable.
801
+ #
802
+ # feature_flag_key - String key of feature flag the variable belongs to
803
+ # feature_enabled - Boolean indicating if feature is enabled or not
804
+ # variation - varition returned by decision service
805
+ # user_id - String user ID
806
+ #
807
+ # Returns string value of the variable.
808
+
809
+ config = project_config
810
+ variable_value = variable['defaultValue']
811
+ if variation
812
+ if feature_enabled == true
813
+ variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']]
814
+ variable_id = variable['id']
815
+ if variation_variable_usages&.key?(variable_id)
816
+ variable_value = variation_variable_usages[variable_id]['value']
817
+ @logger.log(Logger::INFO,
818
+ "Got variable value '#{variable_value}' for variable '#{variable['key']}' of feature flag '#{feature_flag_key}'.")
819
+ else
820
+ @logger.log(Logger::DEBUG,
821
+ "Variable value is not defined. Returning the default variable value '#{variable_value}' for variable '#{variable['key']}'.")
822
+
823
+ end
824
+ else
825
+ @logger.log(Logger::DEBUG,
826
+ "Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'. Returning the default variable value '#{variable_value}'.")
827
+ end
828
+ else
829
+ @logger.log(Logger::INFO,
830
+ "User '#{user_id}' was not bucketed into experiment or rollout for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.")
831
+ end
832
+ variable_value
833
+ end
834
+
661
835
  def user_inputs_valid?(attributes = nil, event_tags = nil)
662
836
  # Helper method to validate user inputs.
663
837
  #
@@ -705,15 +879,43 @@ module Optimizely
705
879
  raise InvalidInputError, 'event_dispatcher'
706
880
  end
707
881
 
708
- def send_impression(config, experiment, variation_key, user_id, attributes = nil)
882
+ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil)
883
+ if experiment.nil?
884
+ experiment = {
885
+ 'id' => '',
886
+ 'key' => '',
887
+ 'layerId' => '',
888
+ 'status' => '',
889
+ 'variations' => [],
890
+ 'trafficAllocation' => [],
891
+ 'audienceIds' => [],
892
+ 'audienceConditions' => [],
893
+ 'forcedVariations' => {}
894
+ }
895
+ end
896
+
709
897
  experiment_key = experiment['key']
710
- variation_id = config.get_variation_id_from_key(experiment_key, variation_key)
711
- user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, user_id, attributes)
898
+
899
+ variation_id = ''
900
+ variation_id = config.get_variation_id_from_key(experiment_key, variation_key) if experiment_key != ''
901
+
902
+ metadata = {
903
+ flag_key: flag_key,
904
+ rule_key: rule_key,
905
+ rule_type: rule_type,
906
+ variation_key: variation_key,
907
+ enabled: enabled
908
+ }
909
+
910
+ user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, metadata, user_id, attributes)
712
911
  @event_processor.process(user_event)
713
912
  return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive?
714
913
 
715
914
  @logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.")
716
- variation = config.get_variation_from_id(experiment_key, variation_id)
915
+
916
+ experiment = nil if experiment_key == ''
917
+ variation = nil
918
+ variation = config.get_variation_from_id(experiment_key, variation_id) unless experiment.nil?
717
919
  log_event = EventFactory.create_log_event(user_event, @logger)
718
920
  @notification_center.send_notifications(
719
921
  NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE],
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2017, 2019, Optimizely and contributors
4
+ # Copyright 2016-2017, 2019-2020, Optimizely and contributors
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -24,23 +24,32 @@ module Optimizely
24
24
  module Audience
25
25
  module_function
26
26
 
27
- def user_in_experiment?(config, experiment, attributes, logger)
28
- # Determine for given experiment if user satisfies the audiences for the experiment.
27
+ def user_meets_audience_conditions?(config, experiment, attributes, logger, logging_hash = nil, logging_key = nil)
28
+ # Determine for given experiment/rollout rule if user satisfies the audience conditions.
29
29
  #
30
30
  # config - Representation of the Optimizely project config.
31
- # experiment - Experiment for which visitor is to be bucketed.
31
+ # experiment - Experiment/Rollout rule in which user is to be bucketed.
32
32
  # attributes - Hash representing user attributes which will be used in determining if
33
33
  # the audience conditions are met.
34
+ # logger - Provides a logger instance.
35
+ # logging_hash - Optional string representing logs hash inside Helpers::Constants.
36
+ # This defaults to 'EXPERIMENT_AUDIENCE_EVALUATION_LOGS'.
37
+ # logging_key - Optional string to be logged as an identifier of experiment under evaluation.
38
+ # This defaults to experiment['key'].
34
39
  #
35
40
  # Returns boolean representing if user satisfies audience conditions for the audiences or not.
41
+ logging_hash ||= 'EXPERIMENT_AUDIENCE_EVALUATION_LOGS'
42
+ logging_key ||= experiment['key']
43
+
44
+ logs_hash = Object.const_get "Optimizely::Helpers::Constants::#{logging_hash}"
36
45
 
37
46
  audience_conditions = experiment['audienceConditions'] || experiment['audienceIds']
38
47
 
39
48
  logger.log(
40
49
  Logger::DEBUG,
41
50
  format(
42
- Helpers::Constants::AUDIENCE_EVALUATION_LOGS['EVALUATING_AUDIENCES_COMBINED'],
43
- experiment['key'],
51
+ logs_hash['EVALUATING_AUDIENCES_COMBINED'],
52
+ logging_key,
44
53
  audience_conditions
45
54
  )
46
55
  )
@@ -50,8 +59,8 @@ module Optimizely
50
59
  logger.log(
51
60
  Logger::INFO,
52
61
  format(
53
- Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT_COMBINED'],
54
- experiment['key'],
62
+ logs_hash['AUDIENCE_EVALUATION_RESULT_COMBINED'],
63
+ logging_key,
55
64
  'TRUE'
56
65
  )
57
66
  )
@@ -74,7 +83,7 @@ module Optimizely
74
83
  logger.log(
75
84
  Logger::DEBUG,
76
85
  format(
77
- Helpers::Constants::AUDIENCE_EVALUATION_LOGS['EVALUATING_AUDIENCE'],
86
+ logs_hash['EVALUATING_AUDIENCE'],
78
87
  audience_id,
79
88
  audience_conditions
80
89
  )
@@ -84,8 +93,8 @@ module Optimizely
84
93
  result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_custom_attr)
85
94
  result_str = result.nil? ? 'UNKNOWN' : result.to_s.upcase
86
95
  logger.log(
87
- Logger::INFO,
88
- format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT'], audience_id, result_str)
96
+ Logger::DEBUG,
97
+ format(logs_hash['AUDIENCE_EVALUATION_RESULT'], audience_id, result_str)
89
98
  )
90
99
  result
91
100
  end
@@ -97,8 +106,8 @@ module Optimizely
97
106
  logger.log(
98
107
  Logger::INFO,
99
108
  format(
100
- Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT_COMBINED'],
101
- experiment['key'],
109
+ logs_hash['AUDIENCE_EVALUATION_RESULT_COMBINED'],
110
+ logging_key,
102
111
  eval_result.to_s.upcase
103
112
  )
104
113
  )