Skip to content

Commit 59ac1d9

Browse files
committed
feat: populate Eval reason as event metadata being sent to the Events API
1 parent 4f4aeee commit 59ac1d9

File tree

6 files changed

+91
-15
lines changed

6 files changed

+91
-15
lines changed

DevCycle/DVCVariable.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ public class DVCVariable<T> {
7373
var defaulted = false
7474
self.key = variable.key
7575
self.defaultValue = defaultValue
76-
if (variable.eval != nil) {
77-
self.eval = variable.eval
78-
}
76+
self.eval = variable.eval
7977

8078
let classString = String(describing: T.self)
8179
do {

DevCycle/DevCycleClient.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,15 +414,25 @@ public class DevCycleClient {
414414
variable, forKey: defaultValue as AnyObject)
415415
}
416416

417-
if !self.closed && !self.disableAutomaticEventLogging {
417+
if !self.closed && !self.disableAutomaticEventLogging {
418418
self.eventQueue.updateAggregateEvents(
419-
variableKey: variable.key, variableIsDefaulted: variable.isDefaulted)
419+
variableKey: variable.key, variableIsDefaulted: variable.isDefaulted, metadata: createVariableEventMetaData(variableEval: variable.eval))
420420
}
421421

422422
return variable
423423
}
424424
}
425425

