From 3b2b933fabb8568f80217d2367ed8ce3e7c41efe Mon Sep 17 00:00:00 2001 From: Desu Sai Venkat <48179357+desusai7@users.noreply.github.com> Date: Wed, 1 Feb 2023 20:53:12 +0530 Subject: [PATCH] feat: added Data Residency support (#203) * Added Support for DataResidency feature * Bumped the version to 1.8.0 * Added Unit TestCases for the Data-Residency Feature * Refactored DataResidencyServer to RSDataResidencyServer * Minor improvements in the constructor of RSConfig * Addressed comments for parsing data residency URL's from sourceConfig * Update CHANGELOG.md * chore: Added the support for parsing Multiple Data Plane URL's for each Data Residency Region * fix: Fixed Issues related to returning a nil type from functions * fix: Checking if the residencyURL is nil before using it * made the enum swift compatible * test: updated enum access in unit tests * fix: made changes as per sonarcloud bug --------- Co-authored-by: Desu Sai Venkat Co-authored-by: Pallab Maiti Co-authored-by: Pallab Maiti Co-authored-by: Abhishek Pandey --- .../project.pbxproj | 8 - .../RudderSampleAppObjC/_AppDelegate.h | 5 +- .../RudderSampleAppObjC/_AppDelegate.m | 137 +------ .../RudderSampleAppObjC/_ViewController.m | 58 --- Podfile | 2 - Podfile.lock | 117 +----- Rudder.xcodeproj/project.pbxproj | 141 ++++++- RudderTests/RudderTests.swift | 346 ++++++++++++++++++ Sources/Classes/Public/RSConfig.h | 3 + Sources/Classes/Public/RSConfigBuilder.h | 2 + Sources/Classes/Public/RSEnums.h | 16 + Sources/Classes/Public/RSEventRepository.h | 1 + .../Classes/Public/RSServerConfigManager.h | 1 + Sources/Classes/Public/RSServerConfigSource.h | 3 + Sources/Classes/Public/RSUtils.h | 3 + Sources/Classes/Public/Rudder.h | 1 + Sources/Classes/RSConfig.m | 5 +- Sources/Classes/RSConfigBuilder.m | 8 + Sources/Classes/RSEventRepository.m | 3 +- Sources/Classes/RSServerConfigManager.m | 4 +- Sources/Classes/RSServerConfigSource.m | 23 ++ Sources/Classes/RSUtils.m | 8 + 22 files changed, 568 insertions(+), 327 deletions(-) create mode 100644 RudderTests/RudderTests.swift create mode 100644 Sources/Classes/Public/RSEnums.h diff --git a/Examples/RudderSampleAppObjC/RudderSampleAppObjC.xcodeproj/project.pbxproj b/Examples/RudderSampleAppObjC/RudderSampleAppObjC.xcodeproj/project.pbxproj index ca0a88f0..4529b019 100644 --- a/Examples/RudderSampleAppObjC/RudderSampleAppObjC.xcodeproj/project.pbxproj +++ b/Examples/RudderSampleAppObjC/RudderSampleAppObjC.xcodeproj/project.pbxproj @@ -212,19 +212,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RudderSampleAppObjC/Pods-RudderSampleAppObjC-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/Rudder-iOS/Rudder.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Rudder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.h b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.h index 031cc7df..249da08c 100644 --- a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.h +++ b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.h @@ -7,11 +7,8 @@ // @import UIKit; -@import UserNotifications; -@import Firebase; -@import FirebaseMessaging; -@interface _AppDelegate : UIResponder +@interface _AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; diff --git a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.m b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.m index 415508b7..7f5943e0 100644 --- a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.m +++ b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_AppDelegate.m @@ -31,6 +31,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [builder withRecordScreenViews:YES]; [builder withEnableBackgroundMode:YES]; [builder withDataPlaneUrl:@"http://localhost:8080"]; + [builder withDataResidencyServer:US]; [builder withCustomFactory:[CustomFactory instance]]; [RSClient getInstance:@"1wvsoF3Kx2SczQNlx1dvcqW9ODW" config:[builder build]]; @@ -56,19 +57,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( ]; [[RSClient sharedInstance] alias:@"new_user_id"]; - [FIRApp configure]; - [FIRMessaging messaging].delegate = self; - - [UNUserNotificationCenter currentNotificationCenter].delegate = self; - UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | - UNAuthorizationOptionSound | UNAuthorizationOptionBadge; - [[UNUserNotificationCenter currentNotificationCenter] - requestAuthorizationWithOptions:authOptions - completionHandler:^(BOOL granted, NSError * _Nullable error) { - // ... - }]; - - [application registerForRemoteNotifications]; return YES; } @@ -79,127 +67,4 @@ - (NSString*) getIDFA { - (NSString*) getDeviceToken { return @"example_device_token"; } - -- (void)applicationWillResignActive:(UIApplication *)application -{ - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -// [START receive_message] -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo -fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - // If you are receiving a notification message while your app is in the background, - // this callback will not be fired till the user taps on the notification launching the application. - // TODO: Handle data of notification - - // With swizzling disabled you must let Messaging know about the message, for Analytics - // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; - - // [START_EXCLUDE] - // Print message ID. - if (userInfo[kGCMMessageIDKey]) { - NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]); - } - // [END_EXCLUDE] - - // Print full message. - NSLog(@"%@", userInfo); - - completionHandler(UIBackgroundFetchResultNewData); -} -// [END receive_message] - -// [START ios_10_message_handling] -// Receive displayed notifications for iOS 10 devices. -// Handle incoming notification messages while app is in the foreground. -- (void)userNotificationCenter:(UNUserNotificationCenter *)center - willPresentNotification:(UNNotification *)notification - withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { - NSDictionary *userInfo = notification.request.content.userInfo; - - // With swizzling disabled you must let Messaging know about the message, for Analytics - // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; - - // [START_EXCLUDE] - // Print message ID. - if (userInfo[kGCMMessageIDKey]) { - NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]); - } - // [END_EXCLUDE] - - // Print full message. - NSLog(@"%@", userInfo); - - // Change this to your preferred presentation option - completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionAlert); -} - -// Handle notification messages after display notification is tapped by the user. -- (void)userNotificationCenter:(UNUserNotificationCenter *)center -didReceiveNotificationResponse:(UNNotificationResponse *)response - withCompletionHandler:(void(^)(void))completionHandler { - NSDictionary *userInfo = response.notification.request.content.userInfo; - if (userInfo[kGCMMessageIDKey]) { - NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]); - } - - // With swizzling disabled you must let Messaging know about the message, for Analytics - // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; - - // Print full message. - NSLog(@"%@", userInfo); - - completionHandler(); -} - -// [END ios_10_message_handling] - -// [START refresh_token] -- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken { - NSLog(@"FCM registration token: %@", fcmToken); - // Notify about received token. - NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"]; - [[NSNotificationCenter defaultCenter] postNotificationName: - @"FCMToken" object:nil userInfo:dataDict]; - // TODO: If necessary send token to application server. - // Note: This callback is fired at each app startup and whenever a new token is generated. -} -// [END refresh_token] - -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - NSLog(@"Unable to register for remote notifications: %@", error); -} - -// This function is added here only for debugging purposes, and can be removed if swizzling is enabled. -// If swizzling is disabled then this function must be implemented so that the APNs device token can be paired to -// the FCM registration token. -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - NSLog(@"APNs device token retrieved: %@", deviceToken); - - // With swizzling disabled you must set the APNs device token here. - // [FIRMessaging messaging].APNSToken = deviceToken; -} - @end diff --git a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_ViewController.m b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_ViewController.m index 82d4b146..a4d6b347 100644 --- a/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_ViewController.m +++ b/Examples/RudderSampleAppObjC/RudderSampleAppObjC/_ViewController.m @@ -19,64 +19,6 @@ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. - -// [[RSClient sharedInstance] identify:@"test_user_id" traits:@{ -// @"firstName": @"Test", -// @"lastName": @"Name", -// @"email": @"test_1@gmail.com", -// @"phone": @"+91-986543210", -// @"company": @{ -// @"id": @"test_company_id", -// @"name": @"Test Company", -// @"industry": @"Test Industry", -// @"address": @"Test Location" -// } -// }]; -// -// [[RSClient sharedInstance] alias:@"some_other_id"]; -// -// [[RSClient sharedInstance] track:@"test_event_2" properties:@{ -// @"string_key_1": @"string_value", -// @"string_key_2": @"string_value", -// @"string_key_3": @"string_value", -// @"string_key_4": @"string_value", -// @"bool_key": @YES, -// @"num_key": @1.2, -// @"dict_key": @{ -// @"c_key_1": @"value_1", -// @"c_key_2": @"value_2" -// } -// }]; - -// RSOption *options = [[RSOption alloc] init]; -// [options putExternalId:@"test" withId:@"test"]; -// [[RSClient sharedInstance] screen:@"ViewController"]; -// [[RSClient sharedInstance] screen:@"Main screen name" properties:@{@"prop_key" : @"prop_value"}]; -// [[RSClient sharedInstance] screen:@"test screen" properties:@{@"prop_key" : @"prop_value"} options:options]; - -// dispatch_async(dispatch_get_main_queue(), ^{ -// NSMutableDictionary *traits = [[NSMutableDictionary alloc] initWithDictionary:@{ -// @"firstName": @"Test", -// @"lastName": @"Name", -// @"email": @"test_1@gmail.com", -// @"phone": @"+91-986543210", -// @"company": @{ -// @"id": @"test_company_id", -// @"name": @"Test Company", -// @"industry": @"Test Industry", -// @"address": @"Test Location" -// }, -// @"user_id": [NSNull null] -// }]; -// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ -// [[RSClient sharedInstance] identify:@"test_user_id" traits:@{}]; -// }); - -// [traits removeAllObjects]; -// [traits setValue:@"Kolkata" forKey:@"address"]; -// }); - - } - (void)didReceiveMemoryWarning diff --git a/Podfile b/Podfile index 22780882..6c8e2c8f 100644 --- a/Podfile +++ b/Podfile @@ -15,8 +15,6 @@ target 'RudderSampleAppObjC' do project 'Examples/RudderSampleAppObjC/RudderSampleAppObjC.xcodeproj' platform :ios, '9.0' shared_pods - pod 'Firebase/Analytics' - pod 'Firebase/Messaging' end target 'RudderSampleAppSwift' do diff --git a/Podfile.lock b/Podfile.lock index c0883d49..4dd5d180 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,127 +1,16 @@ PODS: - - Firebase/Analytics (6.34.0): - - Firebase/Core - - Firebase/Core (6.34.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.9.0) - - Firebase/CoreOnly (6.34.0): - - FirebaseCore (= 6.10.4) - - Firebase/Messaging (6.34.0): - - Firebase/CoreOnly - - FirebaseMessaging (~> 4.7.1) - - FirebaseAnalytics (6.9.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.7) - - GoogleAppMeasurement (= 6.9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - FirebaseCore (6.10.4): - - FirebaseCoreDiagnostics (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - FirebaseCoreDiagnostics (1.7.0): - - GoogleDataTransport (~> 7.4) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - nanopb (~> 1.30906.0) - - FirebaseInstallations (1.7.0): - - FirebaseCore (~> 6.10) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - PromisesObjC (~> 1.2) - - FirebaseInstanceID (4.8.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - FirebaseMessaging (4.7.1): - - FirebaseCore (~> 6.10) - - FirebaseInstanceID (~> 4.7) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Reachability (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - Protobuf (>= 3.9.2, ~> 3.9) - - GoogleAppMeasurement (6.9.0): - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - GoogleDataTransport (7.5.1): - - nanopb (~> 1.30906.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.7.2): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): - - GoogleUtilities/Logger - - nanopb (1.30906.0): - - nanopb/decode (= 1.30906.0) - - nanopb/encode (= 1.30906.0) - - nanopb/decode (1.30906.0) - - nanopb/encode (1.30906.0) - - PromisesObjC (1.2.12) - - Protobuf (3.21.5) - - Rudder (1.7.1) + - Rudder (1.7.2) DEPENDENCIES: - - Firebase/Analytics - - Firebase/Messaging - Rudder (from `.`) -SPEC REPOS: - trunk: - - Firebase - - FirebaseAnalytics - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseInstallations - - FirebaseInstanceID - - FirebaseMessaging - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleUtilities - - nanopb - - PromisesObjC - - Protobuf - EXTERNAL SOURCES: Rudder: :path: "." SPEC CHECKSUMS: - Firebase: c23a36d9e4cdf7877dfcba8dd0c58add66358999 - FirebaseAnalytics: 3bb096873ee0d7fa4b6c70f5e9166b6da413cc7f - FirebaseCore: d3a978a3cfa3240bf7e4ba7d137fdf5b22b628ec - FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 - FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 - FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1 - FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a - GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e - GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 - Protobuf: 7504b04fffcf6662ad629694db8231f5e744327f - Rudder: 61568212171ddc2efe7785d0a6a0de589d181370 + Rudder: 473a45c60ca589c1f9bce568f4837c46a1b15de4 -PODFILE CHECKSUM: 33b0e9f1b94a028aa8a3b4aecf718a44c9b6265e +PODFILE CHECKSUM: b22a79db44ea2c78e3500063ff2c7312ef613413 COCOAPODS: 1.11.3 diff --git a/Rudder.xcodeproj/project.pbxproj b/Rudder.xcodeproj/project.pbxproj index cb9be31c..14d9c227 100644 --- a/Rudder.xcodeproj/project.pbxproj +++ b/Rudder.xcodeproj/project.pbxproj @@ -179,10 +179,23 @@ ED857DF4291279CF00B7BFCE /* RSVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = ED857DF32912773300B7BFCE /* RSVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; F623D3AB27954BF10027CBC3 /* RSEventFilteringPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = F623D3AA27954BF10027CBC3 /* RSEventFilteringPlugin.m */; }; F623D3AD27954C010027CBC3 /* RSEventFilteringPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = F623D3AC27954C010027CBC3 /* RSEventFilteringPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F669056F2907EB8800FE4507 /* RSEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = F65E31162907E8BF00B46FB6 /* RSEnums.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F69E95AB29093E630016E5C9 /* RudderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F69E95AA29093E630016E5C9 /* RudderTests.swift */; }; + F69E95AC29093E630016E5C9 /* Rudder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06CABB842630C3CA0097BEFF /* Rudder.framework */; }; F6A146842769DBC6009EF620 /* WKInterfaceController+RSScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A146832769DBC5009EF620 /* WKInterfaceController+RSScreen.h */; settings = {ATTRIBUTES = (Public, ); }; }; F6A1468A2769E28A009EF620 /* WKInterfaceController+RSScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A146892769E28A009EF620 /* WKInterfaceController+RSScreen.m */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + F69E95AD29093E630016E5C9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 06CABB7B2630C3CA0097BEFF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06CABB832630C3CA0097BEFF; + remoteInfo = Rudder; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 06CABB842630C3CA0097BEFF /* Rudder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rudder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 06CABC2E2630C6660097BEFF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -369,6 +382,9 @@ ED857DF32912773300B7BFCE /* RSVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSVersion.h; sourceTree = ""; }; F623D3AA27954BF10027CBC3 /* RSEventFilteringPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSEventFilteringPlugin.m; sourceTree = ""; }; F623D3AC27954C010027CBC3 /* RSEventFilteringPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSEventFilteringPlugin.h; sourceTree = ""; }; + F65E31162907E8BF00B46FB6 /* RSEnums.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSEnums.h; sourceTree = ""; }; + F69E95A829093E630016E5C9 /* RudderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RudderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F69E95AA29093E630016E5C9 /* RudderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RudderTests.swift; sourceTree = ""; }; F6A146832769DBC5009EF620 /* WKInterfaceController+RSScreen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WKInterfaceController+RSScreen.h"; sourceTree = ""; }; F6A146892769E28A009EF620 /* WKInterfaceController+RSScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKInterfaceController+RSScreen.m"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -383,6 +399,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F69E95A529093E630016E5C9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F69E95AC29093E630016E5C9 /* Rudder.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -391,6 +415,7 @@ children = ( ED857DEE2912750900B7BFCE /* Others */, ED83FDB32731019F006F27B3 /* Sources */, + F69E95A929093E630016E5C9 /* RudderTests */, 06CABB852630C3CA0097BEFF /* Products */, 06CABC2D2630C6660097BEFF /* Frameworks */, ); @@ -400,6 +425,7 @@ isa = PBXGroup; children = ( 06CABB842630C3CA0097BEFF /* Rudder.framework */, + F69E95A829093E630016E5C9 /* RudderTests.xctest */, ); name = Products; sourceTree = ""; @@ -478,6 +504,7 @@ ED83FF2127310B44006F27B3 /* Public */ = { isa = PBXGroup; children = ( + F65E31162907E8BF00B46FB6 /* RSEnums.h */, ED83FF3427310B44006F27B3 /* RSApp.h */, ED83FF9A27310B44006F27B3 /* RSCartSharedEvent.h */, ED83FF8F27310B44006F27B3 /* RSCartViewedEvent.h */, @@ -651,6 +678,14 @@ name = Others; sourceTree = ""; }; + F69E95A929093E630016E5C9 /* RudderTests */ = { + isa = PBXGroup; + children = ( + F69E95AA29093E630016E5C9 /* RudderTests.swift */, + ); + path = RudderTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -666,6 +701,7 @@ ED83FFEC27310B44006F27B3 /* UIViewController+RSScreen.h in Headers */, ED83FFCC27310B44006F27B3 /* RSProperty.h in Headers */, ED83FFCA27310B44006F27B3 /* RSMessageType.h in Headers */, + F669056F2907EB8800FE4507 /* RSEnums.h in Headers */, ED83FFCD27310B44006F27B3 /* RSOption.h in Headers */, ED83FFE327310B44006F27B3 /* RSTraits.h in Headers */, ED83FFD427310B44006F27B3 /* RSPreferenceManager.h in Headers */, @@ -770,18 +806,40 @@ productReference = 06CABB842630C3CA0097BEFF /* Rudder.framework */; productType = "com.apple.product-type.framework"; }; + F69E95A729093E630016E5C9 /* RudderTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F69E95B129093E630016E5C9 /* Build configuration list for PBXNativeTarget "RudderTests" */; + buildPhases = ( + F69E95A429093E630016E5C9 /* Sources */, + F69E95A529093E630016E5C9 /* Frameworks */, + F69E95A629093E630016E5C9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F69E95AE29093E630016E5C9 /* PBXTargetDependency */, + ); + name = RudderTests; + productName = RudderTests; + productReference = F69E95A829093E630016E5C9 /* RudderTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 06CABB7B2630C3CA0097BEFF /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1400; LastUpgradeCheck = 1300; TargetAttributes = { 06CABB832630C3CA0097BEFF = { CreatedOnToolsVersion = 12.3; LastSwiftMigration = 1310; }; + F69E95A729093E630016E5C9 = { + CreatedOnToolsVersion = 14.0.1; + }; }; }; buildConfigurationList = 06CABB7E2630C3CA0097BEFF /* Build configuration list for PBXProject "Rudder" */; @@ -798,6 +856,7 @@ projectRoot = ""; targets = ( 06CABB832630C3CA0097BEFF /* Rudder */, + F69E95A729093E630016E5C9 /* RudderTests */, ); }; /* End PBXProject section */ @@ -810,6 +869,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F69E95A629093E630016E5C9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -904,8 +970,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F69E95A429093E630016E5C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F69E95AB29093E630016E5C9 /* RudderTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + F69E95AE29093E630016E5C9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06CABB832630C3CA0097BEFF /* Rudder */; + targetProxy = F69E95AD29093E630016E5C9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 06CABB962630C3CA0097BEFF /* Debug */ = { isa = XCBuildConfiguration; @@ -1037,7 +1119,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = GTGKNDBD23; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1071,7 +1153,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = GTGKNDBD23; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1094,6 +1176,52 @@ }; name = Release; }; + F69E95AF29093E630016E5C9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F69E95B029093E630016E5C9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = GTGKNDBD23; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.rudderstack.RudderTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1115,6 +1243,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F69E95B129093E630016E5C9 /* Build configuration list for PBXNativeTarget "RudderTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F69E95AF29093E630016E5C9 /* Debug */, + F69E95B029093E630016E5C9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 06CABB7B2630C3CA0097BEFF /* Project object */; diff --git a/RudderTests/RudderTests.swift b/RudderTests/RudderTests.swift new file mode 100644 index 00000000..3182863c --- /dev/null +++ b/RudderTests/RudderTests.swift @@ -0,0 +1,346 @@ +// +// RudderTests.swift +// RudderTests +// +// Created by Desu Sai Venkat on 26/10/22. +// + +import XCTest +@testable import Rudder + +class RudderTests: XCTestCase { + + let writeKey = "123@23456" + var rsConfig: RSConfig! + var rsServerConfigManager: RSServerConfigManager! + var rsServerConfigSource: RSServerConfigSource! + var configJson: String! + + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + + } + + func testWithBothResidenciesInSourceConfig() { + + configJson = """ +{ + "isHosted": true, + "source": { + "config": { + "statsCollection": { + "errorReports": { + "enabled": false + }, + "metrics": { + "enabled": false + } + } + }, + "liveEventsConfig": { + + }, + "dataplanes": { + "EU": [ + { + "url": "https://rudderstacgwyx-eu.dataplane.rudderstack.com", + "default": true + } + ], + "US": [ + { + "url": "https://rudderstacgwyx-us.dataplane.rudderstack.com", + "default": true + } + ] + }, + "id": "2GcaJMDRDWtZsZdeusASLcpyamz", + "name": "Android Dev 2", + "writeKey": "2GcaJLm1a8cSKbfCZdKNGn5XDcO", + "enabled": true, + "sourceDefinitionId": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "createdBy": "2FkbV0e3wAJaiqxfFkWVqDrtT7I", + "workspaceId": "2FkbaBpkwsVa3A3B4ZMZ365BVC3", + "deleted": false, + "transient": false, + "secretVersion": null, + "createdAt": "2022-10-25T09:30:52.830Z", + "updatedAt": "2022-10-25T09:30:52.830Z", + "connections": [ + { + "id": "2GcaQeqMFrit8Wju1xDuEA5KgU6", + "sourceId": "2GcaJMDRDWtZsZdeusASLcpyamz", + "destinationId": "2GcaQQ7ce7kPlhvnbQiINUXOa0h", + "enabled": true, + "deleted": false, + "createdAt": "2022-10-25T09:31:50.331Z", + "updatedAt": "2022-10-25T09:31:50.331Z" + } + ], + "destinations": [ + + ], + "sourceDefinition": { + "options": null, + "config": null, + "configSchema": null, + "uiConfig": null, + "id": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "name": "Android", + "displayName": "Android", + "category": null, + "createdAt": "2019-09-02T08:08:08.373Z", + "updatedAt": "2020-06-18T11:54:00.449Z" + } + } +} +""" + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataPlaneUrl("https://some.random.dataplane.com").build() + rsServerConfigManager = RSServerConfigManager(writeKey, rudderConfig: rsConfig); + rsServerConfigSource = rsServerConfigManager._parseConfig(configJson) + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-us.dataplane.rudderstack.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.EU).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-eu.dataplane.rudderstack.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.US).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-us.dataplane.rudderstack.com/") + } + + func testWithOnlyUSInSourceConfig() { + + configJson = """ +{ + "isHosted": true, + "source": { + "config": { + "statsCollection": { + "errorReports": { + "enabled": false + }, + "metrics": { + "enabled": false + } + } + }, + "liveEventsConfig": { + + }, + "dataplanes": { + "US": [ + { + "url": "https://rudderstacgwyx-us.dataplane.rudderstack.com", + "default": true + } + ] + }, + "id": "2GcaJMDRDWtZsZdeusASLcpyamz", + "name": "Android Dev 2", + "writeKey": "2GcaJLm1a8cSKbfCZdKNGn5XDcO", + "enabled": true, + "sourceDefinitionId": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "createdBy": "2FkbV0e3wAJaiqxfFkWVqDrtT7I", + "workspaceId": "2FkbaBpkwsVa3A3B4ZMZ365BVC3", + "deleted": false, + "transient": false, + "secretVersion": null, + "createdAt": "2022-10-25T09:30:52.830Z", + "updatedAt": "2022-10-25T09:30:52.830Z", + "connections": [ + { + "id": "2GcaQeqMFrit8Wju1xDuEA5KgU6", + "sourceId": "2GcaJMDRDWtZsZdeusASLcpyamz", + "destinationId": "2GcaQQ7ce7kPlhvnbQiINUXOa0h", + "enabled": true, + "deleted": false, + "createdAt": "2022-10-25T09:31:50.331Z", + "updatedAt": "2022-10-25T09:31:50.331Z" + } + ], + "destinations": [ + + ], + "sourceDefinition": { + "options": null, + "config": null, + "configSchema": null, + "uiConfig": null, + "id": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "name": "Android", + "displayName": "Android", + "category": null, + "createdAt": "2019-09-02T08:08:08.373Z", + "updatedAt": "2020-06-18T11:54:00.449Z" + } + } +} +""" + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataPlaneUrl("https://some.random.dataplane.com").build() + rsServerConfigManager = RSServerConfigManager(writeKey, rudderConfig: rsConfig); + rsServerConfigSource = rsServerConfigManager._parseConfig(configJson) + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-us.dataplane.rudderstack.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.EU).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-us.dataplane.rudderstack.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.US).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-us.dataplane.rudderstack.com/") + } + + func testWithOnlyEUInSourceConfig() { + + configJson = """ +{ + "isHosted": true, + "source": { + "config": { + "statsCollection": { + "errorReports": { + "enabled": false + }, + "metrics": { + "enabled": false + } + } + }, + "liveEventsConfig": { + + }, + "dataplanes": { + "EU": [ + { + "url": "https://rudderstacgwyx-eu.dataplane.rudderstack.com", + "default": true + } + ] + }, + "id": "2GcaJMDRDWtZsZdeusASLcpyamz", + "name": "Android Dev 2", + "writeKey": "2GcaJLm1a8cSKbfCZdKNGn5XDcO", + "enabled": true, + "sourceDefinitionId": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "createdBy": "2FkbV0e3wAJaiqxfFkWVqDrtT7I", + "workspaceId": "2FkbaBpkwsVa3A3B4ZMZ365BVC3", + "deleted": false, + "transient": false, + "secretVersion": null, + "createdAt": "2022-10-25T09:30:52.830Z", + "updatedAt": "2022-10-25T09:30:52.830Z", + "connections": [ + { + "id": "2GcaQeqMFrit8Wju1xDuEA5KgU6", + "sourceId": "2GcaJMDRDWtZsZdeusASLcpyamz", + "destinationId": "2GcaQQ7ce7kPlhvnbQiINUXOa0h", + "enabled": true, + "deleted": false, + "createdAt": "2022-10-25T09:31:50.331Z", + "updatedAt": "2022-10-25T09:31:50.331Z" + } + ], + "destinations": [ + + ], + "sourceDefinition": { + "options": null, + "config": null, + "configSchema": null, + "uiConfig": null, + "id": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "name": "Android", + "displayName": "Android", + "category": null, + "createdAt": "2019-09-02T08:08:08.373Z", + "updatedAt": "2020-06-18T11:54:00.449Z" + } + } +} +""" + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataPlaneUrl("https://some.random.dataplane.com").build() + rsServerConfigManager = RSServerConfigManager(writeKey, rudderConfig: rsConfig); + rsServerConfigSource = rsServerConfigManager._parseConfig(configJson) + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://some.random.dataplane.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.EU).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://rudderstacgwyx-eu.dataplane.rudderstack.com/") + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.US).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://some.random.dataplane.com/") + } + + func testWhenNoUrlInSourceConfig() { + configJson = """ +{ + "isHosted": true, + "source": { + "config": { + "statsCollection": { + "errorReports": { + "enabled": false + }, + "metrics": { + "enabled": false + } + } + }, + "liveEventsConfig": { + + }, + "id": "2GcaJMDRDWtZsZdeusASLcpyamz", + "name": "Android Dev 2", + "writeKey": "2GcaJLm1a8cSKbfCZdKNGn5XDcO", + "enabled": true, + "sourceDefinitionId": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "createdBy": "2FkbV0e3wAJaiqxfFkWVqDrtT7I", + "workspaceId": "2FkbaBpkwsVa3A3B4ZMZ365BVC3", + "deleted": false, + "transient": false, + "secretVersion": null, + "createdAt": "2022-10-25T09:30:52.830Z", + "updatedAt": "2022-10-25T09:30:52.830Z", + "connections": [ + { + "id": "2GcaQeqMFrit8Wju1xDuEA5KgU6", + "sourceId": "2GcaJMDRDWtZsZdeusASLcpyamz", + "destinationId": "2GcaQQ7ce7kPlhvnbQiINUXOa0h", + "enabled": true, + "deleted": false, + "createdAt": "2022-10-25T09:31:50.331Z", + "updatedAt": "2022-10-25T09:31:50.331Z" + } + ], + "destinations": [ + + ], + "sourceDefinition": { + "options": null, + "config": null, + "configSchema": null, + "uiConfig": null, + "id": "1QGzOQGVLM35GgtteFH1vYCE0WT", + "name": "Android", + "displayName": "Android", + "category": null, + "createdAt": "2019-09-02T08:08:08.373Z", + "updatedAt": "2020-06-18T11:54:00.449Z" + } + } +} +""" + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataPlaneUrl("https://some.random.dataplane.com").build() + rsServerConfigManager = RSServerConfigManager(writeKey, rudderConfig: rsConfig); + rsServerConfigSource = rsServerConfigManager._parseConfig(configJson) + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://some.random.dataplane.com/") + + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.EU).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://some.random.dataplane.com/") + + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataResidencyServer(RSDataResidencyServer.US).withDataPlaneUrl("https://some.random.dataplane.com").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://some.random.dataplane.com/") + + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).withDataPlaneUrl("https::/somerandomdataplanecom").build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://hosted.rudderlabs.com/") + + rsConfig = RSConfigBuilder().withLoglevel(RSLogLevelVerbose).build() + XCTAssertEqual(RSUtils.getDataPlaneUrl(from: rsServerConfigSource, andRSConfig: rsConfig), "https://hosted.rudderlabs.com/") + + } +} diff --git a/Sources/Classes/Public/RSConfig.h b/Sources/Classes/Public/RSConfig.h index 04ad7044..f736085d 100644 --- a/Sources/Classes/Public/RSConfig.h +++ b/Sources/Classes/Public/RSConfig.h @@ -8,12 +8,15 @@ #import #import "RSIntegrationFactory.h" +#import "RSEnums.h" + NS_ASSUME_NONNULL_BEGIN @interface RSConfig : NSObject @property (nonatomic, nonnull) NSString *dataPlaneUrl; +@property (nonatomic) RSDataResidencyServer dataResidencyServer; @property (nonatomic) int flushQueueSize; @property (nonatomic) int dbCountThreshold; @property (nonatomic) int sleepTimeout; diff --git a/Sources/Classes/Public/RSConfigBuilder.h b/Sources/Classes/Public/RSConfigBuilder.h index 9b559d9a..65360e84 100644 --- a/Sources/Classes/Public/RSConfigBuilder.h +++ b/Sources/Classes/Public/RSConfigBuilder.h @@ -8,6 +8,7 @@ #import #import "RSConfig.h" +#import "RSEnums.h" NS_ASSUME_NONNULL_BEGIN @@ -21,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)withEndPointUrl:(NSString*)endPointUrl __attribute((deprecated("Use withDataPlaneUrl instead."))); - (instancetype)withDataPlaneUrl:(NSString*)dataPlaneUrl; - (instancetype)withDataPlaneURL:(NSURL*)dataPlaneURL; +- (instancetype)withDataResidencyServer:(RSDataResidencyServer) dataResidencyServer; - (instancetype)withFlushQueueSize:(int)flushQueueSize; - (instancetype)withDebug:(BOOL)debug; - (instancetype)withLoglevel:(int)logLevel; diff --git a/Sources/Classes/Public/RSEnums.h b/Sources/Classes/Public/RSEnums.h new file mode 100644 index 00000000..7e3a399e --- /dev/null +++ b/Sources/Classes/Public/RSEnums.h @@ -0,0 +1,16 @@ +// +// RSEnums.h +// Rudder +// +// Created by Desu Sai Venkat on 25/10/22. +// + +#ifndef RSEnums_h +#define RSEnums_h + +typedef NS_ENUM(NSInteger, RSDataResidencyServer) { + EU, + US +}; + +#endif /* RSEnums_h */ diff --git a/Sources/Classes/Public/RSEventRepository.h b/Sources/Classes/Public/RSEventRepository.h index 7f264460..6f82cb64 100644 --- a/Sources/Classes/Public/RSEventRepository.h +++ b/Sources/Classes/Public/RSEventRepository.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN NSString* writeKey; NSString* authToken; NSString* anonymousIdToken; + NSString* dataPlaneUrl; RSConfig* config; #if !TARGET_OS_WATCH UIBackgroundTaskIdentifier backgroundTask; diff --git a/Sources/Classes/Public/RSServerConfigManager.h b/Sources/Classes/Public/RSServerConfigManager.h index 8678ed73..e93a39c9 100644 --- a/Sources/Classes/Public/RSServerConfigManager.h +++ b/Sources/Classes/Public/RSServerConfigManager.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @property RSPreferenceManager *preferenceManager; - (instancetype)init: (NSString*) writeKey rudderConfig:(RSConfig*) rudderConfig; +- (RSServerConfigSource *_Nullable)_parseConfig:(NSString *)configStr; - (RSServerConfigSource*) getConfig; - (int) getError; diff --git a/Sources/Classes/Public/RSServerConfigSource.h b/Sources/Classes/Public/RSServerConfigSource.h index 170ea592..4a0a9e1a 100644 --- a/Sources/Classes/Public/RSServerConfigSource.h +++ b/Sources/Classes/Public/RSServerConfigSource.h @@ -7,6 +7,7 @@ // #import +#import "RSConfig.h" #import "RSServerDestination.h" NS_ASSUME_NONNULL_BEGIN @@ -18,8 +19,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readwrite) BOOL isSourceEnabled; @property (nonatomic, readwrite) NSString *updatedAt; @property (nonatomic, readwrite) NSMutableArray *destinations; +@property (nonatomic, readwrite) NSMutableDictionary* dataPlanes; - (void) addDestination: (RSServerDestination*) destination; +- (NSString *_Nullable) getDataResidencyUrl:(RSDataResidencyServer) residency; @end diff --git a/Sources/Classes/Public/RSUtils.h b/Sources/Classes/Public/RSUtils.h index cbdce116..d3c8693f 100644 --- a/Sources/Classes/Public/RSUtils.h +++ b/Sources/Classes/Public/RSUtils.h @@ -8,6 +8,8 @@ #import #import "RSDBMessage.h" +#import "RSServerConfigSource.h" +#import "RSConfig.h" NS_ASSUME_NONNULL_BEGIN @@ -26,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSMutableArray*) getBatch:(NSMutableArray*) messageDetails withQueueSize: (int) queueSize; + (BOOL) isValidURL:(NSURL*) url; + (NSString*) appendSlashToUrl:(NSString*) url; ++ (NSString *) getDataPlaneUrlFrom:(RSServerConfigSource *) serverConfig andRSConfig:(RSConfig *) rsConfig; extern unsigned int MAX_EVENT_SIZE; extern unsigned int MAX_BATCH_SIZE; diff --git a/Sources/Classes/Public/Rudder.h b/Sources/Classes/Public/Rudder.h index d4bcafd8..f3859246 100644 --- a/Sources/Classes/Public/Rudder.h +++ b/Sources/Classes/Public/Rudder.h @@ -9,6 +9,7 @@ #import "RSConfig.h" #import "RSConfigBuilder.h" +#import "RSEnums.h" #import "RSMessage.h" #import "RSMessageBuilder.h" diff --git a/Sources/Classes/RSConfig.m b/Sources/Classes/RSConfig.m index 7fa935a3..3859c815 100644 --- a/Sources/Classes/RSConfig.m +++ b/Sources/Classes/RSConfig.m @@ -16,6 +16,7 @@ - (instancetype)init self = [super init]; if (self) { _dataPlaneUrl = RSDataPlaneUrl; + _dataResidencyServer = US; _flushQueueSize = RSFlushQueueSize; _dbCountThreshold = RSDBCountThreshold; _sleepTimeout = RSSleepTimeout; @@ -34,11 +35,12 @@ - (instancetype)init } - (instancetype)init:(NSString *) dataPlaneUrl +dataResidencyServer: (RSDataResidencyServer) dataResidencyServer flushQueueSize: (int) flushQueueSize dbCountThreshold: (int) dbCountThreshold sleepTimeOut: (int) sleepTimeout logLevel: (int) logLevel - sessionInActivityTimeOut: (long) sessionInActivityTimeOut +sessionInActivityTimeOut: (long) sessionInActivityTimeOut configRefreshInterval: (int) configRefreshInteval trackLifecycleEvents: (BOOL) trackLifecycleEvents enableBackgroundMode: (BOOL) enableBackgroundMode @@ -49,6 +51,7 @@ - (instancetype)init:(NSString *) dataPlaneUrl self = [super init]; if (self) { _dataPlaneUrl = dataPlaneUrl; + _dataResidencyServer = dataResidencyServer; _flushQueueSize = flushQueueSize; _dbCountThreshold = dbCountThreshold; _sleepTimeout = sleepTimeout; diff --git a/Sources/Classes/RSConfigBuilder.m b/Sources/Classes/RSConfigBuilder.m index 146202b4..9bcb55b9 100644 --- a/Sources/Classes/RSConfigBuilder.m +++ b/Sources/Classes/RSConfigBuilder.m @@ -46,6 +46,14 @@ - (instancetype)withDataPlaneURL:(NSURL *) dataPlaneURL { return self; } +- (instancetype) withDataResidencyServer:(RSDataResidencyServer) dataResidencyServer { + if (config == nil) { + config = [[RSConfig alloc] init]; + } + config.dataResidencyServer = dataResidencyServer; + return self; +} + - (instancetype) withFlushQueueSize: (int) flushQueueSize { if (config == nil) { config = [[RSConfig alloc] init]; diff --git a/Sources/Classes/RSEventRepository.m b/Sources/Classes/RSEventRepository.m index 2b6c0a98..7ca6363e 100644 --- a/Sources/Classes/RSEventRepository.m +++ b/Sources/Classes/RSEventRepository.m @@ -144,6 +144,7 @@ - (void) __initiateSDK { strongSelf->isSDKEnabled = serverConfig.isSourceEnabled; }); if (strongSelf->isSDKEnabled) { + self->dataPlaneUrl = [RSUtils getDataPlaneUrlFrom:serverConfig andRSConfig:self->config]; [RSLogger logDebug:@"EventRepository: initiating processor"]; [strongSelf __initiateProcessor]; @@ -413,7 +414,7 @@ - (int) __flushEventsToServer: (NSString*) payload { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); int __block respStatus = NETWORKSUCCESS; - NSString *dataPlaneEndPoint = [self->config.dataPlaneUrl stringByAppendingString:@"v1/batch"]; + NSString *dataPlaneEndPoint = [self->dataPlaneUrl stringByAppendingString:@"v1/batch"]; [RSLogger logDebug:[[NSString alloc] initWithFormat:@"endPointToFlush %@", dataPlaneEndPoint]]; NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:dataPlaneEndPoint]]; diff --git a/Sources/Classes/RSServerConfigManager.m b/Sources/Classes/RSServerConfigManager.m index 837c09a8..577494f7 100644 --- a/Sources/Classes/RSServerConfigManager.m +++ b/Sources/Classes/RSServerConfigManager.m @@ -67,7 +67,7 @@ - (RSServerConfigSource* _Nullable) _retrieveConfig { } } -- (RSServerConfigSource *)_parseConfig:(NSString *)configStr { +- (RSServerConfigSource *_Nullable)_parseConfig:(NSString *)configStr { NSError *error; NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:[configStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error]; @@ -114,6 +114,8 @@ - (RSServerConfigSource *)_parseConfig:(NSString *)configStr { destination.destinationConfig = [destinationDict objectForKey:@"config"]; [destinations addObject:destination]; } + + source.dataPlanes = [sourceDict objectForKey:@"dataplanes"]; source.destinations = destinations; } else { [RSLogger logError:@"config deserializaion error"]; diff --git a/Sources/Classes/RSServerConfigSource.m b/Sources/Classes/RSServerConfigSource.m index 32d7a8e4..ebf4a698 100644 --- a/Sources/Classes/RSServerConfigSource.m +++ b/Sources/Classes/RSServerConfigSource.m @@ -15,6 +15,7 @@ - (instancetype)init self = [super init]; if (self) { self.destinations = [[NSMutableArray alloc] init]; + self.dataPlanes = [[NSMutableDictionary alloc] init]; } return self; } @@ -23,4 +24,26 @@ - (void)addDestination:(RSServerDestination *)destination { [self.destinations addObject:destination]; } +- (NSString *_Nullable) getDataResidencyUrl:(RSDataResidencyServer) residency { + NSArray * residenceDataPlanes; + switch(residency) { + case EU: + residenceDataPlanes = [self.dataPlanes objectForKey:@"EU"]; + // If EU is missing from the sourceConfig response we should fallback to the US, hence the break is not added here. + default: + if (residenceDataPlanes != nil) + break; + residenceDataPlanes = [self.dataPlanes objectForKey:@"US"]; + } + + if(residenceDataPlanes == nil) + return nil; + for (NSDictionary* residenceDataPlane in residenceDataPlanes) { + if([[residenceDataPlane objectForKey:@"default"] boolValue]) { + return [residenceDataPlane objectForKey:@"url"]; + } + } + return nil; +} + @end diff --git a/Sources/Classes/RSUtils.m b/Sources/Classes/RSUtils.m index cc6fe553..72aa0879 100644 --- a/Sources/Classes/RSUtils.m +++ b/Sources/Classes/RSUtils.m @@ -147,6 +147,14 @@ + (NSString*) appendSlashToUrl:(NSString*) url { return [url stringByAppendingString:@"/"]; } ++ (NSString *) getDataPlaneUrlFrom:(RSServerConfigSource *) serverConfig andRSConfig:(RSConfig *) rsConfig { + NSString* dataResidencyUrl = [serverConfig getDataResidencyUrl:rsConfig.dataResidencyServer]; + if(dataResidencyUrl == nil) { + return [RSUtils appendSlashToUrl:rsConfig.dataPlaneUrl]; + } + return [RSUtils appendSlashToUrl:dataResidencyUrl]; +} + unsigned int MAX_EVENT_SIZE = 32 * 1024; // 32 KB unsigned int MAX_BATCH_SIZE = 500 * 1024; // 500 KB