From 4cf5f436a0ccda741f044a57ee47734a4300d492 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Thu, 24 Sep 2020 19:19:17 +0500
Subject: [PATCH 01/15] Implemented flag decision logic

---
 .../OptimizelySDK.NetStandard20.csproj        |  3 +++
 OptimizelySDK.Tests/TestData.json             |  3 ++-
 OptimizelySDK/Config/DatafileProjectConfig.cs |  5 +++++
 OptimizelySDK/Event/Builder/EventBuilder.cs   | 16 +++++++++++----
 OptimizelySDK/Event/Builder/Params.cs         |  2 ++
 OptimizelySDK/Event/Entity/Decision.cs        |  6 ++++--
 .../Event/Entity/DecisionMetadata.cs          | 20 +++++++++++++++++++
 OptimizelySDK/Event/Entity/ImpressionEvent.cs | 10 ++++++++++
 OptimizelySDK/Event/EventFactory.cs           |  3 ++-
 OptimizelySDK/Event/UserEventFactory.cs       | 16 ++++++++++++---
 OptimizelySDK/Optimizely.cs                   | 14 +++++++------
 OptimizelySDK/OptimizelySDK.csproj            |  1 +
 OptimizelySDK/ProjectConfig.cs                |  6 ++++++
 13 files changed, 88 insertions(+), 17 deletions(-)
 create mode 100644 OptimizelySDK/Event/Entity/DecisionMetadata.cs

diff --git a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
index 82cf73c9..1a126f2c 100644
--- a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
+++ b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
@@ -174,6 +174,9 @@
     </Compile>
     <Compile Include="..\OptimizelySDK\Event\Entity\Decision.cs">
       <Link>Event\Entity\Decision.cs</Link>
+    </Compile>
+	<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
+      <Link>Event\Entity\DecisionMetadata.cs</Link>
     </Compile>
     <Compile Include="..\OptimizelySDK\Event\Entity\EventBatch.cs">
       <Link>Event\Entity\EventBatch.cs</Link>
diff --git a/OptimizelySDK.Tests/TestData.json b/OptimizelySDK.Tests/TestData.json
index 11f3decb..b8b72fbc 100644
--- a/OptimizelySDK.Tests/TestData.json
+++ b/OptimizelySDK.Tests/TestData.json
@@ -565,7 +565,7 @@
 					"key": "integer_variable",
 					"type": "integer",
 					"defaultValue": "7"
-				}				
+				}
 			]
 		},
 		{
@@ -940,5 +940,6 @@
 	],
 	"revision": "15",
 	"anonymizeIP": false,
+	"sendFlagDecisions": true,
 	"botFiltering": true
 }
diff --git a/OptimizelySDK/Config/DatafileProjectConfig.cs b/OptimizelySDK/Config/DatafileProjectConfig.cs
index a90df7fa..fc479b0c 100644
--- a/OptimizelySDK/Config/DatafileProjectConfig.cs
+++ b/OptimizelySDK/Config/DatafileProjectConfig.cs
@@ -69,6 +69,11 @@ public enum OPTLYSDKVersion
         /// </summary>
         public string Revision { get; set; }
 
+        /// <summary>
+        /// SendFlagDecisions determines whether impressions events are sent for ALL decision types.
+        /// </summary>
+        public bool SendFlagDecisions { get; set; }
+        
         /// <summary>
         /// Allow Anonymize IP by truncating the last block of visitors' IP address.
         /// </summary>
diff --git a/OptimizelySDK/Event/Builder/EventBuilder.cs b/OptimizelySDK/Event/Builder/EventBuilder.cs
index 0f0b41b8..8f24390b 100644
--- a/OptimizelySDK/Event/Builder/EventBuilder.cs
+++ b/OptimizelySDK/Event/Builder/EventBuilder.cs
@@ -15,6 +15,7 @@
  */
 using OptimizelySDK.Bucketing;
 using OptimizelySDK.Entity;
+using OptimizelySDK.Event.Entity;
 using OptimizelySDK.Logger;
 using OptimizelySDK.Utils;
 using System;
@@ -121,10 +122,16 @@ private Dictionary<string, object> GetCommonParams(ProjectConfig config, string
             return comonParams;
         }
 
-        private Dictionary<string, object> GetImpressionParams(Experiment experiment, string variationId)
+        private Dictionary<string, object> GetImpressionParams(Experiment experiment,
+            string variationId,
+            string flagKey,
+            string flagType)
         {
 
             var impressionEvent = new Dictionary<string, object>();
+            
+            string variationKey = variationId != null ? experiment.VariationIdToVariationMap[variationId].Key : null;
+            var metadata = new DecisionMetadata(flagKey, flagType, variationKey);
 
             var decisions = new object[]
             {
@@ -132,7 +139,8 @@ private Dictionary<string, object> GetImpressionParams(Experiment experiment, st
                     {
                         { Params.CAMPAIGN_ID,   experiment.LayerId },
                         { Params.EXPERIMENT_ID, experiment.Id },
-                        { Params.VARIATION_ID,  variationId }
+                        { Params.VARIATION_ID,  variationId },
+                        { Params.METADATA,  metadata }
                     }
             };
 
@@ -216,11 +224,11 @@ private Dictionary<string, object> GetImpressionOrConversionParamsWithCommonPara
         /// <param name="userAttributes">associative array of attributes for the user</param>
         /// <returns>LogEvent object to be sent to dispatcher</returns>
         public virtual LogEvent CreateImpressionEvent(ProjectConfig config, Experiment experiment, string variationId,
-            string userId, UserAttributes userAttributes)
+            string userId, UserAttributes userAttributes, string flagKey, string flagType)
         {
 
             var commonParams = GetCommonParams(config, userId, userAttributes ?? new UserAttributes());
-            var impressionOnlyParams = GetImpressionParams(experiment, variationId);
+            var impressionOnlyParams = GetImpressionParams(experiment, variationId, flagKey, flagType);
 
             var impressionParams = GetImpressionOrConversionParamsWithCommonParams(commonParams, new object[] { impressionOnlyParams });
 
diff --git a/OptimizelySDK/Event/Builder/Params.cs b/OptimizelySDK/Event/Builder/Params.cs
index 8b12f96a..14d0a994 100644
--- a/OptimizelySDK/Event/Builder/Params.cs
+++ b/OptimizelySDK/Event/Builder/Params.cs
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+using OptimizelySDK.Event.Entity;
 using System;
 
 namespace OptimizelySDK.Event.Builder
@@ -30,6 +31,7 @@ public static class Params
         public const string ENTITY_ID           = "entity_id";
         public const string EVENTS              = "events";
         public const string EXPERIMENT_ID       = "experiment_id";
+        public const string METADATA            = "metadata";
         public const string PROJECT_ID          = "project_id";
         public const string REVISION            = "revision";
         public const string TIME                = "timestamp";
diff --git a/OptimizelySDK/Event/Entity/Decision.cs b/OptimizelySDK/Event/Entity/Decision.cs
index c25f280f..dc2d5fa8 100644
--- a/OptimizelySDK/Event/Entity/Decision.cs
+++ b/OptimizelySDK/Event/Entity/Decision.cs
@@ -23,15 +23,17 @@ public class Decision
         public string CampaignId { get; private set; }
         [JsonProperty("experiment_id")]
         public string ExperimentId { get; private set; }
+        [JsonProperty("metadata")]
+        public DecisionMetadata Metadata { get; private set; }
         [JsonProperty("variation_id")]
         public string VariationId { get; private set; }
-
         public Decision() {}
 
-        public Decision(string campaignId, string experimentId, string variationId)
+        public Decision(string campaignId, string experimentId, string variationId, DecisionMetadata metadata)
         {
             CampaignId = campaignId;
             ExperimentId = experimentId;
+            Metadata = metadata;
             VariationId = variationId;
         }
     }
diff --git a/OptimizelySDK/Event/Entity/DecisionMetadata.cs b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
new file mode 100644
index 00000000..8bc6318b
--- /dev/null
+++ b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
@@ -0,0 +1,20 @@
+
+namespace OptimizelySDK.Event.Entity
+{
+    /// <summary>
+    /// DecisionMetadata captures additional information regarding the decision
+    /// </summary>
+    public class DecisionMetadata
+    {
+        public string FlagType { get; private set; }
+        public string FlagKey { get; private set; }
+        public string VariationKey { get; private set; }
+
+        public DecisionMetadata(string flagKey, string flagType, string variationKey = null) 
+        {
+            FlagType = flagType;
+            FlagKey = flagKey;
+            VariationKey = variationKey;
+        }
+    }
+}
diff --git a/OptimizelySDK/Event/Entity/ImpressionEvent.cs b/OptimizelySDK/Event/Entity/ImpressionEvent.cs
index 7b3777a1..d8226f97 100644
--- a/OptimizelySDK/Event/Entity/ImpressionEvent.cs
+++ b/OptimizelySDK/Event/Entity/ImpressionEvent.cs
@@ -28,6 +28,7 @@ public class ImpressionEvent : UserEvent
         public VisitorAttribute[] VisitorAttributes { get; private set; }
 
         public Experiment Experiment { get; set; }
+        public DecisionMetadata Metadata { get; set; }
         public Variation Variation { get; set; }
         public bool? BotFiltering { get; set; }
 
@@ -42,6 +43,7 @@ public class Builder
             public VisitorAttribute[] VisitorAttributes;
             private Experiment Experiment;
             private Variation Variation;
+            private DecisionMetadata Metadata;
             private bool? BotFiltering;
 
             public Builder WithUserId(string userId)
@@ -65,6 +67,13 @@ public Builder WithExperiment(Experiment experiment)
                 return this;
             }
 
