Skip to content

Commit

Permalink
feat: add shouldApply to control sending apply
Browse files Browse the repository at this point in the history
  • Loading branch information
nickybondarenko committed Feb 10, 2025
1 parent 3588ae8 commit 5e75ab9
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 27 deletions.
1 change: 1 addition & 0 deletions Sources/Confidence/ConfidenceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct ResolvedValue: Codable, Equatable {
var value: ConfidenceValue?
var flag: String
var resolveReason: ResolveReason
var shouldApply: Bool
}

public struct ResolvesResult: Codable, Equatable {
Expand Down
18 changes: 13 additions & 5 deletions Sources/Confidence/FlagEvaluation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension FlagResolution {
) -> Evaluation<T> {
do {
let parsedKey = try FlagPath.getPath(for: flagName)
let resolvedFlag = self.flags.first { resolvedFlag in resolvedFlag.flag == parsedKey.flag }
let resolvedFlag = self.flags.first { resolvedFlag in resolvedFlag.flag == parsedKey.flag }
guard let resolvedFlag = resolvedFlag else {
return Evaluation(
value: defaultValue,
Expand All @@ -56,7 +56,9 @@ extension FlagResolution {
guard let value = resolvedFlag.value else {
// No backend error, but nil value returned. This can happend with "noSegmentMatch" or "archived", for example
Task {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
if resolvedFlag.shouldApply {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
}
}
return Evaluation(
value: defaultValue,
Expand All @@ -77,7 +79,9 @@ extension FlagResolution {
}
if let typedValue = typedValue {
Task {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
if resolvedFlag.shouldApply {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
}
}
return Evaluation(
value: typedValue,
Expand All @@ -90,7 +94,9 @@ extension FlagResolution {
// `null` type from backend instructs to use client-side default value
if parsedValue == .init(null: ()) {
Task {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
if resolvedFlag.shouldApply {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
}
}
return Evaluation(
value: defaultValue,
Expand All @@ -111,7 +117,9 @@ extension FlagResolution {
}
} else {
Task {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
if resolvedFlag.shouldApply {
await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken)
}
}
return Evaluation(
value: defaultValue,
Expand Down
7 changes: 5 additions & 2 deletions Sources/Confidence/RemoteResolveConfidenceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ class RemoteConfidenceResolveClient: ConfidenceResolveClient {
return ResolvedValue(
value: nil,
flag: try displayName(resolvedFlag: resolvedFlag),
resolveReason: resolvedFlag.reason)
resolveReason: resolvedFlag.reason,
shouldApply: true
)
}

let value = ConfidenceValue(
Expand All @@ -81,7 +83,8 @@ class RemoteConfidenceResolveClient: ConfidenceResolveClient {
variant: variant,
value: value,
flag: try displayName(resolvedFlag: resolvedFlag),
resolveReason: resolvedFlag.reason
resolveReason: resolvedFlag.reason,
shouldApply: true
)
}

Expand Down
3 changes: 2 additions & 1 deletion Tests/ConfidenceProviderTests/ConfidenceProviderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ class ConfidenceProviderTest: XCTestCase {
variant: "variant1",
value: .init(structure: ["int": .init(integer: 42)]),
flag: "flagName",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]
func resolve(ctx: ConfidenceStruct) async throws -> ResolvesResult {
return .init(resolvedValues: resolvedValues, resolveToken: "token")
Expand Down
81 changes: 65 additions & 16 deletions Tests/ConfidenceTests/ConfidenceTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]


Expand Down Expand Up @@ -174,7 +175,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -216,7 +218,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -253,7 +256,8 @@ class ConfidenceTest: XCTestCase {
ResolvedValue(
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .noSegmentMatch)
resolveReason: .noSegmentMatch,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -293,7 +297,8 @@ class ConfidenceTest: XCTestCase {
ResolvedValue(
value: .init(structure: ["size": .init(null: ())]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -334,7 +339,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -382,7 +388,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -424,7 +431,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(double: 3.14)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -465,7 +473,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -518,7 +527,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -578,7 +588,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(integer: 3)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -628,7 +639,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(boolean: true)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -668,7 +680,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(structure: ["boolean": .init(boolean: true)])]),
flag: "flag",
resolveReason: .match
resolveReason: .match,
shouldApply: true
)
client.resolvedValues = [value]

Expand Down Expand Up @@ -710,7 +723,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(null: ())]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand Down Expand Up @@ -779,7 +793,7 @@ class ConfidenceTest: XCTestCase {

let client = FakeClient()
client.resolvedValues =
[ResolvedValue(flag: "flag", resolveReason: .targetingKeyError)]
[ResolvedValue(flag: "flag", resolveReason: .targetingKeyError, shouldApply: true)]

let confidence = Confidence.Builder(clientSecret: "test")
.withContext(initialContext: ["targeting_key": .init(string: "user2")])
Expand Down Expand Up @@ -829,7 +843,8 @@ class ConfidenceTest: XCTestCase {
variant: "control",
value: .init(structure: ["size": .init(boolean: true)]),
flag: "flag",
resolveReason: .match)
resolveReason: .match,
shouldApply: true)
]

let confidence = Confidence.Builder(clientSecret: "test")
Expand All @@ -852,6 +867,40 @@ class ConfidenceTest: XCTestCase {
XCTAssertEqual(flagApplier.applyCallCount, 0)
}

func testShouldNotApply() async throws {
class FakeClient: ConfidenceResolveClient {
var resolveStats: Int = 0
var resolvedValues: [ResolvedValue] = []
func resolve(ctx: ConfidenceStruct) async throws -> ResolvesResult {
self.resolveStats += 1
return .init(resolvedValues: resolvedValues, resolveToken: "token")
}
}

let client = FakeClient()
client.resolvedValues = [
ResolvedValue(
variant: "control",
value: .init(structure: ["size": .init(boolean: true)]),
flag: "flag",
resolveReason: .match,
shouldApply: false)
]

let confidence = Confidence.Builder(clientSecret: "test")
.withContext(initialContext: ["targeting_key": .init(string: "user2")])
.withFlagResolverClient(flagResolver: client)
.withFlagApplier(flagApplier: flagApplier)
.build()

try await confidence.fetchAndActivate()
_ = confidence.getEvaluation(
key: "flag.size",
defaultValue: false)

XCTAssertEqual(flagApplier.applyCallCount, 0)
}

func concurrentActivate() async {
let confidence = Confidence.Builder(clientSecret: "test")
.build()
Expand Down
2 changes: 1 addition & 1 deletion Tests/ConfidenceTests/Helpers/ClientMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class ClientMock: ConfidenceResolveClient {

func resolve(flag: String, ctx: ConfidenceStruct) throws -> ResolveResult {
return ResolveResult(
resolvedValue: ResolvedValue(flag: "flag1", resolveReason: .match),
resolvedValue: ResolvedValue(flag: "flag1", resolveReason: .match, shouldApply: true),
resolveToken: ""
)
}
Expand Down
6 changes: 4 additions & 2 deletions Tests/ConfidenceTests/LocalStorageResolverTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class LocalStorageResolverTest: XCTestCase {
let resolvedValue = ResolvedValue(
value: .init(structure: ["string": .init(string: "Value")]),
flag: "flag_name",
resolveReason: .match
resolveReason: .match,
shouldApply: true
)
let flagResolution = FlagResolution(
context: ["hey": ConfidenceValue(string: "old value")],
Expand All @@ -26,7 +27,8 @@ class LocalStorageResolverTest: XCTestCase {
let resolvedValue = ResolvedValue(
value: .init(structure: ["string": .init(string: "Value")]),
flag: "flag_name",
resolveReason: .match
resolveReason: .match,
shouldApply: true
)
let context =
["hey": ConfidenceValue(string: "old value")]
Expand Down

0 comments on commit 5e75ab9

Please sign in to comment.