-
Notifications
You must be signed in to change notification settings - Fork 1
/
LDManager.swift
188 lines (156 loc) · 5.71 KB
/
LDManager.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//
// LDManager.swift
//
//
// Created by Danny Leiser on 5/11/23.
// Copyright © 2023-Present Forage Technology Corporation. All rights reserved.
//
import Foundation
import LaunchDarkly
protocol LDClientProtocol {
func doubleVariationWrapper(forKey key: String, defaultValue: Double) -> Double
}
extension LDClient: LDClientProtocol {
func doubleVariationWrapper(forKey key: String, defaultValue: Double) -> Double {
doubleVariation(forKey: key, defaultValue: defaultValue)
}
}
protocol LDManagerProtocol {
func getVaultType(ldClient: LDClientProtocol?, genRandomDouble: () -> Double) -> VaultType
}
/**
LD Public Keys
*/
private enum LDMobileKey: String {
case sandbox = "mob-22024b85-05b7-4e24-b290-a071310dfc3d"
case cert = "mob-d2261a08-784b-4300-a45f-ce0e46324d66"
case prod = "mob-5c3dfa7a-fa6d-4cdf-93e8-d28ef8080696"
case staging = "mob-a9903698-759b-48e2-86e1-c551e2b69118"
case dev = "mob-03e025cb-5b4e-4d97-8685-39a22316d601"
}
/**
Vault Types
*/
enum VaultType: String {
case vgs
case basisTheory = "basis_theory"
}
/**
Flag Types
*/
enum FlagType: String {
case vaultPrimaryTrafficPercentage = "vault-primary-traffic-percentage"
}
/**
LaunchDarkly prefix
*/
private let LAUNCH_DARKLY_PREFIX = "LaunchDarkly"
/**
Contexts
*/
private enum ContextKey: String {
case iosSdk = "ios-sdk-service"
}
private enum ContextKind: String {
case service
}
/// `LDManager` is responsible for managing interactions with the LaunchDarkly service.
///
/// - Note: This class is a singleton and should be accessed via `LDManager.shared`.
class LDManager: LDManagerProtocol {
// MARK: - Properties
static let shared = LDManager()
private var logger: ForageLogger?
// MARK: - Initialization
private init() {}
/// Initializes the LaunchDarkly client with a given environment and optional logger.
///
/// - Parameters:
/// - environment: The `EnvironmentTarget` to be used for initialization.
/// - logger: An optional `ForageLogger` for logging purposes.
/// - startLdClient: A closure to start the LaunchDarkly client. Defaults to `LDClient.start`.
func initialize(
_ environment: Environment,
logger: ForageLogger? = nil,
startLdClient: (LDConfig, LDContext?, (() -> Void)?) -> Void = LDClient.start
) {
self.logger = logger?.setPrefix(LAUNCH_DARKLY_PREFIX)
let ldConfig = createLDConfig(for: environment)
guard let ldContext = createLDContext() else {
self.logger?.error("Failed to create LaunchDarkly context", error: nil, attributes: nil)
return
}
startLdClient(ldConfig, ldContext) { [weak self] in
self?.logger?.info("Initialized LaunchDarkly client", attributes: nil)
}
}
/// Determines the type of vault to be used based on the "vault-primary-traffic-percentage" feature flag.
/// Defaults to VGS if something goes wrong (ex: LDClient not initialized, LaunchDarkly is down).
///
/// - Parameters:
/// - ldClient: An optional `LDClientProtocol` object used to fetch feature flags. Defaults to `getDefaultLDClient()`.
/// - genRandomDouble: A closure that returns a random double between 0 and 100. Defaults to `generateRandomDouble`.
///
/// - Returns: The determined `VaultType`.
func getVaultType(
ldClient: LDClientProtocol?,
genRandomDouble: () -> Double
) -> VaultType {
logger = logger?.setPrefix(LAUNCH_DARKLY_PREFIX)
guard let ld = ldClient else {
logger?.error("Defaulting to VGS. LDClient.get() was called before init()!",
error: nil,
attributes: nil)
return .vgs
}
// Fetch the vault percentage from LaunchDarkly
let vaultPercentage = ld.doubleVariationWrapper(
forKey: FlagType.vaultPrimaryTrafficPercentage.rawValue,
defaultValue: 0.0
)
logger?.info("Evaluated \(FlagType.vaultPrimaryTrafficPercentage) = \(vaultPercentage)%",
attributes: nil)
let randomNum = genRandomDouble()
let vaultType: VaultType = (randomNum < vaultPercentage) ? .basisTheory : .vgs
logVaultType(vaultType)
return vaultType
}
private func createLDConfig(for environment: Environment) -> LDConfig {
LDConfig(mobileKey: getLDMobileKey(environment).rawValue, autoEnvAttributes: .enabled)
}
private func createLDContext() -> LDContext? {
var ldContextBuilder = LDContextBuilder(key: ContextKey.iosSdk.rawValue)
ldContextBuilder.kind(ContextKind.service.rawValue)
guard case let .success(context) = ldContextBuilder.build() else {
return nil
}
return context
}
private func logVaultType(_ vaultType: VaultType? = nil) {
guard let vaultType = vaultType else {
// this shouldn't happen, but we log here in case something went really wrong
logger?.error("Vault type was not set!", error: nil, attributes: nil)
return
}
_ = logger?
.addContext(ForageLogContext(
vaultType: vaultType
))
.notice("Using \(vaultType.rawValue)", attributes: nil)
}
static func getDefaultLDClient() -> LDClientProtocol? {
LDClient.get()
}
static func generateRandomDouble() -> Double {
Double.random(in: 0...100)
}
private func getLDMobileKey(_ environment: Environment) -> LDMobileKey {
switch environment {
case .sandbox: return .sandbox
case .cert: return .cert
case .prod: return .prod
case .staging: return .staging
case .dev: return .dev
}
}
}