Sources/Confidence/Apply/CacheData.swift (131 lines of code) (raw):
import Foundation
/// `CacheData` represents object that encapsulates exposure events for evaluated flags.
/// It holds information related to apply event i.e. resolve token, flag name, timestamp .
/// This object is used for tracking exposure events, i.e. by storing them on disk.
struct CacheData: Codable {
var resolveEvents: [ResolveApply]
var isEmpty: Bool {
resolveEvents.isEmpty || resolveEvents.allSatisfy { $0.isEmpty }
}
init(resolveToken: String, flagName: String, applyTime: Date) {
resolveEvents = [
ResolveApply(resolveToken: resolveToken, flagName: flagName, applyTime: applyTime)
]
}
init(resolveToken: String, events: [FlagApply]) {
resolveEvents = [ResolveApply(resolveToken: resolveToken, events: events)]
}
private init(resolveEvents: [ResolveApply]) {
self.resolveEvents = resolveEvents
}
static func convertInTransit(cache: CacheData) -> CacheData {
var mutatedCache = cache
for resolveIndex in 0..<mutatedCache.resolveEvents.count {
for eventIndex in 0..<mutatedCache.resolveEvents[resolveIndex].events.count
where mutatedCache.resolveEvents[resolveIndex].events[eventIndex].status == .sending {
mutatedCache.resolveEvents[resolveIndex].events[eventIndex].status = .created
}
}
return mutatedCache
}
static func empty() -> CacheData {
CacheData(resolveEvents: [])
}
func applyEventExists(resolveToken: String, name: String) -> Bool {
let resolveTokenIndex = applyEventIndex(resolveToken: resolveToken, name: name)
return resolveTokenIndex != nil
}
mutating func setEventStatus(resolveToken: String, name: String, status: ApplyEventStatus = .sent) {
let flagEventIndexes = flagEventIndex(resolveToken: resolveToken, name: name)
guard let resolveIndex = flagEventIndexes.resolveEventIndex,
let flagIndex = flagEventIndexes.flagEventIndex
else {
return
}
resolveEvents[resolveIndex].events[flagIndex].status = status
}
mutating func setEventStatus(resolveToken: String, status: ApplyEventStatus = .sent) {
guard let resolveIndex = resolveEventIndex(resolveToken: resolveToken) else {
return
}
for i in 0..<resolveEvents[resolveIndex].events.count {
resolveEvents[resolveIndex].events[i].status = status
}
}
mutating func add(resolveToken: String, flagName: String, applyTime: Date) -> Bool {
let resolveEventIndex = resolveEventIndex(resolveToken: resolveToken)
if let resolveEventIndex {
// Resolve apply event with given resolve token exists
let resolveEvent = resolveEvents[resolveEventIndex]
let flagEventIndex = resolveEvent.events.firstIndex { flagEvent in
flagEvent.name == flagName
}
if flagEventIndex == nil {
// No flag apply event for given resolve token, adding new record
let flagEvent = FlagApply(name: flagName, applyTime: applyTime)
resolveEvents[resolveEventIndex].events.append(flagEvent)
return true
}
} else {
// No resolve event for given resolve token, adding new record
let event = ResolveApply(resolveToken: resolveToken, flagName: flagName, applyTime: applyTime)
resolveEvents.append(event)
return true
}
return false
}
mutating func remove(resolveToken: String) {
let resolveEventIndex = resolveEvents.firstIndex { resolveEvent in
resolveEvent.resolveToken == resolveToken
}
guard let resolveEventIndex else {
return
}
resolveEvents.remove(at: resolveEventIndex)
}
mutating func remove(resolveToken: String, flagName: String) {
let resolveEventIndex = resolveEvents.firstIndex { resolveEvent in
resolveEvent.resolveToken == resolveToken
}
guard let resolveEventIndex else {
return
}
let resolveEvent = resolveEvents[resolveEventIndex]
let flagEventIndex = resolveEvent.events.firstIndex { event in
event.name == flagName
}
guard let flagEventIndex else {
return
}
// Flag apply event with given flag name exists, cleaning it up
resolveEvents[resolveEventIndex].events.remove(at: flagEventIndex)
if resolveEvents[resolveEventIndex].isEmpty {
resolveEvents.remove(at: resolveEventIndex)
}
}
func flagEvent(resolveToken: String, name: String) -> FlagApply? {
guard let resolveTokenIndex = resolveEventIndex(resolveToken: resolveToken),
let flagEventIndex = applyEventIndex(resolveToken: resolveToken, name: name)
else {
return nil
}
return resolveEvents[resolveTokenIndex].events[flagEventIndex]
}
// MARK: Private
private func flagEventIndex(resolveToken: String, name: String) -> (resolveEventIndex: Int?, flagEventIndex: Int?) {
guard let resolveTokenIndex = resolveEventIndex(resolveToken: resolveToken) else {
return (nil, nil)
}
guard let flagEventIndex = applyEventIndex(resolveToken: resolveToken, name: name) else {
return (resolveTokenIndex, nil)
}
return (resolveTokenIndex, flagEventIndex)
}
private func resolveEventIndex(resolveToken: String) -> Int? {
let resolveTokenIndex = resolveEvents.firstIndex { resolveEvent in
resolveEvent.resolveToken == resolveToken
}
return resolveTokenIndex
}
private func applyEventIndex(resolveToken: String, name: String) -> Int? {
guard let resolveTokenIndex = resolveEventIndex(resolveToken: resolveToken) else {
return nil
}
let flagEventIndex = resolveEvents[resolveTokenIndex].events.firstIndex { applyEvent in
applyEvent.name == name
}
return flagEventIndex
}
}