426+
private func createVariableEventMetaData(variableEval: EvalReason?) -> [String: Any]? {
427+
if let eval = variableEval {
428+
if let targetId = eval.targetId {
429+
return ["eval": ["reason": eval.reason, "details": eval.details, "target_id": targetId]]
430+
}
431+
return ["eval": ["reason": eval.reason, "details": eval.details]]
432+
}
433+
return nil
434+
}
435+
426436
public func identifyUser(user: DevCycleUser, callback: IdentifyCompletedHandler? = nil) throws {
427437
guard let currentUser = self.user, !currentUser.userId.isEmpty,
428438
!user.userId.isEmpty

DevCycle/Models/DevCycleEvent.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,18 @@ struct DVCAggregateEvents {
8888
self.variableEvaluated = [:]
8989
}
9090

91-
mutating func track(variableKey: String, eventType: DVCEventTypes) {
91+
mutating func track(variableKey: String, eventType: DVCEventTypes, metadata: [String: Any]?) {
9292
if (eventType == DVCEventTypes.VariableEvaluated) {
9393
if let variableEvaluatedEvent = self.variableEvaluated[variableKey] {
9494
variableEvaluatedEvent.value = variableEvaluatedEvent.value! + 1
9595
} else {
96-
self.variableEvaluated[variableKey] = DevCycleEvent(type: "variableEvaluated", target: variableKey, clientDate: nil, value: 1, metaData: nil)
96+
self.variableEvaluated[variableKey] = DevCycleEvent(type: "variableEvaluated", target: variableKey, clientDate: nil, value: 1, metaData: metadata)
9797
}
9898
} else {
9999
if let variableDefaultedEvent = self.variableDefaulted[variableKey] {
100100
variableDefaultedEvent.value = variableDefaultedEvent.value! + 1
101101
} else {
102-
self.variableDefaulted[variableKey] = DevCycleEvent(type: "variableDefaulted", target: variableKey, clientDate: nil, value: 1, metaData: nil)
102+
self.variableDefaulted[variableKey] = DevCycleEvent(type: "variableDefaulted", target: variableKey, clientDate: nil, value: 1, metaData: metadata)
103103
}
104104
}
105105
}

DevCycle/Models/EventQueue.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@ class EventQueue {
6868
}
6969
}
7070

71-
func updateAggregateEvents(variableKey: String, variableIsDefaulted: Bool) {
71+
func updateAggregateEvents(variableKey: String, variableIsDefaulted: Bool, metadata: [String: Any]?) {
7272
eventDispatchQueue.async {
7373
self.aggregateEventQueue.track(
7474
variableKey: variableKey,
75-
eventType: variableIsDefaulted ? DVCEventTypes.VariableDefaulted : DVCEventTypes.VariableEvaluated
75+
eventType: variableIsDefaulted ? DVCEventTypes.VariableDefaulted : DVCEventTypes.VariableEvaluated,
76+
metadata: metadata
7677
)
7778
}
7879
}

DevCycleTests/Models/DevCycleClientTests.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,68 @@ class DevCycleClientTest: XCTestCase {
165165
client.close(callback: nil)
166166
}
167167

168+
func testFlushEventsWithEvalReasons() throws{
169+
let data = getConfigData(name: "test_config_eval_reason")
170+
let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as! [String:Any]
171+
let evalReasonConfig = try UserConfig(from: dictionary)
172+
173+
let options = DevCycleOptions.builder().eventFlushIntervalMS(100).build()
174+
let service = MockService(userConfig: evalReasonConfig)
175+
176+
let client = try! DevCycleClient.builder().user(self.user).sdkKey("my_sdk_key").options(options).build(
177+
onInitialized: nil)
178+
client.setup(service: service)
179+
client.config?.userConfig = evalReasonConfig
180+
client.initialized = true
181+
182+
XCTAssertTrue(client.initialized)
183+
184+
let variable1 = client.variable(key: "string-var", defaultValue: "test_string")
185+
XCTAssertEqual(variable1.value, "string1")
186+
XCTAssertEqual(variable1.eval?.reason, "TARGETING_MATCH")
187+
XCTAssertEqual(variable1.eval?.details, "Platform AND App Version")
188+
XCTAssertEqual(variable1.eval?.targetId, "target_id_1")
189+
190+
let variable2Value = client.variableValue(key: "bool-var", defaultValue: false)
191+
XCTAssertTrue(variable2Value)
192+
193+
let variableInvalid = client.variable(key: "title_text", defaultValue: "Default")
194+
XCTAssertEqual(variableInvalid.value, "Default")
195+
XCTAssertEqual(variableInvalid.eval?.reason, "DEFAULT")
196+
XCTAssertEqual(variableInvalid.eval?.details, "User Not Targeted")
197+
XCTAssertNil(variableInvalid.eval?.targetId)
198+
199+
let expectation = XCTestExpectation(description: "EventQueue has events with Eval metadata")
200+
// Add a slight delay to ensure events are queued correctly
201+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
202+
expectation.fulfill()
203+
}
204+
wait(for: [expectation], timeout: 0.5)
205+
206+
let variableEvaluatedEvents = client.eventQueue.aggregateEventQueue.variableEvaluated
207+
XCTAssertEqual(variableEvaluatedEvents.count, 2)
208+
209+
let variableDefaultedEvents = client.eventQueue.aggregateEventQueue.variableDefaulted
210+
XCTAssertEqual(variableDefaultedEvents.count, 1)
211+
212+
let expectedEvaluatedMetadata = ["eval": ["reason": "TARGETING_MATCH", "details": "Platform AND App Version", "target_id": "target_id_1"]]
213+
XCTAssertEqual(variableEvaluatedEvents["bool-var"]?.metaData as! [String: [String:String]], expectedEvaluatedMetadata)
214+
XCTAssertEqual(variableEvaluatedEvents["string-var"]?.metaData as! [String: [String:String]], expectedEvaluatedMetadata)
215+
216+
let expectedDefaultedMetadata = ["eval": ["reason": "DEFAULT", "details": "User Not Targeted"]]
217+
XCTAssertEqual(variableDefaultedEvents["title_text"]?.metaData as! [String : [String : String?]], expectedDefaultedMetadata)
218+
219+
client.flushEvents()
220+
221+
let publishExpectation = XCTestExpectation(description: "EventQueue publishes an event")
222+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
223+
XCTAssertTrue(service.publishCallCount == 1)
224+
publishExpectation.fulfill()
225+
}
226+
wait(for: [publishExpectation], timeout: 0.5)
227+
client.close(callback: nil)
228+
}
229+
168230
func testFlushEventsWithOneEventInQueueAndCallback() {
169231
let expectation = XCTestExpectation(description: "EventQueue publishes an event")
170232
let options = DevCycleOptions.builder().flushEventsIntervalMs(100).build()

DevCycleTests/Models/test_config_eval_reason.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"variationName": "id4 name",
1818
"eval": {
1919
"reason": "TARGETING_MATCH",
20-
"details": "Platform AND App Version"
20+
"details": "Platform AND App Version",
21+
"target_id": "target_id_1"
2122
}
2223
}
2324
},
@@ -33,7 +34,8 @@
3334
"value": true,
3435
"eval": {
3536
"reason": "TARGETING_MATCH",
36-
"details": "Platform AND App Version"
37+
"details": "Platform AND App Version",
38+
"target_id": "target_id_1"
3739
}
3840
},
3941
"json-var": {
@@ -48,7 +50,8 @@
4850
},
4951
"eval": {
5052
"reason": "TARGETING_MATCH",
51-
"details": "Platform AND App Version"
53+
"details": "Platform AND App Version",
54+
"target_id": "target_id_1"
5255
}
5356
},
5457
"string-var": {
@@ -58,7 +61,8 @@
5861
"value": "string1",
5962
"eval": {
6063
"reason": "TARGETING_MATCH",
61-
"details": "Platform AND App Version"
64+
"details": "Platform AND App Version",
65+
"target_id": "target_id_1"
6266
}
6367
},
6468
"num-var": {
@@ -68,7 +72,8 @@
6872
"value": 4,
6973
"eval": {
7074
"reason": "TARGETING_MATCH",
71-
"details": "Platform AND App Version"
75+
"details": "Platform AND App Version",
76+
"target_id": "target_id_1"
7277
}
7378
}
7479
},

0 commit comments

Comments
 (0)