fix: logic of deletion
This commit is contained in:
parent
c631053131
commit
a79ff67a38
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ case let .failure(storageError):
|
|||
/*:
|
||||
### MigratingStorage
|
||||
|
||||
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
|
||||
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
|
||||
|
||||
- source storage: хранилище с которого мигрируем
|
||||
- target storage: хранилище на которое мигрируем
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ case let .failure(storageError):
|
|||
|
||||
### MigratingStorage
|
||||
|
||||
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `BaseMigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
|
||||
При необходимости мигрировать с одного keychain на другой можно воспользоваться классом `MigratingSingleValueStorage`. При создании "мигрирующего" хранилища необходимо будет указать:
|
||||
|
||||
- source storage: хранилище с которого мигрируем
|
||||
- target storage: хранилище на которое мигрируем
|
||||
|
|
|
|||
Loading…
Reference in New Issue