Added logic to use the setting if it's already been sent in a config

Added the ability to define requirements for migrations (in case some data or state needs to be loaded for a migration to be able to be performed correctly)
This commit is contained in:
Morgan Pretty 2023-08-11 18:48:14 +10:00
parent 9c9fb09254
commit ef5aa927a0
8 changed files with 105 additions and 1 deletions

View File

@ -517,6 +517,7 @@
FD1C98E4282E3C5B00B76F9E /* UINavigationBar+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C98E3282E3C5B00B76F9E /* UINavigationBar+Utilities.swift */; };
FD1D732A2A85AA2000E3F410 /* Setting+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1D73292A85AA2000E3F410 /* Setting+Utilities.swift */; };
FD1D732E2A86114600E3F410 /* _015_BlockCommunityMessageRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1D732D2A86114600E3F410 /* _015_BlockCommunityMessageRequests.swift */; };
FD1F9C9F2A862BE60050F671 /* MigrationRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1F9C9E2A862BE60050F671 /* MigrationRequirement.swift */; };
FD23CE1B2A651E6D0000B97C /* NetworkType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE1A2A651E6D0000B97C /* NetworkType.swift */; };
FD23CE1F2A65269C0000B97C /* Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE1E2A65269C0000B97C /* Crypto.swift */; };
FD23CE222A661D000000B97C /* OpenGroupAPI+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE212A661D000000B97C /* OpenGroupAPI+Crypto.swift */; };
@ -1675,6 +1676,7 @@
FD1C98E3282E3C5B00B76F9E /* UINavigationBar+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Utilities.swift"; sourceTree = "<group>"; };
FD1D73292A85AA2000E3F410 /* Setting+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Setting+Utilities.swift"; sourceTree = "<group>"; };
FD1D732D2A86114600E3F410 /* _015_BlockCommunityMessageRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _015_BlockCommunityMessageRequests.swift; sourceTree = "<group>"; };
FD1F9C9E2A862BE60050F671 /* MigrationRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationRequirement.swift; sourceTree = "<group>"; };
FD23CE1A2A651E6D0000B97C /* NetworkType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkType.swift; sourceTree = "<group>"; };
FD23CE1E2A65269C0000B97C /* Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Crypto.swift; sourceTree = "<group>"; };
FD23CE212A661D000000B97C /* OpenGroupAPI+Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupAPI+Crypto.swift"; sourceTree = "<group>"; };
@ -3671,6 +3673,7 @@
children = (
FD17D7BE27F51F8200122BE0 /* ColumnExpressible.swift */,
FD17D7B727F51ECA00122BE0 /* Migration.swift */,
FD1F9C9E2A862BE60050F671 /* MigrationRequirement.swift */,
FD17D7B927F51F2100122BE0 /* TargetMigrations.swift */,
FD17D7C027F5200100122BE0 /* TypedTableDefinition.swift */,
FD37EA1028AB34B3003AE748 /* TypedTableAlteration.swift */,
@ -5678,6 +5681,7 @@
FDB7400B28EB99A70094D718 /* TimeInterval+Utilities.swift in Sources */,
C3D9E35E25675F640040E4F3 /* OWSFileSystem.m in Sources */,
FD09797D27FBDB2000936362 /* Notification+Utilities.swift in Sources */,
FD1F9C9F2A862BE60050F671 /* MigrationRequirement.swift in Sources */,
FDC6D7602862B3F600B04575 /* Dependencies.swift in Sources */,
C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */,
FD17D7C727F5207C00122BE0 /* DatabaseMigrator+Utilities.swift in Sources */,

View File

