Skip to content

Commit

Permalink
Merge branch 'main' into AMP-63197-migrate-legacy-data
Browse files Browse the repository at this point in the history
# Conflicts:
#	Amplitude-Swift.xcodeproj/project.pbxproj
#	Sources/Amplitude/Amplitude.swift
  • Loading branch information
falconandy committed Jul 19, 2023
2 parents f0514a2 + 74992ce commit b36587f
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 54 deletions.
21 changes: 15 additions & 6 deletions Amplitude-Swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
8EDEC14255F82E24CEE00B36 /* AmplitudeSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */; };
8EDEC2E0CC80DF79F5463ACC /* RemnantDataMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC6A9899998F823C278F7 /* RemnantDataMigrationTests.swift */; };
8EDEC4EE0DE1C89889F451B5 /* QueueTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC4F83BFAA664749FAEF0 /* QueueTimeTests.swift */; };
8EDEC972AEB33E4528F7FEEB /* StoragePrefixMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */; };
8EDECD602E181B3E2E85D4DF /* StoragePrefixMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDECC2335BCF4C2EC3A6206 /* StoragePrefixMigration.swift */; };
8EDEC8F8DD2CDCD6568512F8 /* RemnantDataMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */; };
8EDECFCCF4219767F26210D6 /* Sessions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */; };
BA0359CA2A51585D007C383B /* legacy_v3.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = BA0359C92A51585D007C383B /* legacy_v3.sqlite */; };
Expand Down Expand Up @@ -111,6 +113,8 @@
8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemnantDataMigration.swift; sourceTree = "<group>"; };
8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sessions.swift; sourceTree = "<group>"; };
8EDEC4F83BFAA664749FAEF0 /* QueueTimeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueTimeTests.swift; sourceTree = "<group>"; };
8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoragePrefixMigrationTests.swift; sourceTree = "<group>"; };
8EDECC2335BCF4C2EC3A6206 /* StoragePrefixMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoragePrefixMigration.swift; sourceTree = "<group>"; };
8EDEC6A9899998F823C278F7 /* RemnantDataMigrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemnantDataMigrationTests.swift; sourceTree = "<group>"; };
BA0359C92A51585D007C383B /* legacy_v3.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_v3.sqlite; sourceTree = "<group>"; };
BA0639F52A4DD491000F1CEE /* LegacyDatabaseStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyDatabaseStorage.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -200,18 +204,20 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
BA0639F42A4DD46E000F1CEE /* Migration */ = {
8EDEC33A32439724A363C433 /* Migration */ = {
isa = PBXGroup;
children = (
8EDECC2335BCF4C2EC3A6206 /* StoragePrefixMigration.swift */,
BA0639F52A4DD491000F1CEE /* LegacyDatabaseStorage.swift */,
8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */,
);
path = Migration;
sourceTree = "<group>";
};
BA994B972A4F484E00D0913F /* Migration */ = {
8EDECBC5925DC68913C7CB89 /* Migration */ = {
isa = PBXGroup;
children = (
8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */,
BA0359C92A51585D007C383B /* legacy_v3.sqlite */,
BA994B9B2A4F4B7500D0913F /* legacy_v4.sqlite */,
BA994B992A4F48DE00D0913F /* LegacyDatabaseStorageTests.swift */,
Expand Down Expand Up @@ -307,7 +313,7 @@
path = Utilities;
sourceTree = "<group>";
};
OBJ_5 = {
OBJ_5 /* */ = {
isa = PBXGroup;
children = (
OBJ_6 /* Package.swift */,
Expand All @@ -320,12 +326,12 @@
OBJ_81 /* CONTRIBUTING.md */,
OBJ_82 /* AmplitudeSwift.podspec */,
);
name = "";
sourceTree = "<group>";
};
OBJ_53 /* Tests */ = {
isa = PBXGroup;
children = (
BA994B972A4F484E00D0913F /* Migration */,
OBJ_54 /* AmplitudeTests.swift */,
OBJ_55 /* ConfigurationTests.swift */,
OBJ_56 /* ConsoleLoggerTests.swift */,
Expand All @@ -336,6 +342,7 @@
OBJ_69 /* TypesTests.swift */,
OBJ_70 /* Utilities */,
8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */,
8EDECBC5925DC68913C7CB89 /* Migration */,
);
name = Tests;
path = Tests/AmplitudeTests;
Expand Down Expand Up @@ -373,7 +380,6 @@
OBJ_7 /* Sources */ = {
isa = PBXGroup;
children = (
BA0639F42A4DD46E000F1CEE /* Migration */,
OBJ_8 /* Amplitude.swift */,
OBJ_9 /* Configuration.swift */,
OBJ_10 /* ConsoleLogger.swift */,
Expand All @@ -389,6 +395,7 @@
OBJ_42 /* Types.swift */,
OBJ_43 /* Utilities */,
8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */,
8EDEC33A32439724A363C433 /* Migration */,
);
name = Sources;
path = Sources/Amplitude;
Expand Down Expand Up @@ -483,7 +490,7 @@
knownRegions = (
en,
);
mainGroup = OBJ_5;
mainGroup = OBJ_5 /* */;
productRefGroup = OBJ_75 /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down Expand Up @@ -541,6 +548,7 @@
OBJ_158 /* UrlExtensionTests.swift in Sources */,
8EDEC4EE0DE1C89889F451B5 /* QueueTimeTests.swift in Sources */,
8EDEC14255F82E24CEE00B36 /* AmplitudeSessionTests.swift in Sources */,
8EDEC972AEB33E4528F7FEEB /* StoragePrefixMigrationTests.swift in Sources */,
BA994B9A2A4F48DE00D0913F /* LegacyDatabaseStorageTests.swift in Sources */,
8EDEC2E0CC80DF79F5463ACC /* RemnantDataMigrationTests.swift in Sources */,
);
Expand Down Expand Up @@ -589,6 +597,7 @@
OBJ_122 /* QueueTimer.swift in Sources */,
OBJ_124 /* UrlExtension.swift in Sources */,
8EDECFCCF4219767F26210D6 /* Sessions.swift in Sources */,
8EDECD602E181B3E2E85D4DF /* StoragePrefixMigration.swift in Sources */,
8EDEC8F8DD2CDCD6568512F8 /* RemnantDataMigration.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
21 changes: 21 additions & 0 deletions Sources/Amplitude/Amplitude.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,19 @@ public class Amplitude {
let contextPlugin = ContextPlugin()
self.contextPlugin = contextPlugin

migrateApiKeyStorages()

if configuration.migrateLegacyData {
RemnantDataMigration(self).execute()
}

if let deviceId: String? = configuration.storageProvider.read(key: .DEVICE_ID) {
state.deviceId = deviceId
}
if let userId: String? = configuration.storageProvider.read(key: .USER_ID) {
state.userId = userId
}

// required plugin for specific platform, only has lifecyclePlugin now
if let requiredPlugin = VendorSystem.current.requiredPlugin {
_ = add(plugin: requiredPlugin)
Expand Down Expand Up @@ -302,4 +311,16 @@ public class Amplitude {
_ = self.flush()
}
}

private func migrateApiKeyStorages() {
if let persistentStorage = configuration.storageProvider as? PersistentStorage {
let apiKeyStorage = PersistentStorage(storagePrefix: "\(PersistentStorage.DEFAULT_STORAGE_PREFIX)-\(configuration.apiKey)")
StoragePrefixMigration(source: apiKeyStorage, destination: persistentStorage, logger: logger).execute()
}

if let persistentIdentifyStorage = configuration.identifyStorageProvider as? PersistentStorage {
let apiKeyIdentifyStorage = PersistentStorage(storagePrefix: "\(PersistentStorage.DEFAULT_STORAGE_PREFIX)-identify-\(configuration.apiKey)")
StoragePrefixMigration(source: apiKeyIdentifyStorage, destination: persistentIdentifyStorage, logger: logger).execute()
}
}
}
5 changes: 3 additions & 2 deletions Sources/Amplitude/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ public class Configuration {
self.flushIntervalMillis = flushIntervalMillis
self.instanceName = instanceName
self.optOut = optOut
self.storageProvider = storageProvider ?? PersistentStorage(apiKey: apiKey)
self.storageProvider = storageProvider
?? PersistentStorage(storagePrefix: "storage-\(instanceName)")
self.identifyStorageProvider = identifyStorageProvider
?? PersistentStorage(apiKey: apiKey, storagePrefix: "\(PersistentStorage.DEFAULT_STORAGE_PREFIX)-identify")
?? PersistentStorage(storagePrefix: "identify-\(instanceName)")
self.logLevel = logLevel
self.loggerProvider = loggerProvider
self.minIdLength = minIdLength
Expand Down
2 changes: 1 addition & 1 deletion Sources/Amplitude/Events/EventOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class EventOptions {
public var partnerId: String?
internal var attempts: Int

init(
public init(
userId: String? = nil,
deviceId: String? = nil,
timestamp: Int64? = nil,
Expand Down
108 changes: 108 additions & 0 deletions Sources/Amplitude/Migration/StoragePrefixMigration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Foundation

class StoragePrefixMigration {
let source: PersistentStorage
let destination: PersistentStorage
let logger: (any Logger)?

init(source: PersistentStorage, destination: PersistentStorage, logger: (any Logger)?) {
self.source = source
self.destination = destination
self.logger = logger
}

func execute() {
if source.storagePrefix == destination.storagePrefix {
return
}

moveSourceEventFilesToDestination()
moveUserDefaults()
}

private func moveUserDefaults() {
moveStringProperty(StorageKey.DEVICE_ID)
moveStringProperty(StorageKey.USER_ID)
moveIntegerProperty(StorageKey.PREVIOUS_SESSION_ID)
moveIntegerProperty(StorageKey.LAST_EVENT_TIME)
moveIntegerProperty(StorageKey.LAST_EVENT_ID)
moveEventsFileKey()
}

private func moveSourceEventFilesToDestination() {
let sourceEventFiles = source.getEventFiles(includeUnfinished: true)
if sourceEventFiles.count == 0 {
return
}
// Ensure destination directory exists.
_ = destination.getEventsStorageDirectory(createDirectory: true)

let fileManager = FileManager.default
for sourceEventFile in sourceEventFiles {
var destinationEventFile = sourceEventFile.path.replacingOccurrences(of: "/\(source.eventsFileKey)/", with: "/\(destination.eventsFileKey)/")
if fileManager.fileExists(atPath: destinationEventFile) {
var fileExtension = sourceEventFile.pathExtension
if fileExtension != "" {
fileExtension = ".\(fileExtension)"
}
destinationEventFile = "\((destinationEventFile as NSString).deletingPathExtension)-\(NSUUID().uuidString)\(fileExtension)"
}
do {
try fileManager.moveItem(atPath: sourceEventFile.path, toPath: destinationEventFile)
} catch {
logger?.warn(message: "Can't move \(sourceEventFile) to \(destinationEventFile): \(error)")
}
}
}

private func moveStringProperty(_ key: StorageKey) {
guard let sourceValue: String = source.read(key: key) else {
return
}

if destination.read(key: key) == nil {
do {
try destination.write(key: key, value: sourceValue)
} catch {
logger?.warn(message: "can't write destination \(key): \(error)")
}
}

do {
try source.write(key: key, value: nil)
} catch {
logger?.warn(message: "can't write source \(key): \(error)")
}
}

private func moveIntegerProperty(_ key: StorageKey) {
guard let sourceValue: Int = source.read(key: key) else {
return
}

let destinationValue: Int? = destination.read(key: key)
if destinationValue == nil || destinationValue! < sourceValue {
do {
try destination.write(key: key, value: sourceValue)
} catch {
logger?.warn(message: "can't write destination \(key): \(error)")
}
}

do {
try source.write(key: key, value: nil)
} catch {
logger?.warn(message: "can't clear source \(key): \(error)")
}
}

private func moveEventsFileKey() {
if let sourceEventFileKey: Int = source.userDefaults?.integer(forKey: source.eventsFileKey) {
let destinationEventFileKey: Int? = destination.userDefaults?.integer(forKey: destination.eventsFileKey)
if destinationEventFileKey == nil || destinationEventFileKey! < sourceEventFileKey {
destination.userDefaults?.set(sourceEventFileKey, forKey: destination.eventsFileKey)
}
}
source.userDefaults?.removeObject(forKey: source.eventsFileKey)
}
}
6 changes: 3 additions & 3 deletions Sources/Amplitude/Sessions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Sessions {
do {
try amplitude.storage.write(key: StorageKey.PREVIOUS_SESSION_ID, value: _sessionId)
} catch {
print("Can't write PREVIOUS_SESSION_ID to storage: \(error)")
amplitude.logger?.warn(message: "Can't write PREVIOUS_SESSION_ID to storage: \(error)")
}
}
}
Expand All @@ -24,7 +24,7 @@ public class Sessions {
do {
try amplitude.storage.write(key: StorageKey.LAST_EVENT_ID, value: _lastEventId)
} catch {
print("Can't write LAST_EVENT_ID to storage: \(error)")
amplitude.logger?.warn(message: "Can't write LAST_EVENT_ID to storage: \(error)")
}
}
}
Expand All @@ -37,7 +37,7 @@ public class Sessions {
do {
try amplitude.storage.write(key: StorageKey.LAST_EVENT_TIME, value: _lastEventTime)
} catch {
print("Can't write LAST_EVENT_TIME to storage: \(error)")
amplitude.logger?.warn(message: "Can't write LAST_EVENT_TIME to storage: \(error)")
}
}
}
Expand Down
Loading

0 comments on commit b36587f

Please sign in to comment.