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

feat: Storage check #61

Merged
merged 3 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
52 changes: 34 additions & 18 deletions Sources/ConfidenceProvider/Cache/DefaultStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,14 @@ public class DefaultStorage: Storage {
}

public func load<T>(defaultValue: T) throws -> T where T: Decodable {
try storageQueue.sync {
let configUrl = try getConfigUrl()
guard FileManager.default.fileExists(atPath: configUrl.backport.path) else {
return defaultValue
}

let data = try {
do {
return try Data(contentsOf: configUrl)
} catch {
throw ConfidenceError.cacheError(message: "Unable to load cache file: \(error)")
}
}()
guard let data = try read() else {
return defaultValue
}

do {
return try JSONDecoder().decode(T.self, from: data)
} catch {
throw ConfidenceError.corruptedCache(message: "Unable to decode: \(error)")
}
do {
return try JSONDecoder().decode(T.self, from: data)
} catch {
throw ConfidenceError.corruptedCache(message: "Unable to decode: \(error)")
}
}

Expand All @@ -65,6 +54,33 @@ public class DefaultStorage: Storage {
}
}

public func isEmpty() -> Bool {
guard let data = try? read() else {
return false
vahidlazio marked this conversation as resolved.
Show resolved Hide resolved
}

return data.isEmpty
}

func read() throws -> Data? {
try storageQueue.sync {
let configUrl = try getConfigUrl()
guard FileManager.default.fileExists(atPath: configUrl.backport.path) else {
return nil
}

let data = try {
do {
return try Data(contentsOf: configUrl)
} catch {
throw ConfidenceError.cacheError(message: "Unable to load cache file: \(error)")
}
}()

return data
}
}

func getConfigUrl() throws -> URL {
guard
let applicationSupportUrl = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
Expand Down
2 changes: 2 additions & 0 deletions Sources/ConfidenceProvider/Cache/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public protocol Storage {
func load<T>(defaultValue: T) throws -> T where T: Decodable

func clear() throws

func isEmpty() -> Bool
}
4 changes: 4 additions & 0 deletions Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ public class ConfidenceFeatureProvider: FeatureProvider {
}
}

public func isStorageEmpty() -> Bool {
storage.isEmpty()
}

public func onContextSet(
oldContext: OpenFeature.EvaluationContext?,
newContext: OpenFeature.EvaluationContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class CacheDataInteractorTests: XCTestCase {

Task {
// When cache data add method is called
await cacheDataInteractor.add(resolveToken: "token", flagName: "name", applyTime: Date())
_ = await cacheDataInteractor.add(resolveToken: "token", flagName: "name", applyTime: Date())

// Then event is added with
let cache = await cacheDataInteractor.cache
Expand All @@ -49,7 +49,7 @@ final class CacheDataInteractorTests: XCTestCase {

Task {
// When cache data add method is called
await cacheDataInteractor.add(resolveToken: "token", flagName: "name", applyTime: Date())
_ = await cacheDataInteractor.add(resolveToken: "token", flagName: "name", applyTime: Date())

// Then event is added with
let cache = await cacheDataInteractor.cache
Expand Down
20 changes: 10 additions & 10 deletions Tests/ConfidenceProviderTests/CacheDataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class CacheDataTests: XCTestCase {

// When add event is called
let applyTime = Date()
cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)
_ = cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)

// Then cache data has one resolve event
XCTAssertEqual(cacheData.resolveEvents.count, 1)
Expand All @@ -29,7 +29,7 @@ final class CacheDataTests: XCTestCase {
var cacheData = CacheData(resolveToken: "token1", events: [])

// When add event is called with token that already exist in cache data
cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)
_ = cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)

// Then cache data has one resolve event
XCTAssertEqual(cacheData.resolveEvents.count, 1)
Expand All @@ -46,9 +46,9 @@ final class CacheDataTests: XCTestCase {
var cacheData = try CacheDataUtility.prefilledCacheData()

// When add event is called 3 times with token that already exist in cache data
cacheData.add(resolveToken: "token0", flagName: "flagName", applyTime: Date())
cacheData.add(resolveToken: "token0", flagName: "flagName2", applyTime: Date())
cacheData.add(resolveToken: "token0", flagName: "flagName3", applyTime: Date())
_ = cacheData.add(resolveToken: "token0", flagName: "flagName", applyTime: Date())
_ = cacheData.add(resolveToken: "token0", flagName: "flagName2", applyTime: Date())
_ = cacheData.add(resolveToken: "token0", flagName: "flagName3", applyTime: Date())

// Then cache data has 6 apply events
XCTAssertEqual(cacheData.resolveEvents.first?.events.count, 6)
Expand All @@ -58,11 +58,11 @@ final class CacheDataTests: XCTestCase {
// Given pre filled cache data
let applyTime = Date(timeIntervalSince1970: 1000)
var cacheData = CacheData(resolveToken: "token1", events: [])
cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)
_ = cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTime)

// When add event is called with token and flagName that already exist in cache
let applyTimeOther = Date(timeIntervalSince1970: 3000)
cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTimeOther)
_ = cacheData.add(resolveToken: "token1", flagName: "flagName", applyTime: applyTimeOther)

// Then apply record is not overriden
let applyEvent = try XCTUnwrap(cacheData.resolveEvents.first?.events.first)
Expand All @@ -75,9 +75,9 @@ final class CacheDataTests: XCTestCase {
let date = Date(timeIntervalSince1970: 2000)

// When add event is called 3 times with different tokens
cacheData.add(resolveToken: "token1", flagName: "prefilled", applyTime: date)
cacheData.add(resolveToken: "token2", flagName: "prefilled", applyTime: date)
cacheData.add(resolveToken: "token3", flagName: "prefilled", applyTime: date)
_ = cacheData.add(resolveToken: "token1", flagName: "prefilled", applyTime: date)
_ = cacheData.add(resolveToken: "token2", flagName: "prefilled", applyTime: date)
_ = cacheData.add(resolveToken: "token3", flagName: "prefilled", applyTime: date)

// Then cache data has 4 resolve event
XCTAssertEqual(cacheData.resolveEvents.count, 4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum CacheDataUtility {
let applyEvent = FlagApply(name: flagName, applyTime: date)
applyEvents.append(applyEvent)
} else {
cacheData.add(resolveToken: resolveToken, flagName: flagName, applyTime: date)
_ = cacheData.add(resolveToken: resolveToken, flagName: flagName, applyTime: date)
}
}

Expand Down
28 changes: 20 additions & 8 deletions Tests/ConfidenceProviderTests/Helpers/StorageMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,41 @@ import XCTest

class StorageMock: Storage {
var data = ""

var saveExpectation: XCTestExpectation?
private let storageQueue = DispatchQueue(label: "com.confidence.storagemock")

convenience init(data: Encodable) throws {
self.init()
try self.save(data: data)
}

func save(data: Encodable) throws {
let dataB = try JSONEncoder().encode(data)
self.data = String(data: dataB, encoding: .utf8) ?? ""
try storageQueue.sync {
let dataB = try JSONEncoder().encode(data)
self.data = String(data: dataB, encoding: .utf8) ?? ""

saveExpectation?.fulfill()
saveExpectation?.fulfill()
}
}

func load<T>(defaultValue: T) throws -> T where T: Decodable {
if data.isEmpty {
return defaultValue
try storageQueue.sync {
if data.isEmpty {
return defaultValue
}
return try JSONDecoder().decode(T.self, from: data.data)
}
return try JSONDecoder().decode(T.self, from: data.data)
}

func clear() throws {
data = ""
storageQueue.sync {
data = ""
}
}

func isEmpty() -> Bool {
storageQueue.sync {
return data.isEmpty
}
}
}
Loading