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

V4 data migration #63

Merged
merged 11 commits into from
Mar 30, 2021
8 changes: 8 additions & 0 deletions AEPTarget.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
BB1DBEB825D30A4A00DDBA15 /* DeliveryRequestBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1DBEB725D30A4A00DDBA15 /* DeliveryRequestBuilderTests.swift */; };
BB1E55892574A35A006632E4 /* TargetParameters+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E55882574A35A006632E4 /* TargetParameters+Internal.swift */; };
BB1E55A12575BC4B006632E4 /* TargetDeliveryResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E55A02575BC4B006632E4 /* TargetDeliveryResponse.swift */; };
BB278D332613A5D10081C3C9 /* TargetV4Migrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB278D322613A5D10081C3C9 /* TargetV4Migrator.swift */; };
BB278D382613A5E70081C3C9 /* TargetV4MigratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB278D372613A5E70081C3C9 /* TargetV4MigratorTests.swift */; };
BB47C0082554771800EC6FB5 /* TargetConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47C00625546E0500EC6FB5 /* TargetConstants.swift */; };
BB47C00A25547A2E00EC6FB5 /* TargetParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47C00925547A2E00EC6FB5 /* TargetParameters.swift */; };
BB47C00C25547BE800EC6FB5 /* TargetPrefetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47C00B25547BE800EC6FB5 /* TargetPrefetch.swift */; };
Expand Down Expand Up @@ -206,6 +208,8 @@
BB1DBEB725D30A4A00DDBA15 /* DeliveryRequestBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeliveryRequestBuilderTests.swift; sourceTree = "<group>"; };
BB1E55882574A35A006632E4 /* TargetParameters+Internal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TargetParameters+Internal.swift"; sourceTree = "<group>"; };
BB1E55A02575BC4B006632E4 /* TargetDeliveryResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetDeliveryResponse.swift; sourceTree = "<group>"; };
BB278D322613A5D10081C3C9 /* TargetV4Migrator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TargetV4Migrator.swift; sourceTree = "<group>"; };
BB278D372613A5E70081C3C9 /* TargetV4MigratorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TargetV4MigratorTests.swift; sourceTree = "<group>"; };
BB47C00625546E0500EC6FB5 /* TargetConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetConstants.swift; sourceTree = "<group>"; };
BB47C00925547A2E00EC6FB5 /* TargetParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetParameters.swift; sourceTree = "<group>"; };
BB47C00B25547BE800EC6FB5 /* TargetPrefetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetPrefetch.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -308,6 +312,7 @@
BB9C8706255EF202007AEF8B /* TargetDeliveryRequest.swift */,
92AE6D0A25D8E31700F46C59 /* TargetDeliveryRequestBuilder.swift */,
BB1E55A02575BC4B006632E4 /* TargetDeliveryResponse.swift */,
BB278D322613A5D10081C3C9 /* TargetV4Migrator.swift */,
BB1CE6AF260A76E0000BDEB8 /* TargetV5Migrator.swift */,
BB0C91492575F72200F635B8 /* TargetState.swift */,
BB0C915A25775F1100F635B8 /* TargetError.swift */,
Expand Down Expand Up @@ -441,6 +446,7 @@
BB1DBE9925D2F30400DDBA15 /* TargetTests.swift */,
BB4CAD17260CF9F00000711E /* TargetStateTests.swift */,
BB1DBEB725D30A4A00DDBA15 /* DeliveryRequestBuilderTests.swift */,
BB278D372613A5E70081C3C9 /* TargetV4MigratorTests.swift */,
BB1CE6B5260A76F1000BDEB8 /* TargetV5MigratorTests.swift */,
);
path = UnitTests;
Expand Down Expand Up @@ -839,6 +845,7 @@
780DF56A258924C40033F107 /* TargetPreviewManager.swift in Sources */,
786C00DE25C1D95300F26D34 /* TargetPreviewManagerFloatingButtonDelegate.swift in Sources */,
BB47C01225547DAE00EC6FB5 /* TargetRequest.swift in Sources */,
BB278D332613A5D10081C3C9 /* TargetV4Migrator.swift in Sources */,
BB9C870F25623692007AEF8B /* TargetPrefetch+Internal.swift in Sources */,
BB1E55892574A35A006632E4 /* TargetParameters+Internal.swift in Sources */,
7885141E255322D9008E6999 /* Event+Target.swift in Sources */,
Expand All @@ -853,6 +860,7 @@
BB8FF11F2602A72C006C80D9 /* TestableNetworkService.swift in Sources */,
927284B8260D17E200F91BC1 /* TargetFunctionalTests.swift in Sources */,
BB8FF0EE2602A705006C80D9 /* TargetIntegrationTests.swift in Sources */,
BB278D382613A5E70081C3C9 /* TargetV4MigratorTests.swift in Sources */,
BB1DBEA125D2F33D00DDBA15 /* TargetTests.swift in Sources */,
924102A1260C01CA00DA88D2 /* MockPreviewManagerUIDelegate.swift in Sources */,
BB1DBE9025D22C3D00DDBA15 /* Target+PublicAPITests.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions AEPTarget/Sources/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class Target: NSObject, Extension {
public required init?(runtime: ExtensionRuntime) {
self.runtime = runtime
TargetV5Migrator.migrate()
TargetV4Migrator.migrate()
targetState = TargetState()
super.init()
}
Expand Down
10 changes: 10 additions & 0 deletions AEPTarget/Sources/TargetConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,23 @@ enum TargetConstants {
static let SESSION_TIMESTAMP = "Adobe.ADOBEMOBILE_TARGET.SESSION_TIMESTAMP"
}

enum V4Migration {
static let TNT_ID = "ADBMOBILE_TARGET_TNT_ID"
static let THIRD_PARTY_ID = "ADBMOBILE_TARGET_3RD_PARTY_ID"
static let SESSION_ID = "ADBMOBILE_TARGET_SESSION_ID"
static let EDGE_HOST = "ADBMOBILE_TARGET_EDGE_HOST"
static let LAST_TIMESTAMP = "ADBMOBILE_TARGET_LAST_TIMESTAMP"
static let V4_DATA_MIGRATED = "ADBMOBILE_TARGET_DATA_MIGRATED"
}

enum DataStoreKeys {
static let SESSION_TIMESTAMP = "session.timestamp"
static let SESSION_ID = "session.id"
static let TNT_ID = "tnt.id"
static let EDGE_HOST = "edge.host"
static let THIRD_PARTY_ID = "thirdparty.id"
static let V5_MIGRATION_COMPLETE = "v5.migration.complete"
static let V4_MIGRATION_COMPLETE = "v4.migration.complete"
}

enum TargetRequestValue {
Expand Down
69 changes: 69 additions & 0 deletions AEPTarget/Sources/TargetV4Migrator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright 2021 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

import AEPServices
import Foundation

/// Provides functionality for migrating stored data from V4 to Swift V5
enum TargetV4Migrator {
private static var userDefaultV4: UserDefaults {
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
if let v4AppGroup = ServiceProvider.shared.namedKeyValueService.getAppGroup(), !v4AppGroup.isEmpty {
return UserDefaults(suiteName: v4AppGroup) ?? UserDefaults.standard
}

return UserDefaults.standard
}

/// Migrates the V4 Target values into the Swift V5 Target data store
static func migrate() {
let targetDataStore = NamedCollectionDataStore(name: TargetConstants.DATASTORE_NAME)

guard targetDataStore.getBool(key: TargetConstants.DataStoreKeys.V4_MIGRATION_COMPLETE) == nil else {
return
}

// save values
if targetDataStore.getString(key: TargetConstants.DataStoreKeys.THIRD_PARTY_ID) == nil,
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
targetDataStore.getString(key: TargetConstants.DataStoreKeys.TNT_ID) == nil,
targetDataStore.getString(key: TargetConstants.DataStoreKeys.SESSION_ID) == nil,
targetDataStore.getLong(key: TargetConstants.DataStoreKeys.SESSION_TIMESTAMP) == nil,
targetDataStore.getString(key: TargetConstants.DataStoreKeys.EDGE_HOST) == nil
{
if let thirdPartyId = userDefaultV4.string(forKey: TargetConstants.V4Migration.THIRD_PARTY_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.THIRD_PARTY_ID, value: thirdPartyId)
}
if let tntId = userDefaultV4.string(forKey: TargetConstants.V4Migration.TNT_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.TNT_ID, value: tntId)
}
if let edgeHost = userDefaultV4.string(forKey: TargetConstants.V4Migration.EDGE_HOST) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.EDGE_HOST, value: edgeHost)
}
let timestamp = userDefaultV4.integer(forKey: TargetConstants.V4Migration.LAST_TIMESTAMP)
if timestamp > 0 {
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_TIMESTAMP, value: timestamp)
}
if let sessionId = userDefaultV4.string(forKey: TargetConstants.V4Migration.SESSION_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_ID, value: sessionId)
}
}

// remove old values
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.THIRD_PARTY_ID)
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.TNT_ID)
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.EDGE_HOST)
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.SESSION_ID)
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.LAST_TIMESTAMP)
userDefaultV4.removeObject(forKey: TargetConstants.V4Migration.V4_DATA_MIGRATED)

targetDataStore.set(key: TargetConstants.DataStoreKeys.V4_MIGRATION_COMPLETE, value: true)
}
}
35 changes: 22 additions & 13 deletions AEPTarget/Sources/TargetV5Migrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,30 @@ enum TargetV5Migrator {
return
}

// load old values
let edgeHost = userDefaultV5.string(forKey: TargetConstants.V5Migration.EDGE_HOST)
let sessionTimestamp = userDefaultV5.integer(forKey: TargetConstants.V5Migration.SESSION_TIMESTAMP)
let sessionId = userDefaultV5.string(forKey: TargetConstants.V5Migration.SESSION_ID)
let thirdPartyId = userDefaultV5.string(forKey: TargetConstants.V5Migration.THIRD_PARTY_ID)
let tntId = userDefaultV5.string(forKey: TargetConstants.V5Migration.TNT_ID)

// save values
targetDataStore.set(key: TargetConstants.DataStoreKeys.EDGE_HOST, value: edgeHost)
if sessionTimestamp > 0 {
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_TIMESTAMP, value: sessionTimestamp)
if targetDataStore.getString(key: TargetConstants.DataStoreKeys.THIRD_PARTY_ID) == nil,
targetDataStore.getString(key: TargetConstants.DataStoreKeys.TNT_ID) == nil,
targetDataStore.getString(key: TargetConstants.DataStoreKeys.SESSION_ID) == nil,
targetDataStore.getLong(key: TargetConstants.DataStoreKeys.SESSION_TIMESTAMP) == nil,
targetDataStore.getString(key: TargetConstants.DataStoreKeys.EDGE_HOST) == nil
{
if let edgeHost = userDefaultV5.string(forKey: TargetConstants.V5Migration.EDGE_HOST) {
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
targetDataStore.set(key: TargetConstants.DataStoreKeys.EDGE_HOST, value: edgeHost)
}
let sessionTimestamp = userDefaultV5.integer(forKey: TargetConstants.V5Migration.SESSION_TIMESTAMP)
if sessionTimestamp > 0 {
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_TIMESTAMP, value: sessionTimestamp)
}
if let sessionId = userDefaultV5.string(forKey: TargetConstants.V5Migration.SESSION_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_ID, value: sessionId)
}
if let thirdPartyId = userDefaultV5.string(forKey: TargetConstants.V5Migration.THIRD_PARTY_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.THIRD_PARTY_ID, value: thirdPartyId)
}
if let tntId = userDefaultV5.string(forKey: TargetConstants.V5Migration.TNT_ID) {
targetDataStore.set(key: TargetConstants.DataStoreKeys.TNT_ID, value: tntId)
}
}
targetDataStore.set(key: TargetConstants.DataStoreKeys.SESSION_ID, value: sessionId)
targetDataStore.set(key: TargetConstants.DataStoreKeys.THIRD_PARTY_ID, value: thirdPartyId)
targetDataStore.set(key: TargetConstants.DataStoreKeys.TNT_ID, value: tntId)

// remove old values
userDefaultV5.removeObject(forKey: TargetConstants.V5Migration.EDGE_HOST)
Expand Down
34 changes: 29 additions & 5 deletions AEPTarget/Tests/FunctionalTests/TargetFunctionalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class TargetFunctionalTests: XCTestCase {

private func cleanUserDefaults() {
for _ in 0 ... 5 {
for key in getUserDefaultsV5().dictionaryRepresentation().keys {
for key in getUserDefaults().dictionaryRepresentation().keys {
UserDefaults.standard.removeObject(forKey: key)
}
}
Expand All @@ -82,7 +82,7 @@ class TargetFunctionalTests: XCTestCase {
return NamedCollectionDataStore(name: "com.adobe.module.target")
}

private func getUserDefaultsV5() -> UserDefaults {
private func getUserDefaults() -> UserDefaults {
if let v5AppGroup = ServiceProvider.shared.namedKeyValueService.getAppGroup(), !v5AppGroup.isEmpty {
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
return UserDefaults(suiteName: v5AppGroup) ?? UserDefaults.standard
}
Expand Down Expand Up @@ -116,11 +116,16 @@ class TargetFunctionalTests: XCTestCase {

// MARK: - Data Migration

func testTargetInitWithDataMigration() {
let userDefaultsV5 = getUserDefaultsV5()
func testRegisterExtension_registersWithoutAnyErrorOrCrash() {
XCTAssertNoThrow(MobileCore.registerExtensions([Target.self]))
}

func testTargetInitWithDataMigrationFromV5() {
let userDefaultsV5 = getUserDefaults()
let targetDataStore = getTargetDataStore()
cleanUserDefaults()
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
XCTAssertEqual(nil, targetDataStore.getBool(key: "v5.migration.complete"))
XCTAssertEqual(nil, targetDataStore.getBool(key: "v4.migration.complete"))

let timestamp = Date().getUnixTimeInSeconds()
userDefaultsV5.set("edge.host.com", forKey: "Adobe.ADOBEMOBILE_TARGET.EDGE_HOST")
Expand All @@ -129,15 +134,34 @@ class TargetFunctionalTests: XCTestCase {
userDefaultsV5.set("E621E1F8-C36C-495A-93FC-0C247A3E6E5F", forKey: "Adobe.ADOBEMOBILE_TARGET.SESSION_ID")
userDefaultsV5.set(timestamp, forKey: "Adobe.ADOBEMOBILE_TARGET.SESSION_TIMESTAMP")

target = Target(runtime: mockRuntime)
let target = Target(runtime: mockRuntime)
XCTAssertEqual(true, targetDataStore.getBool(key: "v5.migration.complete"))
XCTAssertEqual(true, targetDataStore.getBool(key: "v4.migration.complete"))
XCTAssertEqual("edge.host.com", target?.targetState.edgeHost)
XCTAssertEqual("id_1", target?.targetState.tntId)
XCTAssertEqual("id_2", target?.targetState.thirdPartyId)
XCTAssertEqual("E621E1F8-C36C-495A-93FC-0C247A3E6E5F", target?.targetState.sessionId)
XCTAssertEqual(timestamp, target?.targetState.sessionTimestampInSeconds)
}

func testTargetInitWithDataMigrationFromV4() {
let userDefaultsV4 = getUserDefaults()
let targetDataStore = getTargetDataStore()
cleanUserDefaults()
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
XCTAssertEqual(nil, targetDataStore.getBool(key: "v5.migration.complete"))
XCTAssertEqual(nil, targetDataStore.getBool(key: "v4.migration.complete"))

userDefaultsV4.set("id_1", forKey: "ADBMOBILE_TARGET_TNT_ID")
userDefaultsV4.set("id_2", forKey: "ADBMOBILE_TARGET_3RD_PARTY_ID")
userDefaultsV4.set(true, forKey: "ADBMOBILE_TARGET_DATA_MIGRATED")
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved

let target = Target(runtime: mockRuntime)
XCTAssertEqual(true, targetDataStore.getBool(key: "v5.migration.complete"))
XCTAssertEqual(true, targetDataStore.getBool(key: "v4.migration.complete"))
XCTAssertEqual("id_1", target?.targetState.tntId)
XCTAssertEqual("id_2", target?.targetState.thirdPartyId)
}

// MARK: - Prefetch

func testPrefetchContent() {
Expand Down
28 changes: 25 additions & 3 deletions AEPTarget/Tests/UnitTests/TargetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,34 @@ class TargetTests: XCTestCase {
target.onRegistered()
}

// MARK: - Unit Tests
private func cleanUserDefaults() {
for _ in 0 ... 5 {
for key in getUserDefaults().dictionaryRepresentation().keys {
UserDefaults.standard.removeObject(forKey: key)
}
}
for _ in 0 ... 5 {
for key in UserDefaults.standard.dictionaryRepresentation().keys {
UserDefaults.standard.removeObject(forKey: key)
}
}
ServiceProvider.shared.namedKeyValueService.setAppGroup(nil)
}

func testRegisterExtension_registersWithoutAnyErrorOrCrash() {
XCTAssertNoThrow(MobileCore.registerExtensions([Target.self]))
private func getTargetDataStore() -> NamedCollectionDataStore {
return NamedCollectionDataStore(name: "com.adobe.module.target")
}

private func getUserDefaults() -> UserDefaults {
if let v5AppGroup = ServiceProvider.shared.namedKeyValueService.getAppGroup(), !v5AppGroup.isEmpty {
yangyansong-adbe marked this conversation as resolved.
Show resolved Hide resolved
return UserDefaults(suiteName: v5AppGroup) ?? UserDefaults.standard
}

return UserDefaults.standard
}

// MARK: - Unit Tests

func testRegisterExtension() {
target.onRegistered()
XCTAssertEqual(5, mockRuntime.listeners.count)
Expand Down
Loading