Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FSSDK-9776] handle duplicate experiment key #523

Merged
merged 11 commits into from
Nov 30, 2023
2 changes: 1 addition & 1 deletion Sources/Optimizely/OptimizelyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ open class OptimizelyClient: NSObject {
public func getOptimizelyConfig() throws -> OptimizelyConfig {
guard let config = self.config else { throw OptimizelyError.sdkNotReady }

return OptimizelyConfigImp(projectConfig: config)
return OptimizelyConfigImp(projectConfig: config, logger: logger)
}

}
Expand Down
11 changes: 7 additions & 4 deletions Sources/Optimizely/OptimizelyConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ struct OptimizelyConfigImp: OptimizelyConfig {
var attributes: [OptimizelyAttribute] = []
var audiences: [OptimizelyAudience] = []
var events: [OptimizelyEvent] = []

init(projectConfig: ProjectConfig) {
init(projectConfig: ProjectConfig, logger: OPTLogger = DefaultLogger()) {
guard let project = projectConfig.project else { return }

self.environmentKey = project.environmentKey ?? ""
Expand Down Expand Up @@ -139,7 +139,7 @@ struct OptimizelyConfigImp: OptimizelyConfig {
return updatedRollout
}

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

extension OptimizelyConfigImp {

func makeExperimentsMap(project: Project, experiments: [Experiment]) -> [String: Experiment] {
func makeExperimentsMap(project: Project, experiments: [Experiment], logger: OPTLogger) -> [String: Experiment] {
var map = [String: Experiment]()
experiments.forEach {
if map.keys.contains($0.key) {
logger.w("Duplicate experiment keys found in datafile: \($0.key)")
}
map[$0.key] = $0
}
return map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,64 @@ class OptimizelyClientTests_OptimizelyConfig: XCTestCase {
let result = try? self.optimizely.getOptimizelyConfig()
XCTAssertNil(result)
}

func testOptimizelyConfigWithDuplicateKeys() {
let exp0: [String : Any] = [
"id": "10001",
"key": "duplicate_key",
"status": "Running",
"layerId": "22222",
"variations": [],
"trafficAllocation": [],
"audienceIds": ["33333"],
"audienceConditions": [],
"forcedVariations": ["12345": "1234567890"]
]

let exp1: [String : Any] = [
"id": "10005",
"key": "duplicate_key",
"status": "Running",
"layerId": "22222",
"variations": [],
"trafficAllocation": [],
"audienceIds": ["33333"],
"audienceConditions": [],
"forcedVariations": ["12345": "1234567890"]
]

var projectData: [String: Any] = [
"version": "4",
"projectId": "11111",
"experiments": [],
"audiences": [],
"groups": [],
"attributes": [],
"accountId": "1234567890",
"events": [],
"revision": "5",
"anonymizeIP": true,
"rollouts": [],
"typedAudiences": [],
"integrations": [],
"featureFlags": [],
"botFiltering": false,
"sendFlagDecisions": true
]

projectData["experiments"] = [exp0, exp1]
let model: Project = try! OTUtils.model(from: projectData)
let projectConfig = ProjectConfig()
projectConfig.project = model

let logger = TestLogger()
let optiConfigImpl = OptimizelyConfigImp(projectConfig: projectConfig, logger: logger)
let optimizelyExpMap: [String: OptimizelyExperiment] = optiConfigImpl.experimentsMap
XCTAssertEqual(logger.getMessages(.warning), ["Duplicate experiment keys found in datafile: duplicate_key"])

XCTAssertEqual(optimizelyExpMap.count, 1)
XCTAssertEqual(optimizelyExpMap["duplicate_key"]?.id, "10005")
}

}

Expand Down Expand Up @@ -365,3 +423,41 @@ extension OptimizelyEvent {
}
}

// MARK: - Mock Loggers

fileprivate class TestLogger: OPTLogger {
private static var _logLevel: OptimizelyLogLevel?
public static var logLevel: OptimizelyLogLevel {
get {
return _logLevel ?? .info
}
set (newLevel) {
_logLevel = newLevel
}
}

required public init() {
clearMessages()
}

func log(level: OptimizelyLogLevel, message: String) {
logMessages[level.rawValue].append(message)
}

// Utils

var logMessages = [[String]]()

var logCount: Int {
return logMessages.reduce(0) { $0 + $1.count }
}

func getMessages(_ level: OptimizelyLogLevel) -> [String] {
return logMessages[level.rawValue]
}

func clearMessages() {
logMessages = [[String]](repeating: [], count: OptimizelyLogLevel.debug.rawValue + 1)
}

}
Loading