optimizely-sdk 3.3.2 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  )