Skip to content

Commit 4690cbe

Browse files
[FSSDK-9776] handle duplicate experiment key (#523)
Logged for Duplicate keys (cherry picked from commit c2af8fe)
1 parent 41d2d21 commit 4690cbe

File tree

3 files changed

+104
-5
lines changed

3 files changed

+104
-5
lines changed

Sources/Optimizely/OptimizelyClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ open class OptimizelyClient: NSObject {
745745
public func getOptimizelyConfig() throws -> OptimizelyConfig {
746746
guard let config = self.config else { throw OptimizelyError.sdkNotReady }
747747

748-
return OptimizelyConfigImp(projectConfig: config)
748+
return OptimizelyConfigImp(projectConfig: config, logger: logger)
749749
}
750750
}
751751

Sources/Optimizely/OptimizelyConfig.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ struct OptimizelyConfigImp: OptimizelyConfig {
9595
var attributes: [OptimizelyAttribute] = []
9696
var audiences: [OptimizelyAudience] = []
9797
var events: [OptimizelyEvent] = []
98-
99-
init(projectConfig: ProjectConfig) {
98+
99+
init(projectConfig: ProjectConfig, logger: OPTLogger = DefaultLogger()) {
100100
guard let project = projectConfig.project else { return }
101101

102102
self.environmentKey = project.environmentKey ?? ""
@@ -139,7 +139,7 @@ struct OptimizelyConfigImp: OptimizelyConfig {
139139
return updatedRollout
140140
}
141141

142-
self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments)
142+
self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments, logger: logger)
143143
self.featuresMap = makeFeaturesMap(project: project, experiments: updatedExperiments, rollouts: updatedRollouts)
144144
}
145145
}
@@ -148,9 +148,12 @@ struct OptimizelyConfigImp: OptimizelyConfig {
148148

149149
extension OptimizelyConfigImp {
150150

151-
func makeExperimentsMap(project: Project, experiments: [Experiment]) -> [String: Experiment] {
151+
func makeExperimentsMap(project: Project, experiments: [Experiment], logger: OPTLogger) -> [String: Experiment] {
152152
var map = [String: Experiment]()
153153
experiments.forEach {
154+
if map.keys.contains($0.key) {
155+
logger.w("Duplicate experiment keys found in datafile: \($0.key)")
156+
}
154157
map[$0.key] = $0
155158
}
156159
return map

Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,64 @@ class OptimizelyClientTests_OptimizelyConfig: XCTestCase {
263263
let result = try? self.optimizely.getOptimizelyConfig()
264264
XCTAssertNil(result)
265265
}
266+
267+
func testOptimizelyConfigWithDuplicateKeys() {
268+
let exp0: [String : Any] = [
269+
"id": "10001",
270+
"key": "duplicate_key",
271+
"status": "Running",
272+
"layerId": "22222",
273+
"variations": [],
274+
"trafficAllocation": [],
275+
"audienceIds": ["33333"],
276+
"audienceConditions": [],
277+
"forcedVariations": ["12345": "1234567890"]
278+
]
279+
280+
let exp1: [String : Any] = [
281+
"id": "10005",
282+
"key": "duplicate_key",
283+
"status": "Running",
284+
"layerId": "22222",
285+
"variations": [],
286+
"trafficAllocation": [],
287+
"audienceIds": ["33333"],
288+
"audienceConditions": [],
289+
"forcedVariations": ["12345": "1234567890"]
290+
]
291+
292+
var projectData: [String: Any] = [
293+
"version": "4",
294+
"projectId": "11111",
295+
"experiments": [],
296+
"audiences": [],
297+
"groups": [],
298+
"attributes": [],
299+
"accountId": "1234567890",
300+
"events": [],
301+
"revision": "5",
302+
"anonymizeIP": true,
303+
"rollouts": [],
304+
"typedAudiences": [],
305+
"integrations": [],
306+
"featureFlags": [],
307+
"botFiltering": false,
308+
"sendFlagDecisions": true
309+
]
310+
311+
projectData["experiments"] = [exp0, exp1]
312+
let model: Project = try! OTUtils.model(from: projectData)
313+
let projectConfig = ProjectConfig()
314+
projectConfig.project = model
315+
316+
let logger = TestLogger()
317+
let optiConfigImpl = OptimizelyConfigImp(projectConfig: projectConfig, logger: logger)
318+
let optimizelyExpMap: [String: OptimizelyExperiment] = optiConfigImpl.experimentsMap
319+
XCTAssertEqual(logger.getMessages(.warning), ["Duplicate experiment keys found in datafile: duplicate_key"])
320+
321+
XCTAssertEqual(optimizelyExpMap.count, 1)
322+
XCTAssertEqual(optimizelyExpMap["duplicate_key"]?.id, "10005")
323+
}
266324

267325
}
268326

@@ -371,3 +429,41 @@ extension OptimizelyEvent {
371429
}
372430
}
373431

432+
// MARK: - Mock Loggers
433+
434+
fileprivate class TestLogger: OPTLogger {
435+
private static var _logLevel: OptimizelyLogLevel?
436+
public static var logLevel: OptimizelyLogLevel {
437+
get {
438+
return _logLevel ?? .info
439+
}
440+
set (newLevel) {
441+
_logLevel = newLevel
442+
}
443+
}
444+
445+
required public init() {
446+
clearMessages()
447+
}
448+
449+
func log(level: OptimizelyLogLevel, message: String) {
450+
logMessages[level.rawValue].append(message)
451+
}
452+
453+
// Utils
454+
455+
var logMessages = [[String]]()
456+
457+
var logCount: Int {
458+
return logMessages.reduce(0) { $0 + $1.count }
459+
}
460+
461+
func getMessages(_ level: OptimizelyLogLevel) -> [String] {
462+
return logMessages[level.rawValue]
463+
}
464+
465+
func clearMessages() {
466+
logMessages = [[String]](repeating: [], count: OptimizelyLogLevel.debug.rawValue + 1)
467+
}
468+
469+
}

0 commit comments

Comments
 (0)