Skip to content

Commit f1995d5

Browse files
authored
fix(decide): change nil to NSNull for decision flag notification (#386)
* change nil to NSNull for decision flag notification * fix decisionEventDispatched false set
1 parent 3b31260 commit f1995d5

12 files changed

+182
-79
lines changed

DemoSwiftApp/AppDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
181181
self.user = self.optimizely.createUserContext(userId: self.userId,
182182
attributes: self.attributes)
183183
let decision = self.user.decide(key: self.featureKey, options: [.includeReasons])
184+
print("DECISION = \(decision)")
184185
if let variationKey = decision.variationKey {
185186
self.openVariationView(variationKey: variationKey)
186187
} else {

Sources/Implementation/DecisionInfo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ struct DecisionInfo {
138138
decisionInfo[Constants.DecisionInfoKeys.flagKey] = flagKey
139139
decisionInfo[Constants.DecisionInfoKeys.enabled] = enabled
140140
decisionInfo[Constants.DecisionInfoKeys.variables] = variableValues
141-
decisionInfo[Constants.DecisionInfoKeys.variationKey] = variation?.key
142-
decisionInfo[Constants.DecisionInfoKeys.ruleKey] = ruleKey
141+
decisionInfo[Constants.DecisionInfoKeys.variationKey] = variation?.key ?? NSNull() // keep key in the map even with nil value
142+
decisionInfo[Constants.DecisionInfoKeys.ruleKey] = ruleKey ?? NSNull() //
143143
decisionInfo[Constants.DecisionInfoKeys.reasons] = reasons
144144
decisionInfo[Constants.DecisionInfoKeys.decisionEventDispatched] = decisionEventDispatched
145145
}

Sources/Implementation/Events/BatchEventBuilder.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019-2020, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2021, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -32,10 +32,6 @@ class BatchEventBuilder {
3232
ruleType: String,
3333
enabled: Bool) -> Data? {
3434

35-
if (ruleType == Constants.DecisionSource.rollout.rawValue || variation == nil) && !config.sendFlagDecisions {
36-
return nil
37-
}
38-
3935
let metaData = DecisionMetadata(ruleType: ruleType, ruleKey: experiment?.key ?? "", flagKey: flagKey, variationKey: variation?.key ?? "", enabled: enabled)
4036

4137
let decision = Decision(variationID: variation?.id ?? "",

Sources/Optimizely+Decide/OptimizelyClient+Decide.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@ extension OptimizelyClient {
6262
}
6363

6464
if !allOptions.contains(.disableDecisionEvent) {
65-
sendImpressionEvent(experiment: decision?.experiment,
66-
variation: decision?.variation,
67-
userId: userId,
68-
attributes: attributes,
69-
flagKey: feature.key,
70-
ruleType: decision?.source ?? Constants.DecisionSource.rollout.rawValue,
71-
enabled: enabled)
72-
decisionEventDispatched = true
65+
let ruleType = decision?.source ?? Constants.DecisionSource.rollout.rawValue
66+
if shouldSendDecisionEvent(source: ruleType, decision: decision) {
67+
sendImpressionEvent(experiment: decision?.experiment,
68+
variation: decision?.variation,
69+
userId: userId,
70+
attributes: attributes,
71+
flagKey: feature.key,
72+
ruleType: ruleType,
73+
enabled: enabled)
74+
decisionEventDispatched = true
75+
}
7376
}
7477

7578
var variableMap = [String: Any]()

Sources/Optimizely+Decide/OptimizelyDecision.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,26 @@ extension OptimizelyDecision: Equatable {
6868
lhs.reasons == rhs.reasons
6969
}
7070
}
71+
72+
extension OptimizelyDecision: CustomStringConvertible {
73+
public var description: String {
74+
let variationKey = self.variationKey == nil ? "nil" : ("\"" + self.variationKey! + "\"")
75+
let ruleKey = self.ruleKey == nil ? "nil" : ("\"" + self.ruleKey! + "\"")
76+
77+
return """
78+
{
79+
variationKey: \(variationKey)
80+
enabled: \(enabled)
81+
variables: \(variables)
82+
ruleKey: \(ruleKey)
83+
flagKey: "\(flagKey)"
84+
userContext: \(userContext)
85+
reasons: [
86+
"""
87+
+ (reasons.isEmpty ? "" : reasons.reduce("\n") {$0 + " - \($1)\n"}) +
88+
"""
89+
]
90+
}
91+
"""
92+
}
93+
}

Sources/Optimizely+Decide/OptimizelyUserContext.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,9 @@ extension OptimizelyUserContext: Equatable {
139139
}
140140

141141
}
142+
143+
extension OptimizelyUserContext: CustomStringConvertible {
144+
public var description: String {
145+
return "{ userId: \(userId), attributes: \(attributes) }"
146+
}
147+
}

Sources/Optimizely/OptimizelyClient.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -404,13 +404,15 @@ open class OptimizelyClient: NSObject {
404404
logger.i(.featureNotEnabledForUser(featureKey, userId))
405405
}
406406

407-
sendImpressionEvent(experiment: pair?.experiment,
408-
variation: pair?.variation,
409-
userId: userId,
410-
attributes: attributes,
411-
flagKey: featureKey,
412-
ruleType: source,
413-
enabled: featureEnabled)
407+
if shouldSendDecisionEvent(source: source, decision: pair) {
408+
sendImpressionEvent(experiment: pair?.experiment,
409+
variation: pair?.variation,
410+
userId: userId,
411+
attributes: attributes,
412+
flagKey: featureKey,
413+
ruleType: source,
414+
enabled: featureEnabled)
415+
}
414416

415417
sendDecisionNotification(userId: userId,
416418
attributes: attributes,
@@ -760,6 +762,11 @@ open class OptimizelyClient: NSObject {
760762

761763
extension OptimizelyClient {
762764

765+
func shouldSendDecisionEvent(source: String, decision: FeatureDecision?) -> Bool {
766+
guard let config = self.config else { return false }
767+
return (source == Constants.DecisionSource.featureTest.rawValue && decision?.variation != nil) || config.sendFlagDecisions
768+
}
769+
763770
func sendImpressionEvent(experiment: Experiment?,
764771
variation: Variation?,
765772
userId: String,

Sources/Optimizely/OptimizelyError.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,13 @@ extension OptimizelyError: CustomStringConvertible, ReasonProtocol {
9797
switch self {
9898
case .generic: message = "Unknown reason."
9999

100+
// DO NOT CHANGE these critical error messages - FSC will validate exact-wordings of these messages.
100101
case .sdkNotReady: message = "Optimizely SDK not configured properly yet."
101102
case .featureKeyInvalid(let key): message = "No flag was found for key \"\(key)\"."
102103
case .variableValueInvalid(let key): message = "Variable value for key \"\(key)\" is invalid or wrong type."
103104
case .invalidJSONVariable: message = "Invalid variables for OptimizelyJSON."
104105

106+
// These error messages not validated by FSC
105107
case .experimentKeyInvalid(let key): message = "Experiment key (\(key)) is not in datafile. It is either invalid, paused, or archived."
106108
case .experimentIdInvalid(let id): message = "Experiment ID (\(id)) is not in datafile."
107109
case .experimentHasNoTrafficAllocation(let key): message = "No traffic allocation rules are defined for experiement (\(key))."

Sources/Optimizely/OptimizelyJSON.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,9 @@ extension OptimizelyJSON {
199199
}
200200

201201
}
202+
203+
extension OptimizelyJSON {
204+
public override var description: String {
205+
return "\(map)"
206+
}
207+
}

Tests/OptimizelyTests-Common/BatchEventBuilderTests_Events.swift

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019-2020, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2021, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -99,42 +99,6 @@ class BatchEventBuilderTests_Events: XCTestCase {
9999
XCTAssertNil(de["value"])
100100
}
101101

102-
func testCreateImpressionEventWithSendFlagDecisions() {
103-
let scenarios: [String: Bool] = [
104-
"experiment": true,
105-
"anything-else": true,
106-
Constants.DecisionSource.featureTest.rawValue: true,
107-
Constants.DecisionSource.rollout.rawValue: false
108-
]
109-
let attributes: [String: Any] = [
110-
"s_foo": "foo",
111-
"b_true": true,
112-
"i_42": 42,
113-
"d_4_2": 4.2
114-
]
115-
let experiment = optimizely.config?.getExperiment(id: "10390977714")
116-
let variation = experiment?.getVariation(id: "10416523162")
117-
118-
for scenario in scenarios {
119-
let event = BatchEventBuilder.createImpressionEvent(config: optimizely.config!, experiment: experiment!, variation: variation, userId: userId, attributes: attributes, flagKey: experiment!.key, ruleType: scenario.key, enabled: true)
120-
scenario.value ? XCTAssertNotNil(event): XCTAssertNil(event)
121-
}
122-
123-
// nil variation should always return nil
124-
for scenario in scenarios {
125-
let event = BatchEventBuilder.createImpressionEvent(config: optimizely.config!, experiment: experiment!, variation: nil, userId: userId, attributes: attributes, flagKey: experiment!.key, ruleType: scenario.key, enabled: true)
126-
XCTAssertNil(event)
127-
}
128-
129-
// should always return a event if sendFlagDecisions is set
130-
optimizely.config?.project.sendFlagDecisions = true
131-
for scenario in scenarios {
132-
let event = BatchEventBuilder.createImpressionEvent(config: optimizely.config!, experiment: experiment!, variation: nil, userId: userId, attributes: attributes, flagKey: experiment!.key, ruleType: scenario.key, enabled: true)
133-
XCTAssertNotNil(event)
134-
}
135-
optimizely.config?.project.sendFlagDecisions = nil
136-
}
137-
138102
func testCreateImpressionEventWithoutVariation() {
139103
let attributes: [String: Any] = [
140104
"s_foo": "foo",

0 commit comments

Comments
 (0)