+            public Builder WithMetadata(DecisionMetadata metadata)
+            {
+                Metadata = metadata;
+
+                return this;
+            }
+
             public Builder WithVisitorAttributes(VisitorAttribute[] visitorAttributes)
             {
                 VisitorAttributes = visitorAttributes;
@@ -102,6 +111,7 @@ public ImpressionEvent Build()
                 impressionEvent.VisitorAttributes = VisitorAttributes;
                 impressionEvent.UserId = UserId;
                 impressionEvent.Variation = Variation;
+                impressionEvent.Metadata = Metadata;
                 impressionEvent.BotFiltering = BotFiltering;
 
                 return impressionEvent;
diff --git a/OptimizelySDK/Event/EventFactory.cs b/OptimizelySDK/Event/EventFactory.cs
index 7097ea03..f91e2fcf 100644
--- a/OptimizelySDK/Event/EventFactory.cs
+++ b/OptimizelySDK/Event/EventFactory.cs
@@ -115,7 +115,8 @@ private static Visitor CreateVisitor(ImpressionEvent impressionEvent) {
 
             Decision decision = new Decision(impressionEvent.Experiment?.LayerId,
                 impressionEvent.Experiment?.Id,
-                impressionEvent.Variation?.Id);
+                impressionEvent.Variation?.Id,
+                impressionEvent.Metadata);
 
             SnapshotEvent snapshotEvent = new SnapshotEvent.Builder()
                 .WithUUID(impressionEvent.UUID)
diff --git a/OptimizelySDK/Event/UserEventFactory.cs b/OptimizelySDK/Event/UserEventFactory.cs
index 2733c673..f8bc243b 100644
--- a/OptimizelySDK/Event/UserEventFactory.cs
+++ b/OptimizelySDK/Event/UserEventFactory.cs
@@ -21,10 +21,12 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             Experiment activatedExperiment,
                                                             string variationId,
                                                             string userId,
-                                                            UserAttributes userAttributes)
+                                                            UserAttributes userAttributes,
+                                                            string flagKey,
+                                                            string flagType)
         {
             Variation variation = projectConfig.GetVariationFromId(activatedExperiment?.Key, variationId);
-            return CreateImpressionEvent(projectConfig, activatedExperiment, variation, userId, userAttributes);
+            return CreateImpressionEvent(projectConfig, activatedExperiment, variation, userId, userAttributes, flagKey, flagType);
         }
 
         /// <summary>
@@ -35,12 +37,16 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
         /// <param name="variation">The variation entity</param>
         /// <param name="userId">The user Id</param>
         /// <param name="userAttributes">The user's attributes</param>
+        /// <param name="flagKey">experiment key or feature key</param>
+        /// <param name="flagType">experiment or featureDecision source </param>
         /// <returns>ImpressionEvent instance</returns>
         public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             Experiment activatedExperiment,
                                                             Variation variation,
                                                             string userId,
-                                                            UserAttributes userAttributes)
+                                                            UserAttributes userAttributes,
+                                                            string flagKey,
+                                                            string flagType)
         {
 
             var eventContext = new EventContext.Builder()
@@ -50,10 +56,14 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                 .WithRevision(projectConfig.Revision)                
                 .Build();
 
+            var variationKey = variation?.Key;
+            var metadata = new DecisionMetadata(flagKey, flagType, variationKey);
+
             return new ImpressionEvent.Builder()
                 .WithEventContext(eventContext)
                 .WithBotFilteringEnabled(projectConfig.BotFiltering)
                 .WithExperiment(activatedExperiment)
+                .WithMetadata(metadata)
                 .WithUserId(userId)
                 .WithVariation(variation)
                 .WithVisitorAttributes(EventFactory.BuildAttributeList(userAttributes, projectConfig))
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index e9353384..0bef0b8a 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -252,7 +252,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
                 return null;
             }
 
-            SendImpressionEvent(experiment, variation, userId, userAttributes, config);
+            SendImpressionEvent(experiment, variation, userId, userAttributes, config, experimentKey, "experiment");
 
             return variation;
         }
@@ -470,17 +470,18 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
             bool featureEnabled = false;
             var sourceInfo = new Dictionary<string, string>();
             var decision = DecisionService.GetVariationForFeature(featureFlag, userId, config, userAttributes);
+            var variation = decision.Variation;
 
