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

Add support for overriding param stores #20

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*.swift]
indent_style = space
indent_size = 4
29 changes: 28 additions & 1 deletion Sources/Statsig/InternalStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ fileprivate let MaxCachedUsers = 10
public struct StatsigOverrides {
public var gates: [String: Bool]
public var configs: [String: [String: Any]]
public var params: [String: [String: Any]]

init(_ overrides: [String: Any]) {
gates = overrides[InternalStore.gatesKey] as? [String: Bool] ?? [:]
configs = overrides[InternalStore.configsKey] as? [String: [String: Any]] ?? [:]
params = overrides[InternalStore.paramStoresKey] as? [String: [String: Any]] ?? [:]
}
}

Expand Down Expand Up @@ -486,6 +488,18 @@ class InternalStore {

func getParamStore(client: StatsigClient?, forName storeName: String) -> ParameterStore {
storeQueue.sync {
if let override = (localOverrides[InternalStore.paramStoresKey] as? [String: [String: StatsigDynamicConfigValue]])?[storeName] {
return ParameterStore(
name: storeName,
evaluationDetails: cache.getEvaluationDetails(.LocalOverride),
client: client,
configuration: override.mapValues {[
"ref_type": "static",
"param_type": getTypeOf($0),
"value": $0
]}
)
}
return cache.getParamStore(client, storeName)
}
}
Expand Down Expand Up @@ -563,11 +577,19 @@ class InternalStore {
}
}

func overrideParamStore(_ storeName: String, _ value: [String: Any]) {
storeQueue.async(flags: .barrier) { [weak self] in
self?.localOverrides[jsonDict: InternalStore.paramStoresKey]?[storeName] = value
self?.saveOverrides()
}
}

func removeOverride(_ name: String) {
storeQueue.async(flags: .barrier) { [weak self] in
self?.localOverrides[jsonDict: InternalStore.gatesKey]?.removeValue(forKey: name)
self?.localOverrides[jsonDict: InternalStore.configsKey]?.removeValue(forKey: name)
self?.localOverrides[jsonDict: InternalStore.layerConfigsKey]?.removeValue(forKey: name)
self?.localOverrides[jsonDict: InternalStore.paramStoresKey]?.removeValue(forKey: name)
}
}

Expand All @@ -590,7 +612,12 @@ class InternalStore {
}

private static func getEmptyOverrides() -> [String: Any] {
return [InternalStore.gatesKey: [:], InternalStore.configsKey: [:], InternalStore.layerConfigsKey: [:]]
return [
InternalStore.gatesKey: [:],
InternalStore.configsKey: [:],
InternalStore.layerConfigsKey: [:],
InternalStore.paramStoresKey: [:]
]
}

// Sticky Logic: https://gist.github.com/daniel-statsig/3d8dfc9bdee531cffc96901c1a06a402
Expand Down
11 changes: 11 additions & 0 deletions Sources/Statsig/Statsig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,17 @@ public class Statsig {
client?.overrideLayer(layerName, value: value)
}

/**
Sets a value to be returned for the given parameter store instead of following the actual ref in the store.

Parameters:
- storeName: The name of the config or experiment to be overridden
- value: Dictionary where keys are property names and values are static ref values in the overridden store
*/
public static func overrideParamStore(_ storeName: String, value: [String: Any]) {
client?.overrideParamStore(storeName, value: value)
}

/**
Clears any overridden value for the given gate/dynamic config/experiment.

Expand Down
11 changes: 11 additions & 0 deletions Sources/Statsig/StatsigClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,17 @@ extension StatsigClient {
store.overrideLayer(layerName, value)
}

/**
Sets a value to be returned for the given parameter store instead of following the actual ref in the store.

Parameters:
- storeName: The name of the config or experiment to be overridden
- value: Dictionary where keys are property names and values are static ref values in the overridden store
*/
public func overrideParamStore(_ storeName: String, value: [String: Any]) {
store.overrideParamStore(storeName, value)
}

/**
Clears any overridden value for the given gate/dynamic config/experiment.

Expand Down
14 changes: 14 additions & 0 deletions Tests/StatsigTests/LocalOverridesSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ class LocalOverridesSpec: BaseSpec {
}
}

describe("parameter store overrides") {
it("returns overridden parameter store values") {
Statsig.overrideParamStore("overridden_param_store", value: ["key": "value"])
let store = Statsig.getParameterStore("overridden_param_store")
expect(store.getValue(forKey: "key", defaultValue: "default")).to(equal("value"))
}

it("clears overridden parameter store values") {
Statsig.overrideParamStore("overridden_param_store", value: ["key": "value"])
Statsig.removeOverride("overridden_param_store")
expect(Statsig.getParameterStore("overridden_param_store").getValue(forKey: "key", defaultValue: "default")).to(equal("default"))
}
}

describe("clearing all overrides") {
it("clears all") {
Statsig.overrideGate("overridden_gate", value: true)
Expand Down