fix: logic of deletion

This commit is contained in:
Nikita Semenov 2023-07-07 14:00:43 +03:00
parent c631053131
commit a79ff67a38
6 changed files with 97 additions and 68 deletions

View File

@ -32,16 +32,7 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS
where StoreContent == Value? {
let getClosure: GetClosure = { container in
// Storage container can't handle migration when source storage has value and target doesn't.
// This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)`
// and absence of encoder.
try? container.codableObject(forKey: key, decoder: decoder)
.flatMap {
let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder)
return .success($0)
}
.get()
Self.getValue(from: container, forKey: key, decoder: decoder, encoder: encoder)
}
let setClosure: SetClosure = { container, value in
@ -59,20 +50,7 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS
where StoreContent == Value? {
let getClosure: GetClosure = { container in
// Storage container can't handle migration when source storage has value and target doesn't.
// This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)`
// and absence of encoder.
try? container.codableObject(forKey: key, decoder: decoder)
.flatMap {
let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder)
return .success($0)
}
.flatMapError { _ in
container.set(encodableObject: wrappedValue, forKey: key, encoder: encoder)
.flatMap { _ in .success(wrappedValue) }
}
.get()
Self.getValue(from: container, forKey: key, wrappedValue: wrappedValue, decoder: decoder, encoder: encoder)
}
let setClosure: SetClosure = { container, value in
@ -81,4 +59,30 @@ public extension MigratingBackingStore where MigratingStorages: CodableKeyValueS
self.init(storageContainer: storageContainer, getClosure: getClosure, setClosure: setClosure)
}
static func getValue<Value: Codable>(from container: MigratingStorages,
forKey key: StorageKey<Value>,
wrappedValue: Value? = nil,
decoder: CodableKeyValueDecoder,
encoder: CodableKeyValueEncoder) -> StoreContent where StoreContent == Value? {
// Storage container can't handle migration when source storage has value and target doesn't.
// This happens because of Decodable restrictions for Value in method `codableObject(forKey:decoder)`
// and absence of encoder.
try? container.codableObject(forKey: key, decoder: decoder)
.flatMap {
let _ = container.set(encodableObject: $0, forKey: key, encoder: encoder)
return .success($0)
}
.flatMapError {
guard let wrappedValue else {
return .failure($0)
}
return container.set(encodableObject: wrappedValue, forKey: key, encoder: encoder)
.flatMap { _ in .success(wrappedValue) }
}
.get()
}
}

View File