-            if (decision.Variation != null)
+            SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config, featureKey, decision.Source);
+
+            if (variation != null)
             {
-                var variation = decision.Variation;
                 featureEnabled = variation.FeatureEnabled.GetValueOrDefault();
 
                 if (decision.Source == FeatureDecision.DECISION_SOURCE_FEATURE_TEST)
                 {
                     sourceInfo["experimentKey"] = decision.Experiment.Key;
                     sourceInfo["variationKey"] = variation.Key;
-                    SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config);
                 }
                 else
                 {
@@ -684,11 +685,12 @@ public OptimizelyJSON GetFeatureVariableJSON(string featureKey, string variableK
         /// <param name="userId">The user ID</param>
         /// <param name="userAttributes">The user's attributes</param>
         private void SendImpressionEvent(Experiment experiment, Variation variation, string userId,
-                                         UserAttributes userAttributes, ProjectConfig config)
+                                         UserAttributes userAttributes, ProjectConfig config,
+                                         string flagKey, string flagType)
         {
             if (experiment.IsExperimentRunning)
             {
-                var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation.Id, userId, userAttributes);
+                var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation.Id, userId, userAttributes, flagKey, flagType);
                 EventProcessor.Process(userEvent);
                 Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
 
diff --git a/OptimizelySDK/OptimizelySDK.csproj b/OptimizelySDK/OptimizelySDK.csproj
index 6b25e27e..9964d942 100644
--- a/OptimizelySDK/OptimizelySDK.csproj
+++ b/OptimizelySDK/OptimizelySDK.csproj
@@ -86,6 +86,7 @@
     <Compile Include="Entity\ForcedVariation.cs" />
     <Compile Include="Entity\Group.cs" />
     <Compile Include="Entity\IdKeyEntity.cs" />
+    <Compile Include="Event\Entity\DecisionMetadata.cs" />
     <Compile Include="OptimizelyJSON.cs" />
     <Compile Include="Entity\Rollout.cs" />
     <Compile Include="Entity\TrafficAllocation.cs" />
diff --git a/OptimizelySDK/ProjectConfig.cs b/OptimizelySDK/ProjectConfig.cs
index f51edb66..0b56c2e2 100644
--- a/OptimizelySDK/ProjectConfig.cs
+++ b/OptimizelySDK/ProjectConfig.cs
@@ -44,6 +44,12 @@ public interface ProjectConfig
         /// </summary>
         string Revision { get; set; }
 
+
+        /// <summary>
+        /// SendFlagDecisions determines whether impressions events are sent for ALL decision types.
+        /// </summary>
+        bool SendFlagDecisions { get; set; }
+
         /// <summary>
         /// Allow Anonymize IP by truncating the last block of visitors' IP address.
         /// </summary>

From 314f13bd14717361806ff8087fb0e4a63398340c Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Fri, 25 Sep 2020 17:39:33 +0500
Subject: [PATCH 02/15] added check that if SendFlagDecision is true then send
 the event

---
 OptimizelySDK/Event/UserEventFactory.cs | 19 ++++++++++++-------
 OptimizelySDK/Optimizely.cs             |  6 ++++++
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/OptimizelySDK/Event/UserEventFactory.cs b/OptimizelySDK/Event/UserEventFactory.cs
index f8bc243b..22296c1c 100644
--- a/OptimizelySDK/Event/UserEventFactory.cs
+++ b/OptimizelySDK/Event/UserEventFactory.cs
@@ -1,4 +1,5 @@
-using OptimizelySDK.Entity;
+using OptimizelySDK.Bucketing;
+using OptimizelySDK.Entity;
 using OptimizelySDK.Event.Entity;
 
 namespace OptimizelySDK.Event
@@ -48,13 +49,17 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             string flagKey,
                                                             string flagType)
         {
-
+            if ((flagType == FeatureDecision.DECISION_SOURCE_ROLLOUT || variation == null) && !projectConfig.SendFlagDecisions)
+            {
+                return null;
+            }
+            
             var eventContext = new EventContext.Builder()
-                .WithProjectId(projectConfig.ProjectId)
-                .WithAccountId(projectConfig.AccountId)
-                .WithAnonymizeIP(projectConfig.AnonymizeIP)
-                .WithRevision(projectConfig.Revision)                
-                .Build();
+            .WithProjectId(projectConfig.ProjectId)
+            .WithAccountId(projectConfig.AccountId)
+            .WithAnonymizeIP(projectConfig.AnonymizeIP)
+            .WithRevision(projectConfig.Revision)                
+            .Build();
 
             var variationKey = variation?.Key;
             var metadata = new DecisionMetadata(flagKey, flagType, variationKey);
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 0bef0b8a..5380198a 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -478,6 +478,8 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
             {
                 featureEnabled = variation.FeatureEnabled.GetValueOrDefault();
 
+                // This information is only necessary for feature tests.
+                // For rollouts experiments and variations are an implementation detail only.
                 if (decision.Source == FeatureDecision.DECISION_SOURCE_FEATURE_TEST)
                 {
                     sourceInfo["experimentKey"] = decision.Experiment.Key;
@@ -691,6 +693,10 @@ private void SendImpressionEvent(Experiment experiment, Variation variation, str
             if (experiment.IsExperimentRunning)
             {
                 var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation.Id, userId, userAttributes, flagKey, flagType);
+                if (userEvent == null)
+                {
+                    return;
+                }
                 EventProcessor.Process(userEvent);
                 Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
 

From 1bf73ea031df3af23b257ee7fc44d4fd9aabe28d Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Fri, 25 Sep 2020 21:35:39 +0500
Subject: [PATCH 03/15] - Added unit tests of featureDecision, - Removed extra
 log, -  Added metadata

---
 .../OptimizelySDK.Net35.csproj                |   3 +
 .../OptimizelySDK.Net40.csproj                |   3 +
 .../OptimizelySDK.NetStandard16.csproj        |   4 +
 .../EventTests/EventEntitiesTest.cs           |   2 +-
 .../EventTests/EventFactoryTest.cs            | 208 ++++++++++++++++--
 .../EventTests/UserEventFactoryTest.cs        |   4 +-
 OptimizelySDK.Tests/OptimizelyTest.cs         |   6 -
 OptimizelySDK.Tests/ProjectConfigTest.cs      |   2 +
 OptimizelySDK/Event/Builder/EventBuilder.cs   |  12 +-
 OptimizelySDK/Event/Entity/Decision.cs        |   2 +-
 .../Event/Entity/DecisionMetadata.cs          |  23 +-
 OptimizelySDK/Event/EventFactory.cs           |   2 +-
 OptimizelySDK/Optimizely.cs                   |  38 ++--
 13 files changed, 254 insertions(+), 55 deletions(-)

diff --git a/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj b/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
index 6d97bc2d..e5730209 100644
--- a/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
+++ b/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
@@ -266,6 +266,9 @@
 	<Compile Include="..\OptimizelySDK\Event\Entity\Visitor.cs">
 	  <Link>Event\Entity\Visitor.cs</Link>
 	</Compile>
+	<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
+	  <Link>Event\Entity\DecisionMetadata.cs</Link>
+	</Compile>
 	<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
 	  <Link>Event\Entity\VisitorAttribute.cs</Link>
 	</Compile>
diff --git a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
index 83639901..5ce0521a 100644
--- a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
+++ b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
@@ -280,6 +280,9 @@
 	<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
 	  <Link>Event\Entity\VisitorAttribute.cs</Link>
 	</Compile>
+	<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
+	  <Link>Event\Entity\DecisionMetadata.cs</Link>
+	</Compile>
 	<Compile Include="..\OptimizelySDK\Event\EventFactory.cs">
 	  <Link>Event\EventFactory.cs</Link>
 	</Compile>
diff --git a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
index a38425a0..7d8e0fde 100644
--- a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
+++ b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
@@ -24,6 +24,7 @@
 		<Compile Include="..\OptimizelySDK\AudienceConditions\OrCondition.cs" />
 		<Compile Include="..\OptimizelySDK\Entity\Attribute.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Audience.cs" />
+        <Compile Include="..\OptimizelySDK\Entity\DecisionMetadata.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Entity.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Event.cs" />
         <Compile Include="..\OptimizelySDK\Entity\EventTags.cs" />
@@ -121,6 +122,9 @@
 		<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
 		  <Link>VisitorAttribute.cs</Link>
 		</Compile>
+		<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
+		  <Link>DecisionMetadata.cs</Link>
+		</Compile>
 		<Compile Include="..\OptimizelySDK\Event\Entity\Decision.cs">
 		  <Link>DecisionEvent.cs</Link>
 		</Compile>
diff --git a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
index 15c4e28d..6463974c 100644
--- a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
@@ -104,7 +104,7 @@ public void TestImpressionEventEqualsSerializedPayload()
                 .WithTimeStamp(timeStamp)
                 .WithEventTags(null)
                 .Build();
-            
+            var metadata = new DecisionMetadata("experiment", "7716830082");
             var decision = new Decision("7719770039", "7716830082", "77210100090");
             var snapshot = new Snapshot(events: new SnapshotEvent[] { snapshotEvent }, decisions: new Decision[] { decision });
 
diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index 338d36c1..5534a451 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -27,6 +27,24 @@ public void Setup()
             Config = DatafileProjectConfig.Create(TestData.Datafile, logger, new ErrorHandler.NoOpErrorHandler());
         }
 
+        [Test]
+        public void TestCreateImpressionEventReturnsNullWhenSendFlagDecisionsIsFalseAndIsRollout()
+        {
+            Config.SendFlagDecisions = false;
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(
+                Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, null, "test_feature", "rollout");
+            Assert.IsNull(impressionEvent);
+        }
+
+        [Test]
+        public void TestCreateImpressionEventReturnsNullWhenSendFlagDecisionsIsFalseAndVariationIsNull()
+        {
+            Config.SendFlagDecisions = false;
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(
+                Config, Config.GetExperimentFromKey("test_experiment"), variation: null, TestUserId, null, "test_experiment", "experiment");
+            Assert.IsNull(impressionEvent);
+        }
+
         [Test]
         public void TestCreateImpressionEventNoAttributes()
         {
@@ -45,7 +63,12 @@ public void TestCreateImpressionEventNoAttributes()
                                                     new Dictionary<string, object> {
                                                         { "campaign_id", "7719770039" },
                                                         { "experiment_id", "7716830082" },
-                                                        { "variation_id", "7722370027" }
+                                                        { "variation_id", "7722370027" },
+                                                        { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        } }
                                                     }
                                                 }
                                             },
@@ -93,7 +116,7 @@ public void TestCreateImpressionEventNoAttributes()
                     { "Content-Type", "application/json" }
                 });
             var impressionEvent = UserEventFactory.CreateImpressionEvent(
-                Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, null);
+                Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, null, "test_experiment", "experiment");
 
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
 
@@ -120,7 +143,13 @@ public void TestCreateImpressionEventWithAttributes()
                                                 new Dictionary<string, object> {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -183,7 +212,7 @@ public void TestCreateImpressionEventWithAttributes()
                 { "company", "Optimizely" }
             };
             //
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), variationId, TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), variationId, TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
 
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
@@ -213,7 +242,13 @@ public void TestCreateImpressionEventWithTypedAttributes()
                                                 {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -298,7 +333,7 @@ public void TestCreateImpressionEventWithTypedAttributes()
                 {"integer_key", 15 },
                 {"double_key", 3.14 }
             };
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
 
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
@@ -328,7 +363,13 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayload()
                                                 {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -413,7 +454,128 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayload()
                 { "nan", double.NaN },
                 { "invalid_num_value", Math.Pow(2, 53) + 2 },
             };
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
+            var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
+
+            TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
+
+            Assert.IsTrue(TestData.CompareObjects(expectedLogEvent, logEvent));
+        }
+
+        [Test]
+        public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout()
+        {
+            var guid = Guid.NewGuid();
+            var timeStamp = TestData.SecondsSince1970();
+
+            var payloadParams = new Dictionary<string, object>
+            {
+                { "visitors", new object[]
+                    {
+                        new Dictionary<string, object>()
+                        {
+                            { "snapshots", new object[]
+                                {
+                                    new Dictionary<string, object>
+                                    {
+                                        { "decisions", new object[]
+                                            {
+                                                new Dictionary<string, object>
+                                                {
+                                                    {"campaign_id", null },
+                                                    {"experiment_id", null },
+                                                    {"variation_id", null },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "rollout" },
+                                                            { "FlagKey", "test_feature" },
+                                                            { "VariationKey", null }
+                                                        } 
+                                                    }
+                                                }
+                                            }
+                                        },
+                                        { "events", new object[]
+                                            {
+                                                new Dictionary<string, object>
+                                                {
+                                                    {"entity_id", null },
+                                                    {"timestamp", timeStamp },
+                                                    {"uuid", guid },
+                                                    {"key", "campaign_activated" }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            },
+                            {"attributes", new object[]
+                                {
+                                    new Dictionary<string, object>
+                                    {
+                                        {"entity_id", "7723280020" },
+                                        {"key", "device_type" },
+                                        {"type", "custom" },
+                                        {"value", "iPhone"}
+                                    },
+                                    new Dictionary<string, object>
+                                    {
+                                        {"entity_id", "323434545" },
+                                        {"key", "boolean_key" },
+                                        {"type", "custom" },
+                                        {"value", true}
+                                    },
+                                    new Dictionary<string, object>
+                                    {
+                                        {"entity_id", "808797686" },
+                                        {"key", "double_key" },
+                                        {"type", "custom" },
+                                        {"value", 3.14}
+                                    },
+                                    new Dictionary<string, object>
+                                    {
+                                        {"entity_id", ControlAttributes.BOT_FILTERING_ATTRIBUTE},
+                                        {"key", ControlAttributes.BOT_FILTERING_ATTRIBUTE},
+                                        {"type", "custom" },
+                                        {"value", true }
+                                    }
+                                }
+                            },
+                            { "visitor_id", TestUserId }
+                        }
+                    }
+                },
+                {"project_id", "7720880029" },
+                {"account_id", "1592310167" },
+                {"enrich_decisions", true} ,
+                {"client_name", "csharp-sdk" },
+                {"client_version", Optimizely.SDK_VERSION },
+                {"revision", "15" },
+                {"anonymize_ip", false}
+            };
+
+            var expectedLogEvent = new LogEvent("https://logx.optimizely.com/v1/events",
+                payloadParams,
+                "POST",
+                new Dictionary<string, string>
+                {
+                    { "Content-Type", "application/json" }
+                });
+
+            var userAttributes = new UserAttributes
+            {
+                { "device_type", "iPhone" },
+                { "boolean_key", true },
+                { "double_key", 3.14 },
+                { "", "Android" },
+                { "null", null },
+                { "objects", new object() },
+                { "arrays", new string[] { "a", "b", "c" } },
+                { "negative_infinity", double.NegativeInfinity },
+                { "positive_infinity", double.PositiveInfinity },
+                { "nan", double.NaN },
+                { "invalid_num_value", Math.Pow(2, 53) + 2 },
+            };
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, null, variation: null, TestUserId, userAttributes, "test_feature", "rollout");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
             
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
@@ -1333,7 +1495,13 @@ public void TestCreateImpressionEventWithBucketingIDAttribute()
                                                 {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -1403,7 +1571,7 @@ public void TestCreateImpressionEventWithBucketingIDAttribute()
                 { "company", "Optimizely" },
                 {ControlAttributes.BUCKETING_ID_ATTRIBUTE, "variation" }
             };
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, Config.GetExperimentFromKey("test_experiment"), "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
 
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
@@ -1433,7 +1601,13 @@ public void TestCreateImpressionEventWhenBotFilteringIsProvidedInDatafile()
                                                 {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -1499,7 +1673,7 @@ public void TestCreateImpressionEventWhenBotFilteringIsProvidedInDatafile()
             botFilteringEnabledConfig.BotFiltering = true;
             var experiment = botFilteringEnabledConfig.GetExperimentFromKey("test_experiment");
 
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringEnabledConfig, experiment, "7722370027", TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringEnabledConfig, experiment, "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
             
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
@@ -1529,7 +1703,13 @@ public void TestCreateImpressionEventWhenBotFilteringIsNotProvidedInDatafile()
                                                 {
                                                     {"campaign_id", "7719770039" },
                                                     {"experiment_id", "7716830082" },
-                                                    {"variation_id", "7722370027" }
+                                                    {"variation_id", "7722370027" },
+                                                    { "metadata", new Dictionary<string, object> {
+                                                            { "FlagType", "experiment" },
+                                                            { "FlagKey", "test_experiment" },
+                                                            { "VariationKey", "control" }
+                                                        }
+                                                    }
                                                 }
                                             }
                                         },
@@ -1588,7 +1768,7 @@ public void TestCreateImpressionEventWhenBotFilteringIsNotProvidedInDatafile()
             botFilteringDisabledConfig.BotFiltering = null;
             var experiment = botFilteringDisabledConfig.GetExperimentFromKey("test_experiment");
 
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringDisabledConfig, experiment, "7722370027", TestUserId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringDisabledConfig, experiment, "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
             
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
diff --git a/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
index 47300ca7..ce9bceda 100644
--- a/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
@@ -32,7 +32,7 @@ public void ImpressionEventTest()
             var variation = Config.GetVariationFromId(experiment.Key, "77210100090");
             var userId = TestUserId;
 
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, null);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, null, "test_experiment", "experiment");
 
             Assert.AreEqual(Config.ProjectId, impressionEvent.Context.ProjectId);
             Assert.AreEqual(Config.Revision, impressionEvent.Context.Revision);
@@ -58,7 +58,7 @@ public void ImpressionEventTestWithAttributes()
                 { "company", "Optimizely" }
             };
 
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, userAttributes);
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, userAttributes, "test_experiment", "experiment");
 
             Assert.AreEqual(Config.ProjectId, impressionEvent.Context.ProjectId);
             Assert.AreEqual(Config.Revision, impressionEvent.Context.Revision);
diff --git a/OptimizelySDK.Tests/OptimizelyTest.cs b/OptimizelySDK.Tests/OptimizelyTest.cs
index 26cb54e9..59afe18a 100644
--- a/OptimizelySDK.Tests/OptimizelyTest.cs
+++ b/OptimizelySDK.Tests/OptimizelyTest.cs
@@ -395,7 +395,6 @@ public void TestActivateNoAudienceNoAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in experiment [group_experiment_1] of group [7722400015]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);            
 
             Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
         }
@@ -430,7 +429,6 @@ public void TestActivateWithAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."));
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);                        
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -450,7 +448,6 @@ public void TestActivateWithNullAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"test_user\" is not in the forced variation map."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -490,7 +487,6 @@ public void TestActivateWithTypedAttributes()
 
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);            
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -683,7 +679,6 @@ public void TestInvalidDispatchImpressionEvent()
 
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
             
             // Need to see how error handler can be verified.
             LoggerMock.Verify(l => l.Log(LogLevel.ERROR, It.IsAny<string>()), Times.Once);
@@ -1079,7 +1074,6 @@ public void TestActivateNoAudienceNoAttributesAfterSetForcedVariation()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."), Times.Once);
-            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);            
 
             Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
         }
diff --git a/OptimizelySDK.Tests/ProjectConfigTest.cs b/OptimizelySDK.Tests/ProjectConfigTest.cs
index 63121246..35f9b6d8 100644
--- a/OptimizelySDK.Tests/ProjectConfigTest.cs
+++ b/OptimizelySDK.Tests/ProjectConfigTest.cs
@@ -63,6 +63,8 @@ public void TestInit()
             Assert.AreEqual("7720880029", Config.ProjectId);
             // Check Revision 
             Assert.AreEqual("15", Config.Revision);
+            // Check SendFlagDecision
+            Assert.IsTrue(Config.SendFlagDecisions);
 
             // Check Group ID Map
             var expectedGroupId = CreateDictionary("7722400015", Config.GetGroup("7722400015"));
diff --git a/OptimizelySDK/Event/Builder/EventBuilder.cs b/OptimizelySDK/Event/Builder/EventBuilder.cs
index 8f24390b..2d31baf3 100644
--- a/OptimizelySDK/Event/Builder/EventBuilder.cs
+++ b/OptimizelySDK/Event/Builder/EventBuilder.cs
@@ -123,16 +123,11 @@ private Dictionary<string, object> GetCommonParams(ProjectConfig config, string
         }
 
         private Dictionary<string, object> GetImpressionParams(Experiment experiment,
-            string variationId,
-            string flagKey,
-            string flagType)
+            string variationId)
         {
 
             var impressionEvent = new Dictionary<string, object>();
             
-            string variationKey = variationId != null ? experiment.VariationIdToVariationMap[variationId].Key : null;
-            var metadata = new DecisionMetadata(flagKey, flagType, variationKey);
-
             var decisions = new object[]
             {
                     new Dictionary<string, object>
@@ -140,7 +135,6 @@ private Dictionary<string, object> GetImpressionParams(Experiment experiment,
                         { Params.CAMPAIGN_ID,   experiment.LayerId },
                         { Params.EXPERIMENT_ID, experiment.Id },
                         { Params.VARIATION_ID,  variationId },
-                        { Params.METADATA,  metadata }
                     }
             };
 
@@ -224,11 +218,11 @@ private Dictionary<string, object> GetImpressionOrConversionParamsWithCommonPara
         /// <param name="userAttributes">associative array of attributes for the user</param>
         /// <returns>LogEvent object to be sent to dispatcher</returns>
         public virtual LogEvent CreateImpressionEvent(ProjectConfig config, Experiment experiment, string variationId,
-            string userId, UserAttributes userAttributes, string flagKey, string flagType)
+            string userId, UserAttributes userAttributes)
         {
 
             var commonParams = GetCommonParams(config, userId, userAttributes ?? new UserAttributes());
-            var impressionOnlyParams = GetImpressionParams(experiment, variationId, flagKey, flagType);
+            var impressionOnlyParams = GetImpressionParams(experiment, variationId);
 
             var impressionParams = GetImpressionOrConversionParamsWithCommonParams(commonParams, new object[] { impressionOnlyParams });
 
diff --git a/OptimizelySDK/Event/Entity/Decision.cs b/OptimizelySDK/Event/Entity/Decision.cs
index dc2d5fa8..32b30212 100644
--- a/OptimizelySDK/Event/Entity/Decision.cs
+++ b/OptimizelySDK/Event/Entity/Decision.cs
@@ -29,7 +29,7 @@ public class Decision
         public string VariationId { get; private set; }
         public Decision() {}
 
-        public Decision(string campaignId, string experimentId, string variationId, DecisionMetadata metadata)
+        public Decision(string campaignId, string experimentId, string variationId, DecisionMetadata metadata = null)
         {
             CampaignId = campaignId;
             ExperimentId = experimentId;
diff --git a/OptimizelySDK/Event/Entity/DecisionMetadata.cs b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
index 8bc6318b..9ce28943 100644
--- a/OptimizelySDK/Event/Entity/DecisionMetadata.cs
+++ b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
@@ -1,4 +1,22 @@
-
+/**
+ *
+ *    Copyright 2020, Optimizely and contributors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+using Newtonsoft.Json;
+
 namespace OptimizelySDK.Event.Entity
 {
     /// <summary>
@@ -6,8 +24,11 @@ namespace OptimizelySDK.Event.Entity
     /// </summary>
     public class DecisionMetadata
     {
+        [JsonProperty("flag_type")]
         public string FlagType { get; private set; }
+        [JsonProperty("flag_key")]
         public string FlagKey { get; private set; }
+        [JsonProperty("variation_key")]
         public string VariationKey { get; private set; }
 
         public DecisionMetadata(string flagKey, string flagType, string variationKey = null) 
diff --git a/OptimizelySDK/Event/EventFactory.cs b/OptimizelySDK/Event/EventFactory.cs
index f91e2fcf..211a801b 100644
--- a/OptimizelySDK/Event/EventFactory.cs
+++ b/OptimizelySDK/Event/EventFactory.cs
@@ -120,7 +120,7 @@ private static Visitor CreateVisitor(ImpressionEvent impressionEvent) {
 
             SnapshotEvent snapshotEvent = new SnapshotEvent.Builder()
                 .WithUUID(impressionEvent.UUID)
-                .WithEntityId(impressionEvent.Experiment.LayerId)
+                .WithEntityId(impressionEvent.Experiment?.LayerId)
                 .WithKey(ACTIVATE_EVENT_KEY)
                 .WithTimeStamp(impressionEvent.Timestamp)
                 .Build();
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 5380198a..9b2b868a 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -690,29 +690,27 @@ private void SendImpressionEvent(Experiment experiment, Variation variation, str
                                          UserAttributes userAttributes, ProjectConfig config,
                                          string flagKey, string flagType)
         {
-            if (experiment.IsExperimentRunning)
+            if (experiment != null && !experiment.IsExperimentRunning)
             {
-                var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation.Id, userId, userAttributes, flagKey, flagType);
-                if (userEvent == null)
-                {
-                    return;
-                }
-                EventProcessor.Process(userEvent);
-                Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
-
-                // Kept For backwards compatibility.
-                // This notification is deprecated and the new DecisionNotifications
-                // are sent via their respective method calls.
-                if (NotificationCenter.GetNotificationCount(NotificationCenter.NotificationType.Activate) > 0)
-                {
-                    var impressionEvent = EventFactory.CreateLogEvent(userEvent, Logger);
-                    NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Activate, experiment, userId,
-                    userAttributes, variation, impressionEvent);
-                }
+                Logger.Log(LogLevel.ERROR, @"Experiment has ""Launched"" status so not dispatching event during activation.");
             }
-            else
+            
+            var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation, userId, userAttributes, flagKey, flagType);
+            if (userEvent == null)
             {
-                Logger.Log(LogLevel.ERROR, @"Experiment has ""Launched"" status so not dispatching event during activation.");
+                return;
+            }
+            EventProcessor.Process(userEvent);
+            //Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
+
+            // Kept For backwards compatibility.
+            // This notification is deprecated and the new DecisionNotifications
+            // are sent via their respective method calls.
+            if (NotificationCenter.GetNotificationCount(NotificationCenter.NotificationType.Activate) > 0)
+            {
+                var impressionEvent = EventFactory.CreateLogEvent(userEvent, Logger);
+                NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Activate, experiment, userId,
+                userAttributes, variation, impressionEvent);
             }
         }
 

From d82558dff8b8e79906f2bccdfbff8fc1d3165bf3 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Fri, 25 Sep 2020 21:48:08 +0500
Subject: [PATCH 04/15] Updated headers

---
 .../EventTests/EventEntitiesTest.cs           | 20 ++++++++++++++++++-
 .../EventTests/EventFactoryTest.cs            | 19 +++++++++++++++++-
 .../EventTests/UserEventFactoryTest.cs        | 19 +++++++++++++++++-
 OptimizelySDK.Tests/TestData.json             |  2 +-
 OptimizelySDK/Event/Builder/EventBuilder.cs   |  2 +-
 OptimizelySDK/Event/Builder/Params.cs         |  2 +-
 OptimizelySDK/Event/Entity/Decision.cs        |  2 +-
 OptimizelySDK/Event/Entity/ImpressionEvent.cs |  2 +-
 OptimizelySDK/Event/EventFactory.cs           |  2 +-
 OptimizelySDK/Event/UserEventFactory.cs       | 18 ++++++++++++++++-
 10 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
index 6463974c..6ceca9b1 100644
--- a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
@@ -1,4 +1,22 @@
-using System;
+/**
+ *
+ *    Copyright 2019-2020, Optimizely and contributors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+using System;
 using System.Collections.Generic;
 using Newtonsoft.Json.Linq;
 using Newtonsoft.Json.Schema;
diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index 5534a451..9c5ae4e2 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -1,4 +1,21 @@
-using System;
+/**
+ *
+ *    Copyright 2019-2020, Optimizely and contributors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+using System;
 using System.Collections.Generic;
 using Moq;
 using NUnit.Framework;
diff --git a/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
index ce9bceda..80acd1a6 100644
--- a/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
@@ -1,4 +1,21 @@
-using Moq;
+/**
+ *
+ *    Copyright 2019-2020, Optimizely and contributors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+using Moq;
 using NUnit.Framework;
 using OptimizelySDK.Config;
 using OptimizelySDK.Entity;
diff --git a/OptimizelySDK.Tests/TestData.json b/OptimizelySDK.Tests/TestData.json
index b8b72fbc..9724b0b4 100644
--- a/OptimizelySDK.Tests/TestData.json
+++ b/OptimizelySDK.Tests/TestData.json
@@ -565,7 +565,7 @@
 					"key": "integer_variable",
 					"type": "integer",
 					"defaultValue": "7"
-				}
+				}					
 			]
 		},
 		{
diff --git a/OptimizelySDK/Event/Builder/EventBuilder.cs b/OptimizelySDK/Event/Builder/EventBuilder.cs
index 2d31baf3..8b574983 100644
--- a/OptimizelySDK/Event/Builder/EventBuilder.cs
+++ b/OptimizelySDK/Event/Builder/EventBuilder.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2017-2019, Optimizely
+ * Copyright 2017-2020, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/OptimizelySDK/Event/Builder/Params.cs b/OptimizelySDK/Event/Builder/Params.cs
index 14d0a994..f3b47688 100644
--- a/OptimizelySDK/Event/Builder/Params.cs
+++ b/OptimizelySDK/Event/Builder/Params.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2017, 2019, Optimizely
+ * Copyright 2017, 2019-2020, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/OptimizelySDK/Event/Entity/Decision.cs b/OptimizelySDK/Event/Entity/Decision.cs
index 32b30212..08657a33 100644
--- a/OptimizelySDK/Event/Entity/Decision.cs
+++ b/OptimizelySDK/Event/Entity/Decision.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2019, Optimizely
+ * Copyright 2019-2020, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/OptimizelySDK/Event/Entity/ImpressionEvent.cs b/OptimizelySDK/Event/Entity/ImpressionEvent.cs
index d8226f97..5d525b2d 100644
--- a/OptimizelySDK/Event/Entity/ImpressionEvent.cs
+++ b/OptimizelySDK/Event/Entity/ImpressionEvent.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2019, Optimizely
+ * Copyright 2019-2020, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/OptimizelySDK/Event/EventFactory.cs b/OptimizelySDK/Event/EventFactory.cs
index 211a801b..63482a8b 100644
--- a/OptimizelySDK/Event/EventFactory.cs
+++ b/OptimizelySDK/Event/EventFactory.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2019, Optimizely
+ * Copyright 2019-2020, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/OptimizelySDK/Event/UserEventFactory.cs b/OptimizelySDK/Event/UserEventFactory.cs
index 22296c1c..3593b0d8 100644
--- a/OptimizelySDK/Event/UserEventFactory.cs
+++ b/OptimizelySDK/Event/UserEventFactory.cs
@@ -1,4 +1,20 @@
-using OptimizelySDK.Bucketing;
+/**
+ *
+ *    Copyright 2019-2020, Optimizely and contributors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
 using OptimizelySDK.Entity;
 using OptimizelySDK.Event.Entity;
 

From 8183bcc7bc7d0f17b91f22f17fbe44e0bd234d9e Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Mon, 28 Sep 2020 03:06:36 +0500
Subject: [PATCH 05/15] passing null variation as variable

---
 OptimizelySDK.Tests/EventTests/EventFactoryTest.cs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index 9c5ae4e2..e2f506ce 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -57,8 +57,9 @@ public void TestCreateImpressionEventReturnsNullWhenSendFlagDecisionsIsFalseAndI
         public void TestCreateImpressionEventReturnsNullWhenSendFlagDecisionsIsFalseAndVariationIsNull()
         {
             Config.SendFlagDecisions = false;
+            Variation variation = null;
             var impressionEvent = UserEventFactory.CreateImpressionEvent(
-                Config, Config.GetExperimentFromKey("test_experiment"), variation: null, TestUserId, null, "test_experiment", "experiment");
+                Config, Config.GetExperimentFromKey("test_experiment"), variation, TestUserId, null, "test_experiment", "experiment");
             Assert.IsNull(impressionEvent);
         }
 
@@ -592,7 +593,9 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout(
                 { "nan", double.NaN },
                 { "invalid_num_value", Math.Pow(2, 53) + 2 },
             };
-            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, null, variation: null, TestUserId, userAttributes, "test_feature", "rollout");
+            Variation variation = null;
+
+            var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, null, variation, TestUserId, userAttributes, "test_feature", "rollout");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
             
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));

From 94a582185028ce569da1c85a2705662a1a9aa656 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Mon, 28 Sep 2020 03:34:12 +0500
Subject: [PATCH 06/15] unit test fix converted to snakecase

---
 .../EventTests/EventFactoryTest.cs            | 67 ++++++++++---------
 1 file changed, 34 insertions(+), 33 deletions(-)

diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index e2f506ce..7002fd51 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -82,10 +82,11 @@ public void TestCreateImpressionEventNoAttributes()
                                                         { "campaign_id", "7719770039" },
                                                         { "experiment_id", "7716830082" },
                                                         { "variation_id", "7722370027" },
-                                                        { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                        { "metadata",
+                                                            new Dictionary<string, object> {
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         } }
                                                     }
                                                 }
@@ -163,9 +164,9 @@ public void TestCreateImpressionEventWithAttributes()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -262,9 +263,9 @@ public void TestCreateImpressionEventWithTypedAttributes()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -383,9 +384,9 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayload()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -504,10 +505,10 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout(
                                                     {"experiment_id", null },
                                                     {"variation_id", null },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "rollout" },
-                                                            { "FlagKey", "test_feature" },
-                                                            { "VariationKey", null }
-                                                        } 
+                                                            { "flag_type", "rollout" },
+                                                            { "flag_key", "test_feature" },
+                                                            { "variation_key", null }
+                                                        }
                                                     }
                                                 }
                                             }
@@ -597,7 +598,7 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout(
 
             var impressionEvent = UserEventFactory.CreateImpressionEvent(Config, null, variation, TestUserId, userAttributes, "test_feature", "rollout");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
-            
+
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
 
             Assert.IsTrue(TestData.CompareObjects(expectedLogEvent, logEvent));
@@ -1517,9 +1518,9 @@ public void TestCreateImpressionEventWithBucketingIDAttribute()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -1623,9 +1624,9 @@ public void TestCreateImpressionEventWhenBotFilteringIsProvidedInDatafile()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -1695,7 +1696,7 @@ public void TestCreateImpressionEventWhenBotFilteringIsProvidedInDatafile()
 
             var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringEnabledConfig, experiment, "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
-            
+
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
 
             Assert.IsTrue(TestData.CompareObjects(expectedLogEvent, logEvent));
@@ -1725,9 +1726,9 @@ public void TestCreateImpressionEventWhenBotFilteringIsNotProvidedInDatafile()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "FlagType", "experiment" },
-                                                            { "FlagKey", "test_experiment" },
-                                                            { "VariationKey", "control" }
+                                                            { "flag_type", "experiment" },
+                                                            { "flag_key", "test_experiment" },
+                                                            { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -1790,7 +1791,7 @@ public void TestCreateImpressionEventWhenBotFilteringIsNotProvidedInDatafile()
 
             var impressionEvent = UserEventFactory.CreateImpressionEvent(botFilteringDisabledConfig, experiment, "7722370027", TestUserId, userAttributes, "test_experiment", "experiment");
             var logEvent = EventFactory.CreateLogEvent(impressionEvent, Logger);
-            
+
             TestData.ChangeGUIDAndTimeStamp(expectedLogEvent.Params, impressionEvent.Timestamp, Guid.Parse(impressionEvent.UUID));
 
             Assert.IsTrue(TestData.CompareObjects(expectedLogEvent, logEvent));
@@ -1880,7 +1881,7 @@ public void TestCreateConversionEventWhenBotFilteringIsProvidedInDatafile()
 
             var conversionEvent = UserEventFactory.CreateConversionEvent(botFilteringEnabledConfig, "purchase", TestUserId, userAttributes, null);
             var logEvent = EventFactory.CreateLogEvent(conversionEvent, Logger);
-            
+
             TestData.ChangeGUIDAndTimeStamp(expectedEvent.Params, conversionEvent.Timestamp, Guid.Parse(conversionEvent.UUID));
 
             Assert.IsTrue(TestData.CompareObjects(expectedEvent, logEvent));
@@ -1962,7 +1963,7 @@ public void TestCreateConversionEventWhenBotFilteringIsNotProvidedInDatafile()
             botFilteringDisabledConfig.BotFiltering = null;
 
             var conversionEvent = UserEventFactory.CreateConversionEvent(botFilteringDisabledConfig, "purchase", TestUserId, userAttributes, null);
-            var logEvent = EventFactory.CreateLogEvent(conversionEvent, Logger);            
+            var logEvent = EventFactory.CreateLogEvent(conversionEvent, Logger);
 
             TestData.ChangeGUIDAndTimeStamp(expectedEvent.Params, conversionEvent.Timestamp, Guid.Parse(conversionEvent.UUID));
 
@@ -1986,7 +1987,7 @@ public void TestCreateConversionEventWhenEventUsedInMultipleExp()
                     "111130", new Variation{Id="111131", Key="variation"}
                 }
             };
-            
+
             var payloadParams = new Dictionary<string, object>
                 {
                 {"client_version", Optimizely.SDK_VERSION},
@@ -2187,7 +2188,7 @@ public void TestCreateConversionEventRemovesInvalidAttributesFromPayload()
                 {"7716830082", new Variation{Id="7722370027", Key="control"} }
             };
             var conversionEvent = UserEventFactory.CreateConversionEvent(Config, "purchase", TestUserId, userAttributes, null);
-            var logEvent = EventFactory.CreateLogEvent(conversionEvent, Logger);            
+            var logEvent = EventFactory.CreateLogEvent(conversionEvent, Logger);
 
             TestData.ChangeGUIDAndTimeStamp(expectedEvent.Params, conversionEvent.Timestamp, Guid.Parse(conversionEvent.UUID));
             Assert.IsTrue(TestData.CompareObjects(expectedEvent, logEvent));

From 315aa55059f638a3d058213115e9fbdf61d4bc74 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Mon, 28 Sep 2020 20:06:10 +0500
Subject: [PATCH 07/15] removed invalid path decision metadata from cs16proj

---
 OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj | 1 -
 1 file changed, 1 deletion(-)

diff --git a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
index 7d8e0fde..868c1d8c 100644
--- a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
+++ b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
@@ -24,7 +24,6 @@
 		<Compile Include="..\OptimizelySDK\AudienceConditions\OrCondition.cs" />
 		<Compile Include="..\OptimizelySDK\Entity\Attribute.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Audience.cs" />
-        <Compile Include="..\OptimizelySDK\Entity\DecisionMetadata.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Entity.cs" />
         <Compile Include="..\OptimizelySDK\Entity\Event.cs" />
         <Compile Include="..\OptimizelySDK\Entity\EventTags.cs" />

From 823bdb26584ab68f0ed111640c287306ac080fbb Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Tue, 29 Sep 2020 13:23:30 +0500
Subject: [PATCH 08/15] Added test to check sendFlagDecision is false in case
 if it's missing in datafile.

---
 OptimizelySDK.Tests/ProjectConfigTest.cs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/OptimizelySDK.Tests/ProjectConfigTest.cs b/OptimizelySDK.Tests/ProjectConfigTest.cs
index 35f9b6d8..98d38dec 100644
--- a/OptimizelySDK.Tests/ProjectConfigTest.cs
+++ b/OptimizelySDK.Tests/ProjectConfigTest.cs
@@ -417,6 +417,14 @@ public void TestInit()
             Assert.IsTrue(TestData.CompareObjects(expectedRolloutIdMap, Config.RolloutIdMap));
         }
 
+
+        [Test]
+        public void TestIfSendFlagDecisionKeyIsMissingItShouldReturnFalse()
+        {
+            var tempConfig = DatafileProjectConfig.Create(TestData.SimpleABExperimentsDatafile, LoggerMock.Object, ErrorHandlerMock.Object);
+            Assert.IsFalse(tempConfig.SendFlagDecisions);
+        }
+
         [Test]
         public void TestGetAccountId()
         {

From 719eb73740c09fb000d31183d5314f30f5330015 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Wed, 30 Sep 2020 13:10:05 +0500
Subject: [PATCH 09/15] reverted changes made in this file

---
 OptimizelySDK/Event/Builder/EventBuilder.cs | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/OptimizelySDK/Event/Builder/EventBuilder.cs b/OptimizelySDK/Event/Builder/EventBuilder.cs
index 8b574983..0f0b41b8 100644
--- a/OptimizelySDK/Event/Builder/EventBuilder.cs
+++ b/OptimizelySDK/Event/Builder/EventBuilder.cs
@@ -1,5 +1,5 @@
 /* 
- * Copyright 2017-2020, Optimizely
+ * Copyright 2017-2019, Optimizely
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
  */
 using OptimizelySDK.Bucketing;
 using OptimizelySDK.Entity;
-using OptimizelySDK.Event.Entity;
 using OptimizelySDK.Logger;
 using OptimizelySDK.Utils;
 using System;
@@ -122,19 +121,18 @@ private Dictionary<string, object> GetCommonParams(ProjectConfig config, string
             return comonParams;
         }
 
-        private Dictionary<string, object> GetImpressionParams(Experiment experiment,
-            string variationId)
+        private Dictionary<string, object> GetImpressionParams(Experiment experiment, string variationId)
         {
 
             var impressionEvent = new Dictionary<string, object>();
-            
+
             var decisions = new object[]
             {
                     new Dictionary<string, object>
                     {
                         { Params.CAMPAIGN_ID,   experiment.LayerId },
                         { Params.EXPERIMENT_ID, experiment.Id },
-                        { Params.VARIATION_ID,  variationId },
+                        { Params.VARIATION_ID,  variationId }
                     }
             };
 

From 724a86bccc82bbaf1a82b88799677e3ab01ca2f3 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Fri, 2 Oct 2020 04:14:28 +0500
Subject: [PATCH 10/15] Reverted removal of  Activating user  log and added
 condition to do not log if experiment is null

---
 OptimizelySDK.Tests/OptimizelyTest.cs | 6 ++++++
 OptimizelySDK/Optimizely.cs           | 5 ++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/OptimizelySDK.Tests/OptimizelyTest.cs b/OptimizelySDK.Tests/OptimizelyTest.cs
index 59afe18a..4b40e0de 100644
--- a/OptimizelySDK.Tests/OptimizelyTest.cs
+++ b/OptimizelySDK.Tests/OptimizelyTest.cs
@@ -395,6 +395,7 @@ public void TestActivateNoAudienceNoAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in experiment [group_experiment_1] of group [7722400015]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
         }
@@ -429,6 +430,7 @@ public void TestActivateWithAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."));
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -448,6 +450,7 @@ public void TestActivateWithNullAttributes()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "User \"test_user\" is not in the forced variation map."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -487,6 +490,7 @@ public void TestActivateWithTypedAttributes()
 
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
         }
@@ -680,6 +684,7 @@ public void TestInvalidDispatchImpressionEvent()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
             
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
             // Need to see how error handler can be verified.
             LoggerMock.Verify(l => l.Log(LogLevel.ERROR, It.IsAny<string>()), Times.Once);
 
@@ -1074,6 +1079,7 @@ public void TestActivateNoAudienceNoAttributesAfterSetForcedVariation()
             LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
             LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."), Times.Once);
+            LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);
 
             Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
         }
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 9b2b868a..3c5c3c3a 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -701,8 +701,11 @@ private void SendImpressionEvent(Experiment experiment, Variation variation, str
                 return;
             }
             EventProcessor.Process(userEvent);
-            //Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
 
+            if (experiment != null)
+            { 
+                Logger.Log(LogLevel.INFO, $"Activating user {userId} in experiment {experiment.Key}.");
+            }
             // Kept For backwards compatibility.
             // This notification is deprecated and the new DecisionNotifications
             // are sent via their respective method calls.

From 2aa8e8dc27ddb68de67906bbb422b827e129b279 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Mon, 12 Oct 2020 18:41:08 +0500
Subject: [PATCH 11/15] Renamed flag_type to rule_type and added rule_key

---
 .../EventTests/EventEntitiesTest.cs           |  2 +-
 .../EventTests/EventFactoryTest.cs            | 30 ++++++++++++-------
 .../Event/Entity/DecisionMetadata.cs          | 11 ++++---
 OptimizelySDK/Event/UserEventFactory.cs       | 24 ++++++++++-----
 OptimizelySDK/Optimizely.cs                   |  4 +--
 5 files changed, 45 insertions(+), 26 deletions(-)

diff --git a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
index 6ceca9b1..10e5c3e4 100644
--- a/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
@@ -122,7 +122,7 @@ public void TestImpressionEventEqualsSerializedPayload()
                 .WithTimeStamp(timeStamp)
                 .WithEventTags(null)
                 .Build();
-            var metadata = new DecisionMetadata("experiment", "7716830082");
+            var metadata = new DecisionMetadata("experiment", "experiment_key", "7716830082");
             var decision = new Decision("7719770039", "7716830082", "77210100090");
             var snapshot = new Snapshot(events: new SnapshotEvent[] { snapshotEvent }, decisions: new Decision[] { decision });
 
diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index 7002fd51..00308aa7 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -84,7 +84,8 @@ public void TestCreateImpressionEventNoAttributes()
                                                         { "variation_id", "7722370027" },
                                                         { "metadata",
                                                             new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         } }
@@ -164,7 +165,8 @@ public void TestCreateImpressionEventWithAttributes()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         }
@@ -263,7 +265,8 @@ public void TestCreateImpressionEventWithTypedAttributes()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         }
@@ -384,9 +387,10 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayload()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
-                                                            { "flag_key", "test_experiment" },
-                                                            { "variation_key", "control" }
+                                                           { "rule_type", "experiment" },
+                                                           { "rule_key", "test_experiment" },
+                                                           { "flag_key", "test_experiment" },
+                                                           { "variation_key", "control" }
                                                         }
                                                     }
                                                 }
@@ -505,9 +509,10 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout(
                                                     {"experiment_id", null },
                                                     {"variation_id", null },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "rollout" },
+                                                            { "rule_type", "" },
+                                                            { "rule_key", "" },
                                                             { "flag_key", "test_feature" },
-                                                            { "variation_key", null }
+                                                            { "variation_key", "" }
                                                         }
                                                     }
                                                 }
@@ -1518,7 +1523,8 @@ public void TestCreateImpressionEventWithBucketingIDAttribute()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         }
@@ -1624,7 +1630,8 @@ public void TestCreateImpressionEventWhenBotFilteringIsProvidedInDatafile()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         }
@@ -1726,7 +1733,8 @@ public void TestCreateImpressionEventWhenBotFilteringIsNotProvidedInDatafile()
                                                     {"experiment_id", "7716830082" },
                                                     {"variation_id", "7722370027" },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "flag_type", "experiment" },
+                                                            { "rule_type", "experiment" },
+                                                            { "rule_key", "test_experiment" },
                                                             { "flag_key", "test_experiment" },
                                                             { "variation_key", "control" }
                                                         }
diff --git a/OptimizelySDK/Event/Entity/DecisionMetadata.cs b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
index 9ce28943..551eb5a0 100644
--- a/OptimizelySDK/Event/Entity/DecisionMetadata.cs
+++ b/OptimizelySDK/Event/Entity/DecisionMetadata.cs
@@ -24,17 +24,20 @@ namespace OptimizelySDK.Event.Entity
     /// </summary>
     public class DecisionMetadata
     {
-        [JsonProperty("flag_type")]
-        public string FlagType { get; private set; }
         [JsonProperty("flag_key")]
         public string FlagKey { get; private set; }
+        [JsonProperty("rule_key")]
+        public string RuleKey { get; private set; }
+        [JsonProperty("rule_type")]
+        public string RuleType { get; private set; }
         [JsonProperty("variation_key")]
         public string VariationKey { get; private set; }
 
-        public DecisionMetadata(string flagKey, string flagType, string variationKey = null) 
+        public DecisionMetadata(string flagKey, string ruleKey, string ruleType, string variationKey = "") 
         {
-            FlagType = flagType;
             FlagKey = flagKey;
+            RuleKey = ruleKey;
+            RuleType = ruleType;
             VariationKey = variationKey;
         }
     }
diff --git a/OptimizelySDK/Event/UserEventFactory.cs b/OptimizelySDK/Event/UserEventFactory.cs
index 3593b0d8..6eac5b2e 100644
--- a/OptimizelySDK/Event/UserEventFactory.cs
+++ b/OptimizelySDK/Event/UserEventFactory.cs
@@ -40,10 +40,10 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             string userId,
                                                             UserAttributes userAttributes,
                                                             string flagKey,
-                                                            string flagType)
+                                                            string ruleType)
         {
             Variation variation = projectConfig.GetVariationFromId(activatedExperiment?.Key, variationId);
-            return CreateImpressionEvent(projectConfig, activatedExperiment, variation, userId, userAttributes, flagKey, flagType);
+            return CreateImpressionEvent(projectConfig, activatedExperiment, variation, userId, userAttributes, flagKey, ruleType);
         }
 
         /// <summary>
@@ -55,7 +55,7 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
         /// <param name="userId">The user Id</param>
         /// <param name="userAttributes">The user's attributes</param>
         /// <param name="flagKey">experiment key or feature key</param>
-        /// <param name="flagType">experiment or featureDecision source </param>
+        /// <param name="ruleType">experiment or featureDecision source </param>
         /// <returns>ImpressionEvent instance</returns>
         public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             Experiment activatedExperiment,
@@ -63,13 +63,13 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
                                                             string userId,
                                                             UserAttributes userAttributes,
                                                             string flagKey,
-                                                            string flagType)
+                                                            string ruleType)
         {
-            if ((flagType == FeatureDecision.DECISION_SOURCE_ROLLOUT || variation == null) && !projectConfig.SendFlagDecisions)
+            if ((ruleType == FeatureDecision.DECISION_SOURCE_ROLLOUT || variation == null) && !projectConfig.SendFlagDecisions)
             {
                 return null;
             }
-            
+
             var eventContext = new EventContext.Builder()
             .WithProjectId(projectConfig.ProjectId)
             .WithAccountId(projectConfig.AccountId)
@@ -77,8 +77,16 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
             .WithRevision(projectConfig.Revision)                
             .Build();
 
-            var variationKey = variation?.Key;
-            var metadata = new DecisionMetadata(flagKey, flagType, variationKey);
+            var variationKey = ""; 
+            var ruleKey = "";   
+            var finalRuleType = "";   
+            if (variation != null)
+            {
+                variationKey = variation.Key;
+                ruleKey = activatedExperiment.Key;
+                finalRuleType = ruleType;
+            }
+            var metadata = new DecisionMetadata(flagKey, ruleKey, finalRuleType, variationKey);
 
             return new ImpressionEvent.Builder()
                 .WithEventContext(eventContext)
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 3c5c3c3a..41c08312 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -688,14 +688,14 @@ public OptimizelyJSON GetFeatureVariableJSON(string featureKey, string variableK
         /// <param name="userAttributes">The user's attributes</param>
         private void SendImpressionEvent(Experiment experiment, Variation variation, string userId,
                                          UserAttributes userAttributes, ProjectConfig config,
-                                         string flagKey, string flagType)
+                                         string flagKey, string ruleType)
         {
             if (experiment != null && !experiment.IsExperimentRunning)
             {
                 Logger.Log(LogLevel.ERROR, @"Experiment has ""Launched"" status so not dispatching event during activation.");
             }
             
-            var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation, userId, userAttributes, flagKey, flagType);
+            var userEvent = UserEventFactory.CreateImpressionEvent(config, experiment, variation, userId, userAttributes, flagKey, ruleType);
             if (userEvent == null)
             {
                 return;

From e5a4ae84f282ca351478817cd503aadc0231c89a Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Tue, 13 Oct 2020 19:23:31 +0500
Subject: [PATCH 12/15] Added decision to rollout

---
 OptimizelySDK/Optimizely.cs | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 41c08312..216ff080 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -252,7 +252,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
                 return null;
             }
 
-            SendImpressionEvent(experiment, variation, userId, userAttributes, config, experimentKey, "experiment");
+            SendImpressionEvent(experiment, variation, userId, userAttributes, config, "experiment");
 
             return variation;
         }
@@ -471,8 +471,13 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
             var sourceInfo = new Dictionary<string, string>();
             var decision = DecisionService.GetVariationForFeature(featureFlag, userId, config, userAttributes);
             var variation = decision.Variation;
+            var decisionSource = FeatureDecision.DECISION_SOURCE_ROLLOUT;
+            if (decisionSource != null)
+            {
+                decisionSource = decision.Source;
+            }
 
-            SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config, featureKey, decision.Source);
+            SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config, featureKey, decisionSource);
 
             if (variation != null)
             {
@@ -686,6 +691,23 @@ public OptimizelyJSON GetFeatureVariableJSON(string featureKey, string variableK
         /// <param name="variation">The variation entity</param>
         /// <param name="userId">The user ID</param>
         /// <param name="userAttributes">The user's attributes</param>
+        /// <param name="ruleType">It can either be experiment in case impression event is sent from activate or it's feature-test or rollout</param>
+        private void SendImpressionEvent(Experiment experiment, Variation variation, string userId,
+                                         UserAttributes userAttributes, ProjectConfig config,
+                                         string ruleType)
+        {
+            SendImpressionEvent(experiment, variation, userId, userAttributes, config, "", ruleType);
+        }
+
+        /// <summary>
+        /// Sends impression event.
+        /// </summary>
+        /// <param name="experiment">The experiment</param>
+        /// <param name="variation">The variation entity</param>
+        /// <param name="userId">The user ID</param>
+        /// <param name="userAttributes">The user's attributes</param>
+        /// <param name="flagKey">It can either be experiment key in case if ruleType is experiment or it's feature key in case ruleType is feature-test or rollout</param>
+        /// <param name="ruleType">It can either be experiment in case impression event is sent from activate or it's feature-test or rollout</param>
         private void SendImpressionEvent(Experiment experiment, Variation variation, string userId,
                                          UserAttributes userAttributes, ProjectConfig config,
                                          string flagKey, string ruleType)

From e8db3cb002718ac13c928d1796e8ff2a465e4bf6 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Tue, 13 Oct 2020 19:34:59 +0500
Subject: [PATCH 13/15] when variation is null then ruleType will be rollout

---
 OptimizelySDK/Event/UserEventFactory.cs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/OptimizelySDK/Event/UserEventFactory.cs b/OptimizelySDK/Event/UserEventFactory.cs
index 6eac5b2e..ff78d495 100644
--- a/OptimizelySDK/Event/UserEventFactory.cs
+++ b/OptimizelySDK/Event/UserEventFactory.cs
@@ -79,14 +79,12 @@ public static ImpressionEvent CreateImpressionEvent(ProjectConfig projectConfig,
 
             var variationKey = ""; 
             var ruleKey = "";   
-            var finalRuleType = "";   
             if (variation != null)
             {
                 variationKey = variation.Key;
                 ruleKey = activatedExperiment.Key;
-                finalRuleType = ruleType;
             }
-            var metadata = new DecisionMetadata(flagKey, ruleKey, finalRuleType, variationKey);
+            var metadata = new DecisionMetadata(flagKey, ruleKey, ruleType, variationKey);
 
             return new ImpressionEvent.Builder()
                 .WithEventContext(eventContext)

From 6c8d8f1140e0f0887e80c1774e81b8901659faa3 Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Tue, 13 Oct 2020 20:40:42 +0500
Subject: [PATCH 14/15] unit test fix

---
 OptimizelySDK.Tests/EventTests/EventFactoryTest.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
index 00308aa7..eb53073c 100644
--- a/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
+++ b/OptimizelySDK.Tests/EventTests/EventFactoryTest.cs
@@ -509,7 +509,7 @@ public void TestCreateImpressionEventRemovesInvalidAttributesFromPayloadRollout(
                                                     {"experiment_id", null },
                                                     {"variation_id", null },
                                                     { "metadata", new Dictionary<string, object> {
-                                                            { "rule_type", "" },
+                                                            { "rule_type", "rollout" },
                                                             { "rule_key", "" },
                                                             { "flag_key", "test_feature" },
                                                             { "variation_key", "" }

From 8c13a0eb14dfaa882c65013e3590579dd8e583bd Mon Sep 17 00:00:00 2001
From: muhammadnoman <muhammadnoman@folio3.com>
Date: Wed, 14 Oct 2020 01:52:39 +0500
Subject: [PATCH 15/15] Refact: made experiment constant refact decision source
 condition

---
 OptimizelySDK/Optimizely.cs | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 216ff080..cfc1a186 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -100,6 +100,8 @@ public static String SDK_TYPE
         public const string EVENT_KEY = "Event Key";
         public const string FEATURE_KEY = "Feature Key";
         public const string VARIABLE_KEY = "Variable Key";
+        private const string SOURCE_TYPE_EXPERIMENT = "experiment";
+
         public bool Disposed { get; private set; }
 
         /// <summary>
@@ -252,7 +254,7 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
                 return null;
             }
 
-            SendImpressionEvent(experiment, variation, userId, userAttributes, config, "experiment");
+            SendImpressionEvent(experiment, variation, userId, userAttributes, config, SOURCE_TYPE_EXPERIMENT);
 
             return variation;
         }
@@ -471,11 +473,7 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
             var sourceInfo = new Dictionary<string, string>();
             var decision = DecisionService.GetVariationForFeature(featureFlag, userId, config, userAttributes);
             var variation = decision.Variation;
-            var decisionSource = FeatureDecision.DECISION_SOURCE_ROLLOUT;
-            if (decisionSource != null)
-            {
-                decisionSource = decision.Source;
-            }
+            var decisionSource = decision?.Source ?? FeatureDecision.DECISION_SOURCE_ROLLOUT;
 
             SendImpressionEvent(decision.Experiment, variation, userId, userAttributes, config, featureKey, decisionSource);