Skip to content

Commit

Permalink
feat: CreateConfidence from Provider for Metadata (#141)
Browse files Browse the repository at this point in the history
* feat: SDK ID for native and OF

* feat: Force correct init for Provider
  • Loading branch information
fabriziodemaria committed Jun 18, 2024
1 parent 54a674e commit bf368df
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 23 deletions.
45 changes: 33 additions & 12 deletions Sources/Confidence/Confidence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ import Combine
import os

public class Confidence: ConfidenceEventSender {
public let clientSecret: String
public var region: ConfidenceRegion
private let clientSecret: String
private var region: ConfidenceRegion
private let parent: ConfidenceContextProvider?
private let eventSenderEngine: EventSenderEngine
private let contextSubject = CurrentValueSubject<ConfidenceStruct, Never>([:])
private var removedContextKeys: Set<String> = Set()
private let confidenceQueue = DispatchQueue(label: "com.confidence.queue")
private let remoteFlagResolver: ConfidenceResolveClient
private let flagApplier: FlagApplier
private var cache = FlagResolution.EMPTY
private var storage: Storage
internal let contextReconciliatedChanges = PassthroughSubject<String, Never>()
private var cancellables = Set<AnyCancellable>()
private var currentFetchTask: Task<(), Never>?

// Internal for testing
internal let remoteFlagResolver: ConfidenceResolveClient
internal let contextReconciliatedChanges = PassthroughSubject<String, Never>()

public static let sdkId: String = "SDK_ID_SWIFT_CONFIDENCE"

required init(
clientSecret: String,
region: ConfidenceRegion,
Expand Down Expand Up @@ -255,15 +259,20 @@ public class Confidence: ConfidenceEventSender {

extension Confidence {
public class Builder {
let clientSecret: String
// Must be configured or configured automatically
internal let clientSecret: String
internal let eventStorage: EventStorage
internal let visitorId = VisitorUtil().getId()

// Can be configured
internal var region: ConfidenceRegion = .global
internal var metadata: ConfidenceMetadata?
internal var initialContext: ConfidenceStruct = [:]

// Injectable for testing
internal var flagApplier: FlagApplier?
internal var storage: Storage?
internal let eventStorage: EventStorage
internal var flagResolver: ConfidenceResolveClient?
var region: ConfidenceRegion = .global

var visitorId = VisitorUtil().getId()
var initialContext: ConfidenceStruct = [:]

/**
Initializes the builder with the given credentails.
Expand Down Expand Up @@ -293,6 +302,9 @@ extension Confidence {
return self
}

/**
Sets the initial Context.
*/
public func withContext(initialContext: ConfidenceStruct) -> Builder {
self.initialContext = initialContext
return self
Expand All @@ -307,12 +319,21 @@ extension Confidence {
return self
}

/**
Overrides the Metadata for the Confidence instance. In normal production scenarios, Metadata is
handled automatically by the SDK and this overrides should not be applied.
*/
public func withMetadata(metadata: ConfidenceMetadata) -> Builder {
self.metadata = metadata
return self
}

public func build() -> Confidence {
let options = ConfidenceClientOptions(
credentials: ConfidenceClientCredentials.clientSecret(secret: clientSecret),
region: region)
let metadata = ConfidenceMetadata(
name: "SDK_ID_SWIFT_CONFIDENCE",
let metadata = metadata ?? ConfidenceMetadata(
name: Confidence.sdkId,
version: "0.1.4") // x-release-please-version
let uploader = RemoteConfidenceClient(
options: options,
Expand Down
5 changes: 3 additions & 2 deletions Sources/Confidence/RemoteResolveConfidenceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import Foundation
public class RemoteConfidenceResolveClient: ConfidenceResolveClient {
private let targetingKey = "targeting_key"
private var options: ConfidenceClientOptions
private let metadata: ConfidenceMetadata

private var httpClient: HttpClient
private var applyOnResolve: Bool

// Internal for testing
internal let metadata: ConfidenceMetadata

init(
options: ConfidenceClientOptions,
session: URLSession? = nil,
Expand Down
53 changes: 45 additions & 8 deletions Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import OpenFeature
import os

struct Metadata: ProviderMetadata {
var name: String?
var name: String? = ConfidenceFeatureProvider.providerId
}

/// The implementation of the Confidence Feature Provider. This implementation allows to pre-cache evaluations.
public class ConfidenceFeatureProvider: FeatureProvider {
public var metadata: ProviderMetadata
public static let providerId: String = "SDK_ID_SWIFT_PROVIDER"

public var metadata: ProviderMetadata = Metadata()
public var hooks: [any Hook] = []
private let lock = UnfairLock()
private let initializationStrategy: InitializationStrategy
Expand All @@ -20,8 +22,47 @@ public class ConfidenceFeatureProvider: FeatureProvider {
private var currentResolveTask: Task<Void, Never>?
private let confidenceFeatureProviderQueue = DispatchQueue(label: "com.provider.queue")

/// Initialize the Provider via a `Confidence` object.
public convenience init(confidence: Confidence, initializationStrategy: InitializationStrategy = .fetchAndActivate) {
/**
Creates the `Confidence` object to be used as init parameter for this Provider.
*/
public static func createConfidence(clientSecret: String) -> ConfidenceForOpenFeature {
return ConfidenceForOpenFeature.init(confidence: Confidence.Builder.init(clientSecret: clientSecret)
.withRegion(region: .global)
.withMetadata(metadata: ConfidenceMetadata.init(
name: providerId,
version: "0.2.1") // x-release-please-version
)
.build())
}

/**
Proxy holder to ensure correct Confidence configuration passed into the Provider's init.
Do not instantiate directly.
*/
public class ConfidenceForOpenFeature {
internal init(confidence: Confidence) {
self.confidence = confidence
}
let confidence: Confidence
}

/**
Initialize the Provider via a `Confidence` object.
The `Confidence` object must be creted via the `createConfidence` function available from this same class,
rather then be instantiated directly via `Confidence.init(...)` as you would if not using the OpenFeature integration.
*/
public convenience init(
confidenceForOF: ConfidenceForOpenFeature,
initializationStrategy: InitializationStrategy = .fetchAndActivate
) {
self.init(confidence: confidenceForOF.confidence, session: nil)
}

// Allows to pass a confidence object with injected configurations for testing
internal convenience init(
confidence: Confidence,
initializationStrategy: InitializationStrategy = .fetchAndActivate
) {
self.init(confidence: confidence, session: nil)
}

Expand All @@ -30,10 +71,6 @@ public class ConfidenceFeatureProvider: FeatureProvider {
initializationStrategy: InitializationStrategy = .fetchAndActivate,
session: URLSession?
) {
let metadata = ConfidenceMetadata(
name: "SDK_ID_SWIFT_PROVIDER",
version: "0.2.1") // x-release-please-version
self.metadata = Metadata(name: metadata.name)
self.initializationStrategy = initializationStrategy
self.confidence = confidence
}
Expand Down
20 changes: 19 additions & 1 deletion Tests/ConfidenceProviderTests/ConfidenceProviderTest.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Foundation
import ConfidenceProvider
import Combine
import OpenFeature
import XCTest

@testable import Confidence
@testable import ConfidenceProvider

class ConfidenceProviderTest: XCTestCase {
private var readyExpectation = XCTestExpectation(description: "Ready")
Expand Down Expand Up @@ -74,4 +74,22 @@ class ConfidenceProviderTest: XCTestCase {
await fulfillment(of: [errorExpectation], timeout: 5.0)
cancellable.cancel()
}

func testMetadata() async throws {
class FakeClient: ConfidenceResolveClient {
func resolve(ctx: ConfidenceStruct) async throws -> ResolvesResult {
throw ConfidenceError.internalError(message: "test")
}
}

let confidenceForOpenFeature = ConfidenceFeatureProvider.createConfidence(clientSecret: "testSecret")
let provider = ConfidenceFeatureProvider(
confidenceForOF: confidenceForOpenFeature,
initializationStrategy: .activateAndFetchAsync
)
XCTAssertEqual("SDK_ID_SWIFT_PROVIDER", provider.metadata.name)
let remoteClient = confidenceForOpenFeature.confidence.remoteFlagResolver as? RemoteConfidenceResolveClient
XCTAssertEqual("SDK_ID_SWIFT_PROVIDER", remoteClient?.metadata.name)
XCTAssertNotEqual("", remoteClient?.metadata.version)
}
}
7 changes: 7 additions & 0 deletions Tests/ConfidenceTests/ConfidenceTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ class ConfidenceTest: XCTestCase {
XCTAssertEqual(error as? ConfidenceError, ConfidenceError.invalidContextInMessage)
}
}

func testConfidenceMetadata() {
let confidence = Confidence.Builder(clientSecret: "").build()
let remoteClient = confidence.remoteFlagResolver as? RemoteConfidenceResolveClient
XCTAssertEqual("SDK_ID_SWIFT_CONFIDENCE", remoteClient?.metadata.name)
XCTAssertNotEqual("", remoteClient?.metadata.version)
}
}

final class DispatchQueueFake: DispatchQueueType {
Expand Down

0 comments on commit bf368df

Please sign in to comment.