@ -10,6 +10,7 @@ enum _015_BlockCommunityMessageRequests: Migration {
static let identifier: String = "BlockCommunityMessageRequests"
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.01
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]
static func migrate(_ db: Database) throws {
// Add the new 'Profile' properties
@ -25,7 +26,17 @@ enum _015_BlockCommunityMessageRequests: Migration {
Identity.userExists(db),
(try Setting.exists(db, id: Setting.BoolKey.checkForCommunityMessageRequests.rawValue)) == false
{
db[.checkForCommunityMessageRequests] = true
let rawBlindedMessageRequestValue: Int32 = try SessionUtil
.config(for: .userProfile, publicKey: getUserHexEncodedPublicKey(db))
.wrappedValue
.map { conf -> Int32 in try SessionUtil.rawBlindedMessageRequestValue(in: conf) }
.defaulting(to: -1)
// Use the value in the config if we happen to have one, otherwise use the default
db[.checkForCommunityMessageRequests] = (rawBlindedMessageRequestValue < 0 ?
true :
(rawBlindedMessageRequestValue > 0)
)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration

View File

@ -210,6 +210,22 @@ internal extension SessionUtil {
return updated
}
static func hasSetting(_ db: Database, forKey key: String) throws -> Bool {
let userPublicKey: String = getUserHexEncodedPublicKey(db)
// Currently the only synced setting is 'checkForCommunityMessageRequests'
switch key {
case Setting.BoolKey.checkForCommunityMessageRequests.rawValue:
return try SessionUtil
.config(for: .userProfile, publicKey: userPublicKey)
.wrappedValue
.map { conf -> Bool in (try SessionUtil.rawBlindedMessageRequestValue(in: conf) >= 0) }
.defaulting(to: false)
default: return false
}
}
static func updatingSetting(_ db: Database, _ updated: Setting?) throws {
// Don't current support any nullable settings
guard let updatedSetting: Setting = updated else { return }

View File

@ -186,3 +186,13 @@ internal extension SessionUtil {
}
}
}
// MARK: - Direct Values
extension SessionUtil {
static func rawBlindedMessageRequestValue(in conf: UnsafeMutablePointer<config_object>?) throws -> Int32 {
guard conf != nil else { throw SessionUtilError.nilConfigObject }
return user_profile_get_blinded_msgreqs(conf)
}
}

View File

@ -47,8 +47,10 @@ open class Storage {
fileprivate var dbWriter: DatabaseWriter?
internal var testDbWriter: DatabaseWriter? { dbWriter }
private var unprocessedMigrationRequirements: Atomic<[MigrationRequirement]> = Atomic(MigrationRequirement.allCases)
private var migrator: DatabaseMigrator?
private var migrationProgressUpdater: Atomic<((String, CGFloat) -> ())>?
private var migrationRequirementProcesser: Atomic<(Database?, MigrationRequirement) -> ()>?
// MARK: - Initialization
@ -77,6 +79,7 @@ open class Storage {
migrationTargets: (customMigrationTargets ?? []),
async: false,
onProgressUpdate: nil,
onMigrationRequirement: { _, _ in },
onComplete: { _, _ in }
)
return
@ -148,6 +151,7 @@ open class Storage {
migrationTargets: [MigratableTarget.Type],
async: Bool = true,
onProgressUpdate: ((CGFloat, TimeInterval) -> ())?,
onMigrationRequirement: @escaping (Database?, MigrationRequirement) -> (),
onComplete: @escaping (Swift.Result<Void, Error>, Bool) -> ()
) {
guard isValid, let dbWriter: DatabaseWriter = dbWriter else {
@ -232,13 +236,24 @@ open class Storage {
onProgressUpdate?(totalProgress, totalMinExpectedDuration)
}
})
self.migrationRequirementProcesser = Atomic(onMigrationRequirement)
// Store the logic to run when the migration completes
let migrationCompleted: (Swift.Result<Void, Error>) -> () = { [weak self] result in
// Process any unprocessed requirements which need to be processed before completion
// then clear out the state
self?.unprocessedMigrationRequirements.wrappedValue
.filter { $0.shouldProcessAtCompletionIfNotRequired }
.forEach { self?.migrationRequirementProcesser?.wrappedValue(nil, $0) }
self?.migrationsCompleted.mutate { $0 = true }
self?.migrationProgressUpdater = nil
self?.migrationRequirementProcesser = nil
SUKLegacy.clearLegacyDatabaseInstance()
// Reset in case there is a requirement on a migration which runs when returning from
// the background
self?.unprocessedMigrationRequirements.mutate { $0 = MigrationRequirement.allCases }
// Don't log anything in the case of a 'success' or if the database is suspended (the
// latter will happen if the user happens to return to the background too quickly on
// launch so is unnecessarily alarming, it also gets caught and logged separately by
@ -288,6 +303,22 @@ open class Storage {
}
}
public func willStartMigration(_ db: Database, _ migration: Migration.Type) {
let unprocessedRequirements: Set<MigrationRequirement> = migration.requirements.asSet()
.intersection(unprocessedMigrationRequirements.wrappedValue.asSet())
// No need to do anything if there are no unprocessed requirements
guard !unprocessedRequirements.isEmpty else { return }
// Process all of the requirements for this migration
unprocessedRequirements.forEach { migrationRequirementProcesser?.wrappedValue(db, $0) }
// Remove any processed requirements from the list (don't want to process them multiple times)
unprocessedMigrationRequirements.mutate {
$0 = Array($0.asSet().subtracting(migration.requirements.asSet()))
}
}
public static func update(
progress: CGFloat,
for migration: Migration.Type,

View File

@ -8,17 +8,21 @@ public protocol Migration {
static var identifier: String { get }
static var needsConfigSync: Bool { get }
static var minExpectedRunDuration: TimeInterval { get }
static var requirements: [MigrationRequirement] { get }
static func migrate(_ db: Database) throws
}
public extension Migration {
static var requirements: [MigrationRequirement] { [] }
static func loggedMigrate(
_ storage: Storage?,
targetIdentifier: TargetMigrations.Identifier
) -> ((_ db: Database) throws -> ()) {
return { (db: Database) in
SNLogNotTests("[Migration Info] Starting \(targetIdentifier.key(with: self))")
storage?.willStartMigration(db, self)
storage?.internalCurrentlyRunningMigration.mutate { $0 = (targetIdentifier, self) }
defer { storage?.internalCurrentlyRunningMigration.mutate { $0 = nil } }

View File

@ -0,0 +1,12 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
public enum MigrationRequirement: CaseIterable {
case sessionUtilStateLoaded
var shouldProcessAtCompletionIfNotRequired: Bool {
switch self {
case .sessionUtilStateLoaded: return true
}
}
}

View File

@ -82,6 +82,20 @@ public enum AppSetup {
SNUIKit.self
],
onProgressUpdate: migrationProgressChanged,
onMigrationRequirement: { db, requirement in
switch requirement {
case .sessionUtilStateLoaded:
guard Identity.userExists(db) else { return }
// After the migrations have run but before the migration completion we load the
// SessionUtil state
SessionUtil.loadState(
db,
userPublicKey: getUserHexEncodedPublicKey(db),
ed25519SecretKey: Identity.fetchUserEd25519KeyPair(db)?.secretKey
)
}
},
onComplete: { result, needsConfigSync in
// After the migrations have run but before the migration completion we load the
// SessionUtil state and update the 'needsConfigSync' flag based on whether the
@ -93,6 +107,8 @@ public enum AppSetup {
)
}
// The 'needsConfigSync' flag should be based on whether either a migration or the
// configs need to be sync'ed
migrationsCompletion(result, (needsConfigSync || SessionUtil.needsSync))
// The 'if' is only there to prevent the "variable never read" warning from showing