@ -44,16 +44,14 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora
targetStorage.codableObject(forKey: key, decoder: decoder)
.flatMap {
if case let .failure(error) = removeSourceValue(forKey: key), !error.isValueNotFound {
errorLogger.log(error: error, file: #file, line: #line)
if case let .failure(error) = removeSourceValue(forKey: key) {
logErrorIfNeeded(error, file: #file, line: #line)
}
return .success($0)
}
.flatMapError {
if !$0.isValueNotFound {
errorLogger.log(error: $0, file: #file, line: #line)
}
logErrorIfNeeded($0, file: #file, line: #line)
return sourceStorage.codableObject(forKey: key, decoder: decoder)
}
@ -63,33 +61,46 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora
forKey key: StorageKey<Value>,
encoder: CodableKeyValueEncoder) -> Result<Void, StorageError> {
if case let .failure(error) = removeSourceValue(forKey: key), !error.isValueNotFound {
errorLogger.log(error: error, file: #file, line: #line)
if case let .failure(error) = removeSourceValue(forKey: key) {
logErrorIfNeeded(error, file: #file, line: #line)
}
return targetStorage.set(encodableObject: encodableObject, forKey: key, encoder: encoder)
}
open func removeCodableValue<Value: Codable>(forKey key: StorageKey<Value>) -> Result<Void, StorageError> {
targetStorage.removeCodableValue(forKey: key)
.flatMap { value in
sourceStorage.removeCodableValue(forKey: key)
.flatMapError { _ in .success(value) }
}
.flatMapError {
if !$0.isValueNotFound {
errorLogger.log(error: $0, file: #file, line: #line)
}
switch (targetStorage.removeCodableValue(forKey: key), sourceStorage.removeCodableValue(forKey: key)) {
case (.success, .success):
return .success(())
return removeSourceValue(forKey: key)
}
case let (.success, .failure(error)):
logErrorIfNeeded(error, file: #file, line: #line)
return .success(())
case let (.failure(error), .success):
return .failure(error)
case let (.failure(targetError), .failure(sourceError)):
logErrorIfNeeded(sourceError, file: #file, line: #line)
return .failure(targetError)
}
}
open func hasCodableValue<Value: Decodable>(forKey key: StorageKey<Value>) -> Result<Bool, StorageError> {
targetStorage.hasCodableValue(forKey: key)
.flatMapError { _ in
sourceStorage.hasCodableValue(forKey: key)
}
switch (targetStorage.hasCodableValue(forKey: key), sourceStorage.hasCodableValue(forKey: key)) {
case let (.success(value), _):
return .success(value)
case let (.failure, .success(value)):
return .success(value)
case let (.failure(targetError), .failure(sourceError)):
logErrorIfNeeded(sourceError, file: #file, line: #line)
return .failure(targetError)
}
}
// MARK: - Open methods
@ -98,4 +109,14 @@ where SourceStorage: CodableKeyValueStorage, TargetStorage: CodableKeyValueStora
// override in subclasses
.failure(.valueNotFound)
}
// MARK: - Private methods
private func logErrorIfNeeded(_ error: StorageError, file: StaticString, line: Int) {
guard !error.isValueNotFound else {
return
}
errorLogger.log(error: error, file: file, line: line)
}
}

View File

@ -36,7 +36,7 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType,
public init(sourceStorage: SourceStorage,
targetStorage: TargetStorage,
errorLogger: ErrorLogger = TIFoundationLogger(category: "BaseMigratingSingleValueStorage")) {
errorLogger: ErrorLogger = TIFoundationLogger(category: "MigratingSingleValueStorage")) {
self.sourceStorage = sourceStorage
self.targetStorage = targetStorage
@ -50,8 +50,8 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType,
}
public func store(value: ValueType) -> Result<Void, StorageError> {
if case let .failure(error) = sourceStorage.deleteValue(), !error.isValueNotFound {
errorLogger.log(error: error, file: #file, line: #line)
if case let .failure(error) = sourceStorage.deleteValue() {
logErrorIfNeeded(error, file: #file, line: #line)
}
return targetStorage.store(value: value)
@ -60,8 +60,8 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType,
public func getValue() -> Result<ValueType, StorageError> {
targetStorage.getValue()
.flatMap {
if case let .failure(error) = sourceStorage.deleteValue(), !error.isValueNotFound {
errorLogger.log(error: error, file: #file, line: #line)
if case let .failure(error) = sourceStorage.deleteValue() {
logErrorIfNeeded(error, file: #file, line: #line)
}
return .success($0)
@ -85,29 +85,33 @@ where SourceStorage.ErrorType == TargetStorage.ErrorType,
return .success(())
case let (.success, .failure(error)):
if error.isValueNotFound {
return .success(())
}
logErrorIfNeeded(error, file: #file, line: #line)
errorLogger.log(error: error, file: #file, line: #line)
return .failure(error)
return .success(())
case let (.failure(error), .success):
if error.isValueNotFound {
return .success(())
}
errorLogger.log(error: error, file: #file, line: #line)
return .failure(error)
case let (.failure, .failure(error)):
return .failure(error)
case let (.failure(targetError), .failure(sourceError)):
logErrorIfNeeded(sourceError, file: #file, line: #line)
return .failure(targetError)
}
}
// MARK: - Private methods
private func logErrorIfNeeded(_ error: StorageError, file: StaticString, line: Int) {
guard !error.isValueNotFound else {
return
}
errorLogger.log(error: error, file: file, line: line)
}
}
// MARK: - Factory method
public extension SingleValueStorage where ErrorType == StorageError {
func migrating<S: SingleValueStorage>(to targetStorage: S) -> MigratingSingleValueStorage<Self, S>

View File

@ -118,7 +118,7 @@ case let .failure(storageError):
/*:
### MigratingStorage
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
- source storage: хранилище с которого мигрируем
- target storage: хранилище на которое мигрируем

View File

@ -185,7 +185,7 @@ final class MigratingSingleValueStorageTests: XCTestCase {
let _ = sourceRefreshTokenStorage.store(value: oldToken)
XCTAssertNoThrow(try refreshToken.deleteValue().get())
XCTAssertThrowsError(try refreshToken.deleteValue().get())
XCTAssertNil(MockStorageLogger.defaultLogger.getError())
XCTAssertThrowsError(try sourceRefreshTokenStorage.getValue().get())
XCTAssertThrowsError(try targetRefreshTokenStorage.getValue().get())

View File

@ -116,7 +116,7 @@ case let .failure(storageError):
### MigratingStorage
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
- source storage: хранилище с которого мигрируем
- target storage: хранилище на которое мигрируем