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

refactor: Add Confidence Library scaffolding #83

Merged
merged 9 commits into from
Apr 2, 2024
Merged
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
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
/**/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
Expand All @@ -10,4 +10,4 @@ DerivedData/
.build
.mockingbird
project.json
.swiftpm
.swiftpm

This file was deleted.

17 changes: 11 additions & 6 deletions ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ConfidenceProvider
import Confidence
fabriziodemaria marked this conversation as resolved.
Show resolved Hide resolved
import OpenFeature
import SwiftUI

Expand All @@ -21,17 +22,21 @@ extension ConfidenceDemoApp {
}

// If we have no cache, then do a fetch first.
var initializationStratgey: InitializationStrategy = .activateAndFetchAsync
var initializationStrategy: InitializationStrategy = .activateAndFetchAsync
if ConfidenceFeatureProvider.isStorageEmpty() {
initializationStratgey = .fetchAndActivate
initializationStrategy = .fetchAndActivate
}

let provider = ConfidenceFeatureProvider
.Builder(credentials: .clientSecret(secret: secret))
.with(initializationStrategy: initializationStratgey)
let confidence = Confidence.Builder(clientSecret: secret)
.withInitializationstrategy(initializationStrategy: initializationStrategy)
.build()
let provider = ConfidenceFeatureProvider(confidence: confidence)

// NOTE: Using a random UUID for each app start is not advised and can result in getting stale values.
let ctx = MutableContext(targetingKey: UUID.init().uuidString, structure: MutableStructure())
OpenFeatureAPI.shared.setProvider(provider: provider, initialContext: ctx)
Task {
await OpenFeatureAPI.shared.setProviderAndWait(provider: provider, initialContext: ctx)
confidence.send(eventName: "my_event")
}
}
}
13 changes: 11 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,33 @@
import PackageDescription

let package = Package(
name: "ConfidenceProvider",
name: "Confidence",
platforms: [
.iOS(.v14),
.macOS(.v12)
],
products: [
.library(
name: "ConfidenceProvider",
targets: ["ConfidenceProvider"])
targets: ["ConfidenceProvider"]),
.library(
name: "Confidence",
targets: ["Confidence"])
fabriziodemaria marked this conversation as resolved.
Show resolved Hide resolved
],
dependencies: [
.package(url: "[email protected]:open-feature/swift-sdk.git", from: "0.1.0"),
],
targets: [
.target(
name: "Confidence",
dependencies: [],
plugins: []
),
.target(
name: "ConfidenceProvider",
dependencies: [
.product(name: "OpenFeature", package: "swift-sdk"),
"Confidence"
],
plugins: []
),
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ In the dependencies section of Package.swift add:
and in the target dependencies section add:
```swift
.product(name: "ConfidenceProvider", package: "confidence-openfeature-provider-swift"),
.product(name: "Confidence", package: "confidence-openfeature-provider-swift"),
```

## Usage

### Import Modules

Import the `ConfidenceProvider` and `OpenFeature` modules
Import the `ConfidenceProvider`, the `Confidence` and the `OpenFeature` modules

```swift
import ConfidenceProvider
import Confidence
import OpenFeature
```

Expand Down
82 changes: 82 additions & 0 deletions Sources/Confidence/Confidence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Foundation

public class Confidence: ConfidenceEventSender {
public var context: [String: String]
public let clientSecret: String
public var timeout: TimeInterval
public var region: ConfidenceRegion
public var initializationStrategy: InitializationStrategy

init(
clientSecret: String,
timeout: TimeInterval,
region: ConfidenceRegion,
initializationStrategy: InitializationStrategy
) {
self.context = [:]
self.clientSecret = clientSecret
self.timeout = timeout
self.region = region
self.initializationStrategy = initializationStrategy
}

// TODO: Implement actual event uploading to the backend
public func send(eventName: String) {
print("Sending \(eventName) - Targeting key: \(context["targeting_key"] ?? "UNKNOWN")")
}

public func updateContextEntry(key: String, value: String) {
context[key] = value
}

public func removeContextEntry(key: String) {
context.removeValue(forKey: key)
}

public func clearContext() {
context = [:]
}

// TODO: Implement creation of child instances
public func withContext(_ context: [String: String]) -> Self {
return self
}
}

extension Confidence {
public class Builder {
let clientSecret: String
var timeout: TimeInterval = 10.0
var region: ConfidenceRegion = .global
var initializationStrategy: InitializationStrategy = .fetchAndActivate

public init(clientSecret: String) {
self.clientSecret = clientSecret
}

public func withTimeout(timeout: TimeInterval) -> Builder {
self.timeout = timeout
return self
}


public func withRegion(region: ConfidenceRegion) -> Builder {
self.region = region
return self
}

public func withInitializationstrategy(initializationStrategy: InitializationStrategy) -> Builder {
self.initializationStrategy = initializationStrategy
return self
}

public func build() -> Confidence {
return Confidence(
clientSecret: clientSecret,
timeout: timeout,
region: region,
initializationStrategy: initializationStrategy
)
}
}
}
7 changes: 7 additions & 0 deletions Sources/Confidence/ConfidenceEventSender.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

/// Sends events to Confidence. Contextual data is appended to each event
// TODO: Add functions for sending events with payload
public protocol ConfidenceEventSender: Contextual {
func send(eventName: String)
}
9 changes: 9 additions & 0 deletions Sources/Confidence/ConfidenceRegion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

/// Sets the region for the network request to the Confidence backend.
/// This is applied for both sending events as well as fetching flag's data.
public enum ConfidenceRegion {
case global
case europe
case usa
}
15 changes: 15 additions & 0 deletions Sources/Confidence/Contextual.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

/// A Contextual implementer maintains context data and can create child instances
/// that can still access their parent's data
public protocol Contextual {
// TODO: Add complex type to the context Dictionary
var context: [String: String] { get set }

func updateContextEntry(key: String, value: String)
func removeContextEntry(key: String)
func clearContext()
/// Creates a child Contextual instance that still has access
/// to its parent context
func withContext(_ context: [String: String]) -> Self
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation

/// Flag resolve configuration related to how to refresh flags at startup
public enum InitializationStrategy {
case fetchAndActivate, activateAndFetchAsync
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation
import Confidence

public struct ConfidenceClientOptions {
public var credentials: ConfidenceClientCredentials
public var timeout: TimeInterval
public var region: ConfidenceRegion
public var initializationStrategy: InitializationStrategy

public init(
credentials: ConfidenceClientCredentials,
timeout: TimeInterval? = nil,
region: ConfidenceRegion? = nil,
initializationStrategy: InitializationStrategy = .fetchAndActivate
) {
self.credentials = credentials
self.timeout = timeout ?? 10.0
self.region = region ?? .global
self.initializationStrategy = initializationStrategy
}
}

public enum ConfidenceClientCredentials {
fabriziodemaria marked this conversation as resolved.
Show resolved Hide resolved
case clientSecret(secret: String)

public func getSecret() -> String {
switch self {
case .clientSecret(let secret):
return secret
}
}
}
Loading
Loading