diff --git a/.gitignore b/.gitignore index ab6ef954..ab4040da 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ build_wrapper_output_directory/ _ General AUL Entries Bulk Capture.txt _ AUL Entries Capture Samples.txt + +.build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 71adf7b6..dd3c840c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,37 +2,33 @@ The change log has moved to this repo's [GitHub Releases Page](https://github.com/rollbar/rollbar-apple/releases). -## Release Notes Tagging Conventions - -1. Every entry within the PackageReleaseNotes element is expected to be started with - at least one of the tags listed: - - feat: A new feature - fix: A bug fix - docs: Documentation only changes - style: Changes that do not affect the meaning of the code - refactor: A code change that neither a bug fix nor a new feature - perf: A code change that improves performance - test: Adding or modifying unit test code - chore: Changes to the build process or auxiliary tools and libraries such as documentation generation, etc. - -2. Every entry within the PackageReleaseNotes element is expected to be tagged with - EITHER - "resolve #GITHUB_ISSUE_NUMBER:" - meaning completely addresses the GitHub issue - OR - "ref #GITHUB_ISSUE_NUMBER:" - meaning relevant to the GitHub issue - depending on what is more appropriate in each case. - ## Release Notes +### 2.4.0 +- New SDK Demos for iOS Swift and Objective-C. +- Fixed a rare crash during JSON serialization. +- More robust and more flexible SDK reconfigurability. +- Thread safety by design. +- Fully independent configurability of each individual instance of a logger. +- Flexible and standardized rate limiting control (both locally configured and server enforced). +- Structured payload storage based on Sqlite instead of a flat text file. +- Improved local payload logging based on developer options of a config object. Separately for all the incoming vs transmitted vs dropped payloads. +- Improved internal diagnostics of the SDK with reach debug build assertions. +- Ooptimized payload modification implementation. +- Improved performance of the RollbarThread. +- Improved internal SDK recovery from any unforeseen internal SDK exceptions/errors (including during processing of totally custom user-specified data within a payload). +- General codebase code quality and maintainability improvements. +- A higher level of code reuse. +- And more... + ### 2.3.4 - fix: resolve #218 - Failsafe to ensure obj is not nil when creating dict literal ### 2.3.3 -- fix: resolve #190 - Fix Cocoapods build warnings in RollbarNotifier -- fix: resolve #191 - Fix Cocoapods build warnings in RollbarCommon +- fix: resolve #190 - Fix Cocoapods build warnings in RollbarNotifier +- fix: resolve #191 - Fix Cocoapods build warnings in RollbarCommon - fix: resolve #192 - Fix SonarCloud detected bugs ### 2.3.2 @@ -76,7 +72,7 @@ The change log has moved to this repo's [GitHub Releases Page](https://github.co ### 2.1.0 -- feat: resolve #141 - Apply developer options of the persisted payload when sending the payload +- feat: resolve #141 - Apply developer options of the persisted payload when sending the payload - feat: resolve #133 - Implement RollbarCocoaLumberjack module - test: resolve #134 - Implement unit tests for RollbarCocoaLumberjack - fix: resolve #136 - RollbarPLCrashReporter.init() no longer available diff --git a/Demos/RollbarDemoSettings.h b/Demos/RollbarDemoSettings.h deleted file mode 100644 index 500f5201..00000000 --- a/Demos/RollbarDemoSettings.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef RollbarDemoSettings_h -#define RollbarDemoSettings_h - -static NSString * const ROLLBAR_DEMO_ENVIRONMENT = -@"Rollbar-Apple-Samples"; - -static NSString * const ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN = -@"09da180aba21479e9ed3d91e0b8d58d6"; - -static NSString * const ROLLBAR_DEMO_DEPLOYS_WRITE_ACCESS_TOKEN = -@"efdc4b85d66045f293a7f9e99c732f61"; - -static NSString * const ROLLBAR_DEMO_DEPLOYS_READ_ACCESS_TOKEN = -@"595cbf76b05b45f2b3ef661a2e0078d4"; - -#endif /* RollbarDemoSettings_h */ diff --git a/Demos/RollbarDemoSettings.swift b/Demos/RollbarDemoSettings.swift deleted file mode 100644 index 870e65a3..00000000 --- a/Demos/RollbarDemoSettings.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -struct RollbarDemoSettings { - static let environment = "Rollbar-Apple-Samples"; - static let payloadsPostAccessToken = "09da180aba21479e9ed3d91e0b8d58d6"; - static let deploysWriteAccessToken = "efdc4b85d66045f293a7f9e99c732f61"; - static let deploysReadAccessToken = "595cbf76b05b45f2b3ef661a2e0078d4"; -} diff --git a/Demos/iosAppObjC/iosAppObjC.xcodeproj/project.pbxproj b/Demos/iosAppObjC/iosAppObjC.xcodeproj/project.pbxproj index f1d417ec..9df839d1 100644 --- a/Demos/iosAppObjC/iosAppObjC.xcodeproj/project.pbxproj +++ b/Demos/iosAppObjC/iosAppObjC.xcodeproj/project.pbxproj @@ -7,9 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 3BDB825A292D67900093AC9D /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB8258292D67900093AC9D /* SceneDelegate.m */; }; 5533980A2627F4670031D02C /* RollbarPLCrashReporter in Frameworks */ = {isa = PBXBuildFile; productRef = 553398092627F4670031D02C /* RollbarPLCrashReporter */; }; 55684F702553B4C400F82F34 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 55684F6F2553B4C400F82F34 /* AppDelegate.m */; }; - 55684F732553B4C400F82F34 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 55684F722553B4C400F82F34 /* SceneDelegate.m */; }; 55684F762553B4C400F82F34 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 55684F752553B4C400F82F34 /* ViewController.m */; }; 55684F792553B4C400F82F34 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55684F772553B4C400F82F34 /* Main.storyboard */; }; 55684F7B2553B4C700F82F34 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 55684F7A2553B4C700F82F34 /* Assets.xcassets */; }; @@ -22,21 +22,20 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 3BDB8258292D67900093AC9D /* SceneDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 3BDB8259292D67900093AC9D /* SceneDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 3BDB825B292E34990093AC9D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 55684F6B2553B4C400F82F34 /* iosAppObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosAppObjC.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55684F6E2553B4C400F82F34 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 55684F6F2553B4C400F82F34 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 55684F712553B4C400F82F34 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; - 55684F722553B4C400F82F34 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 55684F742553B4C400F82F34 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 55684F752553B4C400F82F34 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 55684F782553B4C400F82F34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55684F7A2553B4C700F82F34 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 55684F7D2553B4C700F82F34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 55684F7F2553B4C700F82F34 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55684F802553B4C700F82F34 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55684F9B2553BD1B00F82F34 /* iosAppObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iosAppObjC.entitlements; sourceTree = ""; }; 55684F9C2553BD1C00F82F34 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; - 55C1B42127C5C95000017B22 /* RollbarDemoSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RollbarDemoSettings.h; path = ../../RollbarDemoSettings.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,18 +74,17 @@ 55684F6D2553B4C400F82F34 /* iosAppObjC */ = { isa = PBXGroup; children = ( - 55C1B42127C5C95000017B22 /* RollbarDemoSettings.h */, + 3BDB825B292E34990093AC9D /* Info.plist */, 55684F9B2553BD1B00F82F34 /* iosAppObjC.entitlements */, 55684F6E2553B4C400F82F34 /* AppDelegate.h */, 55684F6F2553B4C400F82F34 /* AppDelegate.m */, - 55684F712553B4C400F82F34 /* SceneDelegate.h */, - 55684F722553B4C400F82F34 /* SceneDelegate.m */, + 3BDB8259292D67900093AC9D /* SceneDelegate.h */, + 3BDB8258292D67900093AC9D /* SceneDelegate.m */, 55684F742553B4C400F82F34 /* ViewController.h */, 55684F752553B4C400F82F34 /* ViewController.m */, 55684F772553B4C400F82F34 /* Main.storyboard */, 55684F7A2553B4C700F82F34 /* Assets.xcassets */, 55684F7C2553B4C700F82F34 /* LaunchScreen.storyboard */, - 55684F7F2553B4C700F82F34 /* Info.plist */, 55684F802553B4C700F82F34 /* main.m */, ); path = iosAppObjC; @@ -178,7 +176,7 @@ 55684F762553B4C400F82F34 /* ViewController.m in Sources */, 55684F702553B4C400F82F34 /* AppDelegate.m in Sources */, 55684F812553B4C700F82F34 /* main.m in Sources */, - 55684F732553B4C400F82F34 /* SceneDelegate.m in Sources */, + 3BDB825A292D67900093AC9D /* SceneDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -238,7 +236,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -255,10 +253,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.rollbar.iosAppObjC; SDKROOT = iphoneos; }; name = Debug; @@ -308,9 +308,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.rollbar.iosAppObjC; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; @@ -324,12 +326,20 @@ CODE_SIGN_ENTITLEMENTS = iosAppObjC/iosAppObjC.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LDX6L68VZJ; + CURRENT_PROJECT_VERSION = 3; + DEVELOPMENT_TEAM = 6B5NER795L; INFOPLIST_FILE = iosAppObjC/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rollbar.iosAppObjC; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -345,12 +355,20 @@ CODE_SIGN_ENTITLEMENTS = iosAppObjC/iosAppObjC.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LDX6L68VZJ; + CURRENT_PROJECT_VERSION = 3; + DEVELOPMENT_TEAM = 6B5NER795L; INFOPLIST_FILE = iosAppObjC/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rollbar.iosAppObjC; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Demos/iosAppObjC/iosAppObjC/AppDelegate.h b/Demos/iosAppObjC/iosAppObjC/AppDelegate.h index 72c98f50..3254d7a6 100644 --- a/Demos/iosAppObjC/iosAppObjC/AppDelegate.h +++ b/Demos/iosAppObjC/iosAppObjC/AppDelegate.h @@ -1,14 +1,6 @@ -// -// AppDelegate.h -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - -#import +@import UIKit; @interface AppDelegate : UIResponder - @end diff --git a/Demos/iosAppObjC/iosAppObjC/AppDelegate.m b/Demos/iosAppObjC/iosAppObjC/AppDelegate.m index a34113ed..82f75761 100644 --- a/Demos/iosAppObjC/iosAppObjC/AppDelegate.m +++ b/Demos/iosAppObjC/iosAppObjC/AppDelegate.m @@ -1,12 +1,4 @@ -// -// AppDelegate.m -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - #import "AppDelegate.h" -#import "RollbarDemoSettings.h" @import RollbarNotifier; @import RollbarKSCrash; @@ -14,98 +6,39 @@ @implementation AppDelegate - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. - [self initRollbar]; - - NSData *data = [[NSData alloc] init]; - NSError *error; - NSJSONReadingOptions serializationOptions = (NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves); - NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:data - options:serializationOptions - error:&error]; - if (!payload && error) { - [Rollbar log:RollbarLevel_Error - error:error - data:nil - context:nil - ]; - } + RollbarMutableConfig *config = [ + // Rollbar post_client_item access token + RollbarConfig mutableConfigWithAccessToken:@"YOUR-ROLLBAR-ACCESSTOKEN" + environment:@"staging"]; - @try { - [self callTroublemaker]; - } @catch (NSException *exception) { - [Rollbar errorException:exception data:nil context:@"from @try-@catch"]; - } @finally { - [Rollbar infoMessage:@"Post-trouble notification!" data:nil context:@"from @try-@finally"]; - } - + config.loggingOptions.codeVersion = @"main"; + config.developerOptions.suppressSdkInfoLogging = NO; + config.telemetry.memoryStatsAutocollectionInterval = 0.5; + config.telemetry.enabled = YES; - // now, cause a crash: - // [self callTroublemaker]; - - //@throw NSInternalInconsistencyException; - - // [self performSelector:@selector(die_die)]; - // [self performSelector:NSSelectorFromString(@"crashme:") withObject:nil afterDelay:10]; + //id crashCollector = [[RollbarKSCrashCollector alloc] init]; + id crashCollector = [[RollbarPLCrashCollector alloc] init]; + + [Rollbar initWithConfiguration:config + crashCollector:crashCollector]; + + [Rollbar infoMessage:@"Rollbar is up and running! Enjoy your remote error and log monitoring..."]; return YES; } - #pragma mark - UISceneSession lifecycle - - (UISceneConfiguration *) application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. - return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; -} - - -- (void) application:(UIApplication *)application -didDiscardSceneSessions:(NSSet *)sceneSessions { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" + sessionRole:connectingSceneSession.role]; } -- (void)initRollbar { - - // configure Rollbar: - RollbarConfig *config = [RollbarConfig new]; - - config.destination.accessToken = ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN; - config.destination.environment = ROLLBAR_DEMO_ENVIRONMENT; - config.developerOptions.suppressSdkInfoLogging = YES; - config.telemetry.memoryStatsAutocollectionInterval = 0.5; - config.telemetry.enabled = YES; - - // init Rollbar shared instance: - //id crashCollector = [[RollbarKSCrashCollector alloc] init]; - id crashCollector = [[RollbarPLCrashCollector alloc] init]; - - [Rollbar initWithConfiguration:config crashCollector:crashCollector]; - - [Rollbar infoMessage:@"Rollbar is up and running! Enjoy your remote error and log monitoring..."]; -} - -- (void)callTroublemaker { - NSArray *items = @[@"one", @"two", @"three"]; - NSLog(@"Here is the trouble-item: %@", items[10]); -} - - //- (void)demonstrateDeployApiUsage { - // - // RollbarDeploysDemoClient * rollbarDeploysIntro = [[RollbarDeploysDemoClient new] init]; - // [rollbarDeploysIntro demoDeploymentRegistration]; - // [rollbarDeploysIntro demoGetDeploymentDetailsById]; - // [rollbarDeploysIntro demoGetDeploymentsPage]; - //} - - @end diff --git a/Demos/iosAppObjC/iosAppObjC/Base.lproj/Main.storyboard b/Demos/iosAppObjC/iosAppObjC/Base.lproj/Main.storyboard index 808a21ce..b5848e50 100644 --- a/Demos/iosAppObjC/iosAppObjC/Base.lproj/Main.storyboard +++ b/Demos/iosAppObjC/iosAppObjC/Base.lproj/Main.storyboard @@ -1,24 +1,150 @@ - + + - + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/iosAppObjC/iosAppObjC/Info.plist b/Demos/iosAppObjC/iosAppObjC/Info.plist index 72bf2c4f..81ed29b7 100644 --- a/Demos/iosAppObjC/iosAppObjC/Info.plist +++ b/Demos/iosAppObjC/iosAppObjC/Info.plist @@ -2,24 +2,6 @@ - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -39,28 +21,5 @@ - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - diff --git a/Demos/iosAppObjC/iosAppObjC/SceneDelegate.h b/Demos/iosAppObjC/iosAppObjC/SceneDelegate.h index 1ee2ecf0..b18ed508 100644 --- a/Demos/iosAppObjC/iosAppObjC/SceneDelegate.h +++ b/Demos/iosAppObjC/iosAppObjC/SceneDelegate.h @@ -1,15 +1,8 @@ -// -// SceneDelegate.h -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - -#import +@import UIKit; @interface SceneDelegate : UIResponder -@property (strong, nonatomic) UIWindow * window; +@property (strong, nonatomic) UIWindow *window; @end diff --git a/Demos/iosAppObjC/iosAppObjC/SceneDelegate.m b/Demos/iosAppObjC/iosAppObjC/SceneDelegate.m index 35cbd6aa..3ac0ce3d 100644 --- a/Demos/iosAppObjC/iosAppObjC/SceneDelegate.m +++ b/Demos/iosAppObjC/iosAppObjC/SceneDelegate.m @@ -1,59 +1,5 @@ -// -// SceneDelegate.m -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - #import "SceneDelegate.h" -@interface SceneDelegate () - -@end - @implementation SceneDelegate - -- (void) scene:(UIScene *)scene - willConnectToSession:(UISceneSession *)session - options:(UISceneConnectionOptions *)connectionOptions { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). -} - - -- (void)sceneDidDisconnect:(UIScene *)scene { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). -} - - -- (void)sceneDidBecomeActive:(UIScene *)scene { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. -} - - -- (void)sceneWillResignActive:(UIScene *)scene { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). -} - - -- (void)sceneWillEnterForeground:(UIScene *)scene { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. -} - - -- (void)sceneDidEnterBackground:(UIScene *)scene { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. -} - - @end diff --git a/Demos/iosAppObjC/iosAppObjC/ViewController.h b/Demos/iosAppObjC/iosAppObjC/ViewController.h index a9acdcb2..29e7ff1c 100644 --- a/Demos/iosAppObjC/iosAppObjC/ViewController.h +++ b/Demos/iosAppObjC/iosAppObjC/ViewController.h @@ -1,14 +1,5 @@ -// -// ViewController.h -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - -#import +@import UIKit; @interface ViewController : UIViewController - @end - diff --git a/Demos/iosAppObjC/iosAppObjC/ViewController.m b/Demos/iosAppObjC/iosAppObjC/ViewController.m index d3d964b1..c249fe19 100644 --- a/Demos/iosAppObjC/iosAppObjC/ViewController.m +++ b/Demos/iosAppObjC/iosAppObjC/ViewController.m @@ -1,22 +1,76 @@ -// -// ViewController.m -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// +@import RollbarNotifier; #import "ViewController.h" -@interface ViewController () +@implementation ViewController -@end +- (IBAction)interrupt:(UIButton *)sender { + raise(SIGINT); +} -@implementation ViewController +- (IBAction)trap:(UIButton *)sender { + raise(SIGTRAP); +} + +- (IBAction)abort:(UIButton *)sender { + abort(); // SIGABRT +} + +- (IBAction)exit:(UIButton *)sender { + exit(1); +} -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. +- (IBAction)assert:(UIButton *)sender { + assert(NO); } +- (IBAction)nsassert:(UIButton *)sender { + NSAssert(NO, @"whoops"); +} + +- (IBAction)throw:(UIButton *)sender { + @throw NSInternalInconsistencyException; +} + +- (IBAction)divideByZero:(UIButton *)sender { + //int i = 1 / 0; + __unused div_t i = div(1, 0); +} + +- (IBAction)null:(UIButton *)sender { + ((char *)NULL)[1] = 0; +} + +- (IBAction)tryCatchFinally:(UIButton *)sender { + @try { + NSArray *items = @[@"one", @"two", @"three"]; + __unused NSString *item = items[4]; + } @catch (NSException *exception) { + [Rollbar errorException:exception + data:nil + context:@"from @try-@catch"]; + } @finally { + [Rollbar infoMessage:@"Finally notification" + data:nil + context:@"from @try-@finally"]; + } +} + +- (IBAction)performAbsurdSelector:(UIButton *)sender { + [self performSelector:@selector(absurd)]; +} + +- (IBAction)invalidJson:(UIButton *)sender { + NSError *error; + __unused NSDictionary *payload = + [NSJSONSerialization JSONObjectWithData:[[NSData alloc] init] + options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves + error:&error]; + + [Rollbar log:RollbarLevel_Error + error:error + data:nil + context:@"While trying to serialize data."]; +} @end diff --git a/Demos/iosAppObjC/iosAppObjC/main.m b/Demos/iosAppObjC/iosAppObjC/main.m index 81d6a600..90ce0c45 100644 --- a/Demos/iosAppObjC/iosAppObjC/main.m +++ b/Demos/iosAppObjC/iosAppObjC/main.m @@ -1,18 +1,13 @@ -// -// main.m -// iosAppObjC -// -// Created by Andrey Kornich on 2020-11-04. -// - #import #import "AppDelegate.h" int main(int argc, char * argv[]) { - NSString * appDelegateClassName; + NSString *appDelegateClassName; + @autoreleasepool { // Setup code that might create autoreleased objects goes here. appDelegateClassName = NSStringFromClass([AppDelegate class]); } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); } diff --git a/Demos/iosAppSwift/iosAppSwift.xcodeproj/project.pbxproj b/Demos/iosAppSwift/iosAppSwift.xcodeproj/project.pbxproj new file mode 100644 index 00000000..af6422c8 --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift.xcodeproj/project.pbxproj @@ -0,0 +1,395 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 3BDB8240292D2DCE0093AC9D /* iosAppSwiftApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB823F292D2DCE0093AC9D /* iosAppSwiftApp.swift */; }; + 3BDB8242292D2DCE0093AC9D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB8241292D2DCE0093AC9D /* ContentView.swift */; }; + 3BDB8244292D2DCF0093AC9D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB8243292D2DCF0093AC9D /* Assets.xcassets */; }; + 3BDB8247292D2DCF0093AC9D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB8246292D2DCF0093AC9D /* Preview Assets.xcassets */; }; + 3BDB824F292D2E4B0093AC9D /* RollbarAUL in Frameworks */ = {isa = PBXBuildFile; productRef = 3BDB824E292D2E4B0093AC9D /* RollbarAUL */; }; + 3BDB8251292D2E4B0093AC9D /* RollbarCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 3BDB8250292D2E4B0093AC9D /* RollbarCommon */; }; + 3BDB8253292D2E4B0093AC9D /* RollbarNotifier in Frameworks */ = {isa = PBXBuildFile; productRef = 3BDB8252292D2E4B0093AC9D /* RollbarNotifier */; }; + 3BDB8255292D2E4B0093AC9D /* RollbarPLCrashReporter in Frameworks */ = {isa = PBXBuildFile; productRef = 3BDB8254292D2E4B0093AC9D /* RollbarPLCrashReporter */; }; + 3BDB8257292D2E4B0093AC9D /* RollbarSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 3BDB8256292D2E4B0093AC9D /* RollbarSwift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 3BDB823C292D2DCE0093AC9D /* iosAppSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosAppSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 3BDB823F292D2DCE0093AC9D /* iosAppSwiftApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosAppSwiftApp.swift; sourceTree = ""; }; + 3BDB8241292D2DCE0093AC9D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 3BDB8243292D2DCF0093AC9D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 3BDB8246292D2DCF0093AC9D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3BDB8239292D2DCE0093AC9D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BDB8255292D2E4B0093AC9D /* RollbarPLCrashReporter in Frameworks */, + 3BDB8253292D2E4B0093AC9D /* RollbarNotifier in Frameworks */, + 3BDB8257292D2E4B0093AC9D /* RollbarSwift in Frameworks */, + 3BDB8251292D2E4B0093AC9D /* RollbarCommon in Frameworks */, + 3BDB824F292D2E4B0093AC9D /* RollbarAUL in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3BDB8233292D2DCE0093AC9D = { + isa = PBXGroup; + children = ( + 3BDB823E292D2DCE0093AC9D /* iosAppSwift */, + 3BDB823D292D2DCE0093AC9D /* Products */, + 3BDB824D292D2E4B0093AC9D /* Frameworks */, + ); + sourceTree = ""; + }; + 3BDB823D292D2DCE0093AC9D /* Products */ = { + isa = PBXGroup; + children = ( + 3BDB823C292D2DCE0093AC9D /* iosAppSwift.app */, + ); + name = Products; + sourceTree = ""; + }; + 3BDB823E292D2DCE0093AC9D /* iosAppSwift */ = { + isa = PBXGroup; + children = ( + 3BDB823F292D2DCE0093AC9D /* iosAppSwiftApp.swift */, + 3BDB8241292D2DCE0093AC9D /* ContentView.swift */, + 3BDB8243292D2DCF0093AC9D /* Assets.xcassets */, + 3BDB8245292D2DCF0093AC9D /* Preview Content */, + ); + path = iosAppSwift; + sourceTree = ""; + }; + 3BDB8245292D2DCF0093AC9D /* Preview Content */ = { + isa = PBXGroup; + children = ( + 3BDB8246292D2DCF0093AC9D /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 3BDB824D292D2E4B0093AC9D /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3BDB823B292D2DCE0093AC9D /* iosAppSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3BDB824A292D2DCF0093AC9D /* Build configuration list for PBXNativeTarget "iosAppSwift" */; + buildPhases = ( + 3BDB8238292D2DCE0093AC9D /* Sources */, + 3BDB8239292D2DCE0093AC9D /* Frameworks */, + 3BDB823A292D2DCE0093AC9D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosAppSwift; + packageProductDependencies = ( + 3BDB824E292D2E4B0093AC9D /* RollbarAUL */, + 3BDB8250292D2E4B0093AC9D /* RollbarCommon */, + 3BDB8252292D2E4B0093AC9D /* RollbarNotifier */, + 3BDB8254292D2E4B0093AC9D /* RollbarPLCrashReporter */, + 3BDB8256292D2E4B0093AC9D /* RollbarSwift */, + ); + productName = iosAppSwift; + productReference = 3BDB823C292D2DCE0093AC9D /* iosAppSwift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3BDB8234292D2DCE0093AC9D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1410; + LastUpgradeCheck = 1410; + TargetAttributes = { + 3BDB823B292D2DCE0093AC9D = { + CreatedOnToolsVersion = 14.1; + }; + }; + }; + buildConfigurationList = 3BDB8237292D2DCE0093AC9D /* Build configuration list for PBXProject "iosAppSwift" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3BDB8233292D2DCE0093AC9D; + productRefGroup = 3BDB823D292D2DCE0093AC9D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3BDB823B292D2DCE0093AC9D /* iosAppSwift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3BDB823A292D2DCE0093AC9D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BDB8247292D2DCF0093AC9D /* Preview Assets.xcassets in Resources */, + 3BDB8244292D2DCF0093AC9D /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3BDB8238292D2DCE0093AC9D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BDB8242292D2DCE0093AC9D /* ContentView.swift in Sources */, + 3BDB8240292D2DCE0093AC9D /* iosAppSwiftApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 3BDB8248292D2DCF0093AC9D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 3BDB8249292D2DCF0093AC9D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3BDB824B292D2DCF0093AC9D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 3; + DEVELOPMENT_ASSET_PATHS = "\"iosAppSwift/Preview Content\""; + DEVELOPMENT_TEAM = 6B5NER795L; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Rollbar Demo"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.matux.test.rollbarAppDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3BDB824C292D2DCF0093AC9D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 3; + DEVELOPMENT_ASSET_PATHS = "\"iosAppSwift/Preview Content\""; + DEVELOPMENT_TEAM = 6B5NER795L; + ENABLE_PREVIEWS = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Rollbar Demo"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.matux.test.rollbarAppDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3BDB8237292D2DCE0093AC9D /* Build configuration list for PBXProject "iosAppSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BDB8248292D2DCF0093AC9D /* Debug */, + 3BDB8249292D2DCF0093AC9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BDB824A292D2DCF0093AC9D /* Build configuration list for PBXNativeTarget "iosAppSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BDB824B292D2DCF0093AC9D /* Debug */, + 3BDB824C292D2DCF0093AC9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 3BDB824E292D2E4B0093AC9D /* RollbarAUL */ = { + isa = XCSwiftPackageProductDependency; + productName = RollbarAUL; + }; + 3BDB8250292D2E4B0093AC9D /* RollbarCommon */ = { + isa = XCSwiftPackageProductDependency; + productName = RollbarCommon; + }; + 3BDB8252292D2E4B0093AC9D /* RollbarNotifier */ = { + isa = XCSwiftPackageProductDependency; + productName = RollbarNotifier; + }; + 3BDB8254292D2E4B0093AC9D /* RollbarPLCrashReporter */ = { + isa = XCSwiftPackageProductDependency; + productName = RollbarPLCrashReporter; + }; + 3BDB8256292D2E4B0093AC9D /* RollbarSwift */ = { + isa = XCSwiftPackageProductDependency; + productName = RollbarSwift; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 3BDB8234292D2DCE0093AC9D /* Project object */; +} diff --git a/Demos/iosAppSwift/iosAppSwift.xcodeproj/xcshareddata/xcschemes/iosAppSwift.xcscheme b/Demos/iosAppSwift/iosAppSwift.xcodeproj/xcshareddata/xcschemes/iosAppSwift.xcscheme new file mode 100644 index 00000000..cf323e7d --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift.xcodeproj/xcshareddata/xcschemes/iosAppSwift.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AccentColor.colorset/Contents.json b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..13613e3e --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/Contents.json b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demos/iosAppSwift/iosAppSwift/ContentView.swift b/Demos/iosAppSwift/iosAppSwift/ContentView.swift new file mode 100644 index 00000000..63bf9659 --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/ContentView.swift @@ -0,0 +1,134 @@ +import SwiftUI +import RollbarSwift +import RollbarNotifier + +enum ExampleError: Error { + case + invalidResult, + outOfBounds, + invalidInput +} + +struct ContentView: View { + let example = Example(); + + var body: some View { + VStack { + Text("Rollbar Apple SDK Example") + .font(.title) + .padding(.bottom) + + VStack { + Button("Manual Logging Example", action: example.manualLogging) + .tint(.blue) + .buttonStyle(.bordered) + + Button("Divide by zero") { _ = example.divide(by: 0) } + .tint(.blue) + .buttonStyle(.bordered) + + Button("Log invalid JSON", action: example.logInvalidJson) + .tint(.blue) + .buttonStyle(.bordered) + .padding(.bottom) + + Button("Throw an ExampleError") { try! example.throwError() } + .tint(.blue) + .buttonStyle(.bordered) + + Button("Throw an NSException", action: example.throwNSException) + .tint(.blue) + .buttonStyle(.bordered) + .padding(.bottom) + + Button("Assertion Failure", action: example.forceAssertionFailure) + .tint(.blue) + .buttonStyle(.bordered) + + Button("Precondition Failure", action: example.forcePreconditionFailure) + .tint(.blue) + .buttonStyle(.bordered) + + Button("Fatal Error", action: example.forceFatalError) + .tint(.blue) + .buttonStyle(.bordered) + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} + +struct Example { + let logger = RollbarLogger(configuration: Rollbar.configuration()) + + /// Some different ways to explicitly log an error to Rollbar. + func manualLogging() { + let extraInfo = ["item_1": "value_1", "item_2": "value_2"] + + Rollbar.log(.error, message: "My log message") + + Rollbar.log( + .error, + error: ExampleError.invalidInput, + data: extraInfo, + context: "Some additional information.") + + Rollbar.errorMessage("My error message", data: extraInfo) + + Rollbar.errorError(ExampleError.invalidResult, data: extraInfo) + + do { + throw ExampleError.outOfBounds + } catch { + Rollbar.errorError(error, data: extraInfo) + } + } + + /// A hard crash is captured by the crash reporter. The next time + /// the application is started, the data is sent to Rollbar. + func forceFatalError() { + fatalError("Force a crash") + } + + func forcePreconditionFailure() { + preconditionFailure("Precondition failed") + } + + func forceAssertionFailure() { + assertionFailure("Assertion failed") + } + + func divide(by y: Int) -> Int { + 1 / y + } + + func throwError() throws { + throw ExampleError.outOfBounds + } + + func throwNSException() { + RollbarExceptionGuard(logger: logger).tryExecute { + RollbarTryCatch.throw("NSException from ObjC") + } + } + + func logInvalidJson() { + Rollbar.log( + .warning, + message: "Logging with extras and context", + data: [ + "fingerprint": "targeted-mistake-recycling-incorrect-range", + "bestSolutionTokens": [51241, 42421, 32142], + "guessTokens": [22414, 89389], + // This is a (Int, Int), which can't be turned into Json + "detectedRangeStart": (1, 10), + ], + context: "Rollbar Example Logging Invalid Json") + } +} diff --git a/Demos/iosAppSwift/iosAppSwift/Preview Content/Preview Assets.xcassets/Contents.json b/Demos/iosAppSwift/iosAppSwift/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demos/iosAppSwift/iosAppSwift/iosAppSwiftApp.swift b/Demos/iosAppSwift/iosAppSwift/iosAppSwiftApp.swift new file mode 100644 index 00000000..6c7a595c --- /dev/null +++ b/Demos/iosAppSwift/iosAppSwift/iosAppSwiftApp.swift @@ -0,0 +1,101 @@ +import SwiftUI +import RollbarSwift +import RollbarNotifier +import RollbarPLCrashReporter + +@main +struct iosAppSwiftApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} + +class AppDelegate: NSObject, UIApplicationDelegate { + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil + ) -> Bool { + // Dynamically read these settings from your config settings on + // application startup. + let accessToken = "YOUR-ROLLBAR-ACCESSTOKEN" // Rollbar post_client_item access token + let environment = "staging" + let codeVersion = "main" // Ideally codeVersion is commit SHA https://docs.rollbar.com/docs/versions + + // Initialize a configuration object and add configuration settings as + // needed. + let config = RollbarConfig.mutableConfig( + withAccessToken: accessToken, + environment: environment) + + config.loggingOptions.codeVersion = codeVersion + + // Optionally anonymize the IP address + //config.loggingOptions.captureIp = RollbarCaptureIpType.anonymize + + // Suppress Rollbar event being logged (e.g. in XCode debug logs) + //config.developerOptions.suppressSdkInfoLogging = true + + config.telemetry.enabled = true + config.telemetry.captureLog = true + config.telemetry.maximumTelemetryData = 10 + + config.modifyRollbarData = Rollbar.transform(payload:) + + // Optionally don't send certain occurrences based on some aspect of + // the payload contents + //config.checkIgnoreRollbarData = Rollbar.shouldIgnore(payload:) + + // List of fields to scrub + // Make sure to test this if you are overriding the default scrub list + //config.dataScrubber.scrubFields = ["accessToken", "cpu", "key_y"] + + // optionally add data about the user to improve error response + config.person.id = "12345" + + // additional custom data to add to every occurrence sent + config.customData = ["customer_type": "enterprise"] + + // Initialize a Rollbar shared instance with a crash collector + Rollbar.initWithConfiguration( + config, + crashCollector: RollbarPLCrashCollector()) + + // Note the ability to add aditional key/value pairs to the occurrence data for extra context + Rollbar.infoMessage( + "Rollbar is up and running! Enjoy your remote error and log monitoring...", + data: ["key_x": "value_x", "key_y": "value_y"]) + + return true + } +} + +extension Rollbar { + /// `return true` means DO NOT send the data to Rollbar (i.e. ignore) + /// `return false` means DO send the data to Rollbar + static func shouldIgnore(payload: RollbarData) -> Bool { + return false + } + + /// Transform the occurrence payload just before the data is sent. + /// + /// This allows data to be added/removed from the payload basd on some + /// aspect of the payload data. + /// + /// This method is often used to do advanced data scrubbing or to add + /// additional data to the payload that is only available at the time + /// the error occurs. + static func transform(payload: RollbarData) -> RollbarData { + // Context is an indexed fast search field in the Rollbar Web UI + // + // The items timeline view can be filtered by context + // https://docs.rollbar.com/docs/search-items#context + payload.context = "owner#ui_team" + + return payload + } +} diff --git a/Demos/macosAppObjC/macosAppObjC.xcodeproj/project.pbxproj b/Demos/macosAppObjC/macosAppObjC.xcodeproj/project.pbxproj index b146ba04..01fd1f83 100644 --- a/Demos/macosAppObjC/macosAppObjC.xcodeproj/project.pbxproj +++ b/Demos/macosAppObjC/macosAppObjC.xcodeproj/project.pbxproj @@ -31,7 +31,6 @@ 55759A772477561100ED3F04 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55759A782477561100ED3F04 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55759A7A2477561100ED3F04 /* macosAppObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macosAppObjC.entitlements; sourceTree = ""; }; - 55C1B42027C5C4E800017B22 /* RollbarDemoSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RollbarDemoSettings.h; path = ../../RollbarDemoSettings.h; sourceTree = ""; }; 55FD07122478614B000BBC22 /* RollbarDeploysDemoClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RollbarDeploysDemoClient.h; sourceTree = ""; }; 55FD07132478614B000BBC22 /* RollbarDeploysDemoClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RollbarDeploysDemoClient.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -72,7 +71,6 @@ 55759A6B2477560D00ED3F04 /* macosAppObjC */ = { isa = PBXGroup; children = ( - 55C1B42027C5C4E800017B22 /* RollbarDemoSettings.h */, 55759A6C2477560D00ED3F04 /* AppDelegate.h */, 55759A6D2477560D00ED3F04 /* AppDelegate.m */, 55759A6F2477560D00ED3F04 /* ViewController.h */, diff --git a/Demos/macosAppObjC/macosAppObjC/AppDelegate.m b/Demos/macosAppObjC/macosAppObjC/AppDelegate.m index 91af9330..5dc5500c 100644 --- a/Demos/macosAppObjC/macosAppObjC/AppDelegate.m +++ b/Demos/macosAppObjC/macosAppObjC/AppDelegate.m @@ -7,21 +7,24 @@ // #import "AppDelegate.h" - #import "RollbarDeploysDemoClient.h" -#import "RollbarDemoSettings.h" @import RollbarNotifier; @import RollbarAUL; @import RollbarKSCrash; @import RollbarPLCrashReporter; +static NSString * const ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN = @"09da180aba21479e9ed3d91e0b8d58d6"; +static NSString * const ROLLBAR_DEMO_DEPLOYS_WRITE_ACCESS_TOKEN = @"efdc4b85d66045f293a7f9e99c732f61"; +static NSString * const ROLLBAR_DEMO_DEPLOYS_READ_ACCESS_TOKEN = @"595cbf76b05b45f2b3ef661a2e0078d4"; + __attribute__((noinline)) static void crashIt (void) { /* Trigger a crash */ ((char *)NULL)[1] = 0; } @interface AppDelegate () +@property (nonatomic, readonly) RollbarLogger *logger; @end @@ -77,16 +80,15 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification { - (void)initRollbar { // configure Rollbar: - RollbarConfig *config = [RollbarConfig new]; - config.destination.accessToken = ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN; - config.destination.environment = ROLLBAR_DEMO_ENVIRONMENT; + RollbarMutableConfig *config = [RollbarConfig mutableConfigWithAccessToken:ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN + environment:@"staging"]; //config.developerOptions.suppressSdkInfoLogging = YES; - config.customData = @{ @"someKey": @"someValue", }; + config.customData = [NSMutableDictionary dictionaryWithDictionary: @{ @"someKey": @"someValue", }]; config.telemetry.enabled = YES; config.telemetry.memoryStatsAutocollectionInterval = 0.5; config.telemetry.maximumTelemetryData = 30; - [RollbarAulStoreMonitor.sharedInstance configureRollbarLogger:Rollbar.currentLogger]; + //[RollbarAulStoreMonitor.sharedInstance configureRollbarLogger:Rollbar.currentLogger]; [RollbarAulStoreMonitor.sharedInstance start]; // optional crash reporter: diff --git a/Demos/macosAppObjC/macosAppObjC/RollbarDeploysDemoClient.m b/Demos/macosAppObjC/macosAppObjC/RollbarDeploysDemoClient.m index eb090c5e..cb4de3e9 100644 --- a/Demos/macosAppObjC/macosAppObjC/RollbarDeploysDemoClient.m +++ b/Demos/macosAppObjC/macosAppObjC/RollbarDeploysDemoClient.m @@ -7,9 +7,12 @@ // #import "RollbarDeploysDemoClient.h" -#import "RollbarDemoSettings.h" @import RollbarDeploys; +static NSString * const ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN = @"09da180aba21479e9ed3d91e0b8d58d6"; +static NSString * const ROLLBAR_DEMO_DEPLOYS_WRITE_ACCESS_TOKEN = @"efdc4b85d66045f293a7f9e99c732f61"; +static NSString * const ROLLBAR_DEMO_DEPLOYS_READ_ACCESS_TOKEN = @"595cbf76b05b45f2b3ef661a2e0078d4"; + @interface RollbarDeploysObserver : NSObject< RollbarDeploymentRegistrationObserver, @@ -58,7 +61,7 @@ - (void)demoDeploymentRegistration { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; - NSString * const environment = ROLLBAR_DEMO_ENVIRONMENT; + NSString * const environment = @"staging"; NSString * const comment = [NSString stringWithFormat:@"a new deploy at %@", [dateFormatter stringFromDate:[NSDate date]]]; NSString * const revision = @"a_revision"; diff --git a/Demos/macosAppObjC/macosAppObjC/ViewController.h b/Demos/macosAppObjC/macosAppObjC/ViewController.h index 6a98f7d5..2af7b0af 100644 --- a/Demos/macosAppObjC/macosAppObjC/ViewController.h +++ b/Demos/macosAppObjC/macosAppObjC/ViewController.h @@ -10,6 +10,5 @@ @interface ViewController : NSViewController - @end diff --git a/Demos/macosAppSwift/macosAppSwift.xcodeproj/project.pbxproj b/Demos/macosAppSwift/macosAppSwift.xcodeproj/project.pbxproj index 5ae3ff3e..45333629 100644 --- a/Demos/macosAppSwift/macosAppSwift.xcodeproj/project.pbxproj +++ b/Demos/macosAppSwift/macosAppSwift.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 5544A01F25FB074300C710A1 /* RollbarCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 5544A01E25FB074300C710A1 /* RollbarCommon */; }; 5544A02125FB074300C710A1 /* RollbarNotifier in Frameworks */ = {isa = PBXBuildFile; productRef = 5544A02025FB074300C710A1 /* RollbarNotifier */; }; 5544A02325FB074300C710A1 /* RollbarSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5544A02225FB074300C710A1 /* RollbarSwift */; }; - 55C1B41F27C5BDF000017B22 /* RollbarDemoSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C1B41E27C5BDF000017B22 /* RollbarDemoSettings.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -27,7 +26,6 @@ 55098C6B25FA93110045C180 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 55098C6D25FA93110045C180 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55098C6E25FA93110045C180 /* macosAppSwift.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macosAppSwift.entitlements; sourceTree = ""; }; - 55C1B41E27C5BDF000017B22 /* RollbarDemoSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RollbarDemoSettings.swift; path = ../../RollbarDemoSettings.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -66,7 +64,6 @@ 55098C6325FA930D0045C180 /* macosAppSwift */ = { isa = PBXGroup; children = ( - 55C1B41E27C5BDF000017B22 /* RollbarDemoSettings.swift */, 55098C6425FA930D0045C180 /* macosAppSwiftApp.swift */, 55098C6625FA930D0045C180 /* ContentView.swift */, 55098C6825FA93110045C180 /* Assets.xcassets */, @@ -169,7 +166,6 @@ buildActionMask = 2147483647; files = ( 55098C6725FA930D0045C180 /* ContentView.swift in Sources */, - 55C1B41F27C5BDF000017B22 /* RollbarDemoSettings.swift in Sources */, 55098C6525FA930D0045C180 /* macosAppSwiftApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Demos/macosAppSwift/macosAppSwift/ContentView.swift b/Demos/macosAppSwift/macosAppSwift/ContentView.swift index d412e9b5..34ad9c4a 100644 --- a/Demos/macosAppSwift/macosAppSwift/ContentView.swift +++ b/Demos/macosAppSwift/macosAppSwift/ContentView.swift @@ -59,14 +59,7 @@ func handleSwiftError() { func handleObjCException() { - do { - - self.generateObjCException(); - } - catch { - - print("Unexpected error: \(error).") - } + self.generateObjCException(); } func generateObjCException() { @@ -90,13 +83,9 @@ func handleSwiftError() { func createGuard() -> RollbarExceptionGuard { - let config = RollbarConfig(); - - config.destination.accessToken = - RollbarDemoSettings.payloadsPostAccessToken; - - config.destination.environment = - RollbarDemoSettings.environment; + let config = RollbarConfig.mutableConfig( + withAccessToken: RollbarDemoSettings.payloadsPostAccessToken, + environment: RollbarDemoSettings.environment) // AUL capture setup: // config.developerOptions.transmit = true; diff --git a/Demos/tvosAppObjC/tvosAppObjC.xcodeproj/project.pbxproj b/Demos/tvosAppObjC/tvosAppObjC.xcodeproj/project.pbxproj index e8c726b4..344c1591 100644 --- a/Demos/tvosAppObjC/tvosAppObjC.xcodeproj/project.pbxproj +++ b/Demos/tvosAppObjC/tvosAppObjC.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 554E6728282B448E00908A64 /* RollbarDemoSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RollbarDemoSettings.h; path = ../../RollbarDemoSettings.h; sourceTree = ""; }; 55889E242818B83B002E0228 /* tvosAppObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvosAppObjC.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55889E272818B83B002E0228 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 55889E282818B83B002E0228 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -65,7 +64,6 @@ 55889E262818B83B002E0228 /* tvosAppObjC */ = { isa = PBXGroup; children = ( - 554E6728282B448E00908A64 /* RollbarDemoSettings.h */, 55889E272818B83B002E0228 /* AppDelegate.h */, 55889E282818B83B002E0228 /* AppDelegate.m */, 55889E2A2818B83B002E0228 /* ViewController.h */, diff --git a/Demos/tvosAppObjC/tvosAppObjC/AppDelegate.m b/Demos/tvosAppObjC/tvosAppObjC/AppDelegate.m index cb6dd7f5..f11aacd6 100644 --- a/Demos/tvosAppObjC/tvosAppObjC/AppDelegate.m +++ b/Demos/tvosAppObjC/tvosAppObjC/AppDelegate.m @@ -6,7 +6,6 @@ // #import "AppDelegate.h" -#import "RollbarDemoSettings.h" @import RollbarNotifier; @import RollbarKSCrash; @@ -82,10 +81,9 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { - (void)initRollbar { // configure Rollbar: - RollbarConfig *config = [RollbarConfig new]; + RollbarMutableConfig *config = [RollbarConfig mutableConfigWithAccessToken:@"09da180aba21479e9ed3d91e0b8d58d6" + environment:@"staging"]; - config.destination.accessToken = ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN; - config.destination.environment = ROLLBAR_DEMO_ENVIRONMENT; config.developerOptions.suppressSdkInfoLogging = YES; config.telemetry.memoryStatsAutocollectionInterval = 0.5; config.telemetry.enabled = YES; diff --git a/Demos/watchosAppObjC/watchosAppObjC WatchKit Extension/ExtensionDelegate.m b/Demos/watchosAppObjC/watchosAppObjC WatchKit Extension/ExtensionDelegate.m index 16124a63..c2b1664f 100644 --- a/Demos/watchosAppObjC/watchosAppObjC WatchKit Extension/ExtensionDelegate.m +++ b/Demos/watchosAppObjC/watchosAppObjC WatchKit Extension/ExtensionDelegate.m @@ -6,7 +6,6 @@ // #import "ExtensionDelegate.h" -#import "RollbarDemoSettings.h" @import RollbarNotifier; @@ -97,10 +96,11 @@ - (void)handleBackgroundTasks:(NSSet *)backgroundTask - (void)initRollbar { // configure Rollbar: - RollbarConfig *config = [RollbarConfig new]; + RollbarMutableConfig *config = [ + // Rollbar post_client_item access token + RollbarMutableConfig mutableConfigWithAccessToken:@"YOUR-ROLLBAR-ACCESSTOKEN" + environment:@"staging"]; - config.destination.accessToken = ROLLBAR_DEMO_PAYLOADS_ACCESS_TOKEN; - config.destination.environment = ROLLBAR_DEMO_ENVIRONMENT; config.developerOptions.suppressSdkInfoLogging = YES; config.telemetry.memoryStatsAutocollectionInterval = 0.5; config.telemetry.enabled = YES; diff --git a/Demos/watchosAppObjC/watchosAppObjC.xcodeproj/project.pbxproj b/Demos/watchosAppObjC/watchosAppObjC.xcodeproj/project.pbxproj index c74c6969..442e8b55 100644 --- a/Demos/watchosAppObjC/watchosAppObjC.xcodeproj/project.pbxproj +++ b/Demos/watchosAppObjC/watchosAppObjC.xcodeproj/project.pbxproj @@ -62,7 +62,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 554E6731282B4C3C00908A64 /* RollbarDemoSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RollbarDemoSettings.h; path = ../../RollbarDemoSettings.h; sourceTree = ""; }; 55889E432818B937002E0228 /* watchosAppObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = watchosAppObjC.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55889E472818B937002E0228 /* watchosAppObjC WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchosAppObjC WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 55889E4D2818B937002E0228 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; @@ -125,7 +124,6 @@ 55889E592818B939002E0228 /* watchosAppObjC WatchKit Extension */ = { isa = PBXGroup; children = ( - 554E6731282B4C3C00908A64 /* RollbarDemoSettings.h */, 55889E5A2818B939002E0228 /* InterfaceController.h */, 55889E5B2818B939002E0228 /* InterfaceController.m */, 55889E5D2818B939002E0228 /* ExtensionDelegate.h */, diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..0e3de8d9 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,43 @@ +{ + "object": { + "pins": [ + { + "package": "CocoaLumberjack", + "repositoryURL": "https://github.com/CocoaLumberjack/CocoaLumberjack.git", + "state": { + "branch": null, + "revision": "0188d31089b5881a269e01777be74c7316924346", + "version": "3.8.0" + } + }, + { + "package": "KSCrash", + "repositoryURL": "https://github.com/kstenerud/KSCrash.git", + "state": { + "branch": null, + "revision": "f45a917d93928b32626f3268239c58a6cdd852bb", + "version": "1.15.25" + } + }, + { + "package": "PLCrashReporter", + "repositoryURL": "https://github.com/microsoft/plcrashreporter.git", + "state": { + "branch": null, + "revision": "b1a342da19ed9b3af61ea2efa7656c2af30aeb7c", + "version": "1.11.0" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "6fe203dc33195667ce1759bf0182975e4653ba1c", + "version": "1.4.4" + } + } + ] + }, + "version": 1 +} diff --git a/RollbarAUL.podspec b/RollbarAUL.podspec index 139b6733..243fd37d 100644 --- a/RollbarAUL.podspec +++ b/RollbarAUL.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarAUL" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -54,7 +54,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true s.xcconfig = { "USE_HEADERMAP" => "NO", diff --git a/RollbarCocoaLumberjack.podspec b/RollbarCocoaLumberjack.podspec index 04693908..6d63b775 100644 --- a/RollbarCocoaLumberjack.podspec +++ b/RollbarCocoaLumberjack.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarCocoaLumberjack" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -56,7 +56,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarCommon.podspec b/RollbarCommon.podspec index c235a57d..d2e250c7 100644 --- a/RollbarCommon.podspec +++ b/RollbarCommon.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarCommon" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -52,7 +52,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO.m b/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO.m index 313cff18..d95391ba 100644 --- a/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO.m +++ b/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO.m @@ -1,6 +1,7 @@ #import "RollbarDTO.h" #import "RollbarSdkLog.h" #import "NSJSONSerialization+Rollbar.h" +#import "NSObject+Rollbar.h" @import ObjectiveC.runtime; @@ -148,6 +149,24 @@ - (nullable NSData *)serializeToJSONData { RollbarSdkLog(@"JSON-invalid internal data."); } + NSJSONWritingOptions opt = 0; + NSError *error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self->_data options:opt error:&error]; + if ((nil == jsonData) && (nil != error)) { + + RollbarSdkLog(@"Error serializing NSData: %@", [error localizedDescription]); + } + return jsonData; +} + +- (nullable NSString *)serializeToJSONString { + + BOOL hasValidData = [NSJSONSerialization isValidJSONObject:self->_data]; + if (NO == hasValidData) { + + RollbarSdkLog(@"JSON-invalid internal data."); + } + NSJSONWritingOptions opt = 0; #ifdef DEBUG opt |= NSJSONWritingPrettyPrinted; @@ -163,18 +182,9 @@ - (nullable NSData *)serializeToJSONData { if ((nil == jsonData) && (nil != error)) { RollbarSdkLog(@"Error serializing NSData: %@", [error localizedDescription]); - } - return jsonData; -} - -- (nullable NSString *)serializeToJSONString { - - NSData *jsonData = [self serializeToJSONData]; - if (nil == jsonData) { - return nil; } - + NSString *result = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; return result; @@ -349,22 +359,27 @@ - (instancetype)initWithDictionary:(nullable NSDictionary *)data if (nil == data) { - data = [NSMutableDictionary dictionary]; + data = [NSMutableDictionary dictionary]; } if (![RollbarDTO isTransferableObject:data]) { - + RollbarSdkLog(@"JSON-invalid internal data."); return self; } - if ([data isKindOfClass:[NSMutableDictionary class]]) { + if ([data isKindOfClass:[NSMutableDictionary class]]) { - self->_data = (NSMutableDictionary *) data; + self->_data = (NSMutableDictionary *) data; } - else { - +// else { +// +// self->_data = data.mutableCopy; +// } + + if (!self->_data) { self->_data = data.mutableCopy; } + self->_dataArray = nil; self->_dataDictionary = (NSMutableDictionary *) self->_data; for (NSString *key in self->_dataDictionary.allKeys) { @@ -390,7 +405,8 @@ - (instancetype)initWithArray:(NSArray *)data { self->_dataDictionary = nil; if (!data) { - return self; + //return self; + data = [NSMutableArray arrayWithCapacity:5]; } if (![RollbarDTO isTransferableObject:data]) { @@ -400,9 +416,11 @@ - (instancetype)initWithArray:(NSArray *)data { if ([data isKindOfClass:[NSMutableArray class]]) { self->_data = (NSMutableArray *) data; } - else { + + if (!self->_data) { self->_data = data.mutableCopy; } + self->_dataDictionary = nil; self->_dataArray = (NSMutableArray *) self->_data; for (id item in self->_dataArray) { @@ -425,4 +443,68 @@ - (instancetype)init { return self; } +//-(instancetype)init { +// +// if (self = [self initWithDictionary:@{}]) { +// return self; +// } +// return nil; +//} + + +#pragma mark - NSCopying protocol + +-(id) copyWithZone: (NSZone *) zone { + + NSString *thisClassName = [self rollbar_objectClassName]; + + if (YES == [thisClassName hasPrefix:@"RollbarMutable"]) { + NSBundle *classBundle = [NSBundle bundleForClass:[self class]]; + NSString *immutableClassName = [thisClassName stringByReplacingOccurrencesOfString:@"RollbarMutable" + withString:@"Rollbar"]; + Class immutableClass = [classBundle classNamed:immutableClassName]; + if (immutableClass) { + RollbarDTO *clone = [[immutableClass allocWithZone:zone] initWithJSONString:[self serializeToJSONString]]; + return clone; + } + } + else if ((YES == [thisClassName hasPrefix:@"Rollbar"]) && (NO == [thisClassName hasPrefix:@"RollbarMutable"])) { + NSBundle *classBundle = [NSBundle bundleForClass:[self class]]; + NSString *mutableClassName = [thisClassName stringByReplacingOccurrencesOfString:@"Rollbar" + withString:@"RollbarMutable"]; + Class mutableClass = [classBundle classNamed:mutableClassName]; + if (mutableClass) { + // OPTIMIZATION: + // Since we have a mutable alternative to ths class, + // it means this is an instance of an immutable class. + // Hence, there is no need to clone an immutable object. + return self; + } + } + + RollbarDTO *clone = [[[self class] allocWithZone:zone] initWithJSONString:[self serializeToJSONString]]; + return clone; +} + +#pragma mark - NSMutableCopying protocol + +-(id) mutableCopyWithZone: (NSZone *) zone { + + NSString *thisClassName = [self rollbar_objectClassName]; + + if ((NO == [thisClassName hasPrefix:@"RollbarMutable"]) && (YES == [thisClassName hasPrefix:@"Rollbar"])) { + NSBundle *classBundle = [NSBundle bundleForClass:[self class]]; + NSString *mutableClassName = [thisClassName stringByReplacingOccurrencesOfString:@"Rollbar" + withString:@"RollbarMutable"]; + Class mutableClass = [classBundle classNamed:mutableClassName]; + if (mutableClass) { + RollbarDTO *clone = [[mutableClass allocWithZone:zone] initWithJSONString:[self serializeToJSONString]]; + return clone; + } + } + + RollbarDTO *clone = [[[self class] allocWithZone:zone] initWithJSONString:[self serializeToJSONString]]; + return clone; +} + @end diff --git a/RollbarCommon/Sources/RollbarCommon/NSJSONSerialization+Rollbar.m b/RollbarCommon/Sources/RollbarCommon/NSJSONSerialization+Rollbar.m index 8ab53170..6252540a 100644 --- a/RollbarCommon/Sources/RollbarCommon/NSJSONSerialization+Rollbar.m +++ b/RollbarCommon/Sources/RollbarCommon/NSJSONSerialization+Rollbar.m @@ -1,6 +1,10 @@ #import "NSJSONSerialization+Rollbar.h" #import "RollbarSdkLog.h" +#import +// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html +#define IS_NSOBJECT(_X_) (strchr("@#", @encode(typeof(_X_))[0]) != NULL) + @implementation NSJSONSerialization (Rollbar) NS_ASSUME_NONNULL_BEGIN @@ -42,7 +46,7 @@ + (nullable NSData *)rollbar_dataWithJSONObject:(id)obj } + (nonnull NSMutableDictionary *)rollbar_safeDataFromJSONObject:(nullable id)obj { - + NSMutableDictionary *safeData = [NSMutableDictionary new]; if (nil == obj) { @@ -50,16 +54,21 @@ + (nonnull NSMutableDictionary *)rollbar_safeDataFromJSONObject:(nullable id)obj } [obj enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - // Defensive failsafe to avoid exceptions when trying to create dictionary literal with nil `obj` - obj = obj ?: [NSNull null]; - + + if (obj == nil || !IS_NSOBJECT(obj)) { + obj = [NSNull null]; + } + if ([obj isKindOfClass:[NSDictionary class]]) { [safeData setObject:[[self class] rollbar_safeDataFromJSONObject:obj] forKey:key]; } else if ([obj isKindOfClass:[NSArray class]]) { [safeData setObject:((NSArray *)obj).mutableCopy forKey:key]; - } else if ([NSJSONSerialization isValidJSONObject:@{key:obj}]) { + } else if (obj == [NSNull null] || [obj isKindOfClass:[NSNull class]]) { + + [safeData setObject:obj forKey:key]; + } else if ([NSJSONSerialization isValidJSONObject:obj]) { [safeData setObject:obj forKey:key]; } else if ([obj isKindOfClass:[NSNumber class]]) { diff --git a/RollbarCommon/Sources/RollbarCommon/RollbarBundleUtil.m b/RollbarCommon/Sources/RollbarCommon/RollbarBundleUtil.m index 84a5f920..9b3537ba 100644 --- a/RollbarCommon/Sources/RollbarCommon/RollbarBundleUtil.m +++ b/RollbarCommon/Sources/RollbarCommon/RollbarBundleUtil.m @@ -5,6 +5,9 @@ @implementation RollbarBundleUtil + (nonnull NSString *)detectAppBundleVersion { NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + if (!infoDictionary) { + return @"n/a"; + } NSString *major = infoDictionary[@"CFBundleShortVersionString"]; NSString *minor = infoDictionary[@"CFBundleVersion"]; NSString *version = [NSString stringWithFormat:@"%@.%@", major, minor]; diff --git a/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO.h b/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO.h index 9cb445d4..2aedacdc 100644 --- a/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO.h +++ b/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN /// The foundation for defining Rollbar Data Transfer Objects (DTOs). -@interface RollbarDTO : NSObject { +@interface RollbarDTO : NSObject { @private id _data; //... diff --git a/RollbarCommon/Sources/RollbarCommon/include/RollbarOsUtil.h b/RollbarCommon/Sources/RollbarCommon/include/RollbarOsUtil.h index 56b7eeba..841c0a7a 100644 --- a/RollbarCommon/Sources/RollbarCommon/include/RollbarOsUtil.h +++ b/RollbarCommon/Sources/RollbarCommon/include/RollbarOsUtil.h @@ -21,9 +21,13 @@ NS_ASSUME_NONNULL_BEGIN + (NSTimeInterval)detectOsUptimeInterval; -#pragma mark - utility +#pragma mark - static utility nature -- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; @end diff --git a/RollbarCommon/Tests/RollbarCommonTests/NSJSONSerialization+RollbarTests.swift b/RollbarCommon/Tests/RollbarCommonTests/NSJSONSerialization+RollbarTests.swift index f42172be..d38720ce 100644 --- a/RollbarCommon/Tests/RollbarCommonTests/NSJSONSerialization+RollbarTests.swift +++ b/RollbarCommon/Tests/RollbarCommonTests/NSJSONSerialization+RollbarTests.swift @@ -31,53 +31,41 @@ final class NSJSONSerializationRollbarTests: XCTestCase { } func testNSJSONSerializationRollbar_safeDataFromJSONObject() { - let goldenStandard = "{\"access_token\":\"321\",\"data\":{\"attribute\":\"An attribute\",\"body\":\"Message\",\"date\":\"1970-01-01 00:00:00 +0000\",\"error\":{},\"httpUrlResponse\":{\"header1\":\"Header 1\",\"header2\":\"Header 2\"},\"innerData\":{\"attribute\":\"An attribute\",\"body\":\"Message\",\"date\":\"1970-01-01 00:00:00 +0000\",\"error\":{},\"httpUrlResponse\":{\"header1\":\"Header 1\",\"header2\":\"Header 2\"},\"optionalField\":null,\"scrubFields\":\"secret,CCV,password\",\"url\":\"http:\\/\\/www.apple.com\"},\"optionalField\":null,\"scrubFields\":\"secret,CCV,password\",\"url\":\"http:\\/\\/www.apple.com\"}}"; - let optionalFieldValue: String? = nil var data = [ "body": "Message", - "optionalField": optionalFieldValue as Any, + "optionalField": (nil as String?) as Any, "attribute": "An attribute", - "date": NSDate.init(timeIntervalSince1970: TimeInterval.init()), - "url": NSURL.init(string: "http://www.apple.com")!, - "error": NSError.init(domain: "Error Domain", code: 101, userInfo: nil), - "httpUrlResponse": HTTPURLResponse.init( - url: URL.init(string: "https://www.rollbar.com")!, + "date": Date(timeIntervalSince1970: TimeInterval.init()), + "url": URL(string: "http://www.apple.com")!, + "error": NSError(domain: "Error Domain", code: 101, userInfo: nil), + "httpUrlResponse": HTTPURLResponse( + url: URL(string: "https://www.rollbar.com")!, statusCode: 500, httpVersion: "1.2", headerFields: ["header1": "Header 1", "header2": "Header 2"]) as Any, - "scrubFields": NSSet.init(array: ["password", "secret", "CCV"]), - ] as [String : Any]; - - if #available(OSX 10.13, iOS 11.0, *) { - data["innerData"] = JSONSerialization.rollbar_data( - withJSONObject: data, - options: .sortedKeys, - error: nil, - safe: true) - } else { - // Fallback on earlier versions - XCTFail("Test it on more recent OS version!"); - }; + "scrubFields": NSSet(array: ["password", "secret", "CCV"]), + ] as [String: Any]; + + data["innerData"] = JSONSerialization.rollbar_data( + withJSONObject: data, + options: .sortedKeys, + error: nil, + safe: true) let payload = [ "access_token": "321", "data": data, - ] as [String : Any]; - + ] as [String : Any]; + let safeJson = JSONSerialization.rollbar_safeData(fromJSONObject: payload); do { - if #available(OSX 10.13, iOS 11.0, *) { - let jsonData = try JSONSerialization.data(withJSONObject: safeJson, options: .sortedKeys) - let result = String.init(data: jsonData, encoding: .utf8); - XCTAssertEqual(goldenStandard, result); - } else { - // Fallback on earlier versions - XCTFail("Test it on more recent OS version!"); - }; + let jsonData = try JSONSerialization.data(withJSONObject: safeJson, options: .sortedKeys) + let result = String(data: jsonData, encoding: .utf8); + XCTAssertEqual(goldenStandard, result); } catch { XCTFail("Unexpected failure!"); diff --git a/RollbarDeploys.podspec b/RollbarDeploys.podspec index bdcd7522..e287dccf 100644 --- a/RollbarDeploys.podspec +++ b/RollbarDeploys.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarDeploys" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -53,7 +53,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarKSCrash.podspec b/RollbarKSCrash.podspec index 96765543..47f1bef5 100644 --- a/RollbarKSCrash.podspec +++ b/RollbarKSCrash.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarKSCrash" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -54,7 +54,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarNotifier.podspec b/RollbarNotifier.podspec index 095f3992..4cc9dd05 100644 --- a/RollbarNotifier.podspec +++ b/RollbarNotifier.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarNotifier" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -53,7 +53,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m index 304ded6f..7f483928 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m @@ -14,7 +14,7 @@ #pragma mark - constants -static NSString * const NOTIFIER_VERSION = @"2.3.4"; +static NSString * const NOTIFIER_VERSION = @"2.4.0"; static NSString * const NOTIFIER_NAME = @"rollbar-apple"; @@ -52,10 +52,96 @@ @implementation RollbarConfig +#pragma mark - factory methods + ++ (nonnull RollbarConfig *)configWithAccessToken:(nonnull NSString *)token { + + NSAssert(token, @"Access token must be initialized!"); + NSAssert(token.length > 0, @"Access token must not be empty string!"); + + RollbarConfig *config = [[RollbarConfig alloc] initWithAccessToken:token + environment:nil]; + + return config; +} + ++ (nonnull RollbarConfig *)configWithAccessToken:(nonnull NSString *)token + environment:(nonnull NSString *)env { + + NSAssert(env, @"Environment must be initialized!"); + NSAssert(env.length > 0, @"Environment must not be empty string!"); + + RollbarConfig *config = [[RollbarConfig alloc] initWithAccessToken:token + environment:env]; + + return config; +} + ++ (nonnull RollbarMutableConfig *)mutableConfigWithAccessToken:(nonnull NSString *)token { + + NSAssert(token, @"Access token must be initialized!"); + NSAssert(token.length > 0, @"Access token must not be empty string!"); + + RollbarMutableConfig *config = [[RollbarMutableConfig alloc] initWithAccessToken:token + environment:nil]; + + return config; +} + ++ (nonnull RollbarMutableConfig *)mutableConfigWithAccessToken:(nonnull NSString *)token + environment:(nonnull NSString *)env { + + NSAssert(env, @"Environment must be initialized!"); + NSAssert(env.length > 0, @"Environment must not be empty string!"); + + RollbarMutableConfig *config = [[RollbarMutableConfig alloc] initWithAccessToken:token + environment:env]; + + return config; +} + #pragma mark - initializers -- (instancetype)init { +- (instancetype)initWithAccessToken:(nullable NSString *)token environment:(nullable NSString *)env { + + RollbarDestination *destination = nil; + if (token && env) { + destination = [[RollbarDestination alloc] initWithAccessToken:token environment:env]; + } + else if (token) { + destination = [[RollbarDestination alloc] initWithAccessToken:token]; + } + else { + destination = [[RollbarMutableDestination alloc] init]; + } + + if (!destination) { + return nil; + } + + if (self = [super initWithDictionary:@{ + DFK_DESTINATION:destination.jsonFriendlyData, + DFK_DEVELOPER_OPTIONS:[RollbarDeveloperOptions new].jsonFriendlyData, + DFK_LOGGING_OPTIONS:[RollbarLoggingOptions new].jsonFriendlyData, + DFK_HTTP_PROXY:[RollbarProxy new].jsonFriendlyData, + DFK_HTTPS_PROXY:[RollbarProxy new].jsonFriendlyData, + DFK_DATA_SCRUBBER:[RollbarScrubbingOptions new].jsonFriendlyData, + DFK_TELEMETRY:[RollbarTelemetryOptions new].jsonFriendlyData, + DFK_NOTIFIER:[[RollbarModule alloc] initWithName:NOTIFIER_NAME version:NOTIFIER_VERSION].jsonFriendlyData, + DFK_SERVER:[RollbarServerConfig new].jsonFriendlyData, + DFK_PERSON: [NSMutableDictionary new], + DFK_CUSTOM: [NSMutableDictionary new] + }]) { + + return self; + } + + return nil; +} + +- (instancetype)init { + self = [super initWithDictionary:@{ DFK_DESTINATION:[RollbarDestination new].jsonFriendlyData, DFK_DEVELOPER_OPTIONS:[RollbarDeveloperOptions new].jsonFriendlyData, @@ -72,7 +158,6 @@ - (instancetype)init { return self; } - #pragma mark - Rollbar destination - (RollbarDestination *)destination { @@ -81,37 +166,188 @@ - (RollbarDestination *)destination { return dto; } +#pragma mark - Developer options + +- (RollbarDeveloperOptions *)developerOptions { + id data = [self safelyGetDictionaryByKey:DFK_DEVELOPER_OPTIONS]; + return [[RollbarDeveloperOptions alloc] initWithDictionary:data]; +} + +#pragma mark - Logging options + +- (RollbarLoggingOptions *)loggingOptions { + id data = [self safelyGetDictionaryByKey:DFK_LOGGING_OPTIONS]; + return [[RollbarLoggingOptions alloc] initWithDictionary:data]; +} + +#pragma mark - Notifier + +- (RollbarModule *)notifier { + id data = [self safelyGetDictionaryByKey:DFK_NOTIFIER]; + return [[RollbarModule alloc] initWithDictionary:data]; +} + +#pragma mark - Data scrubber + +- (RollbarScrubbingOptions *)dataScrubber { + id data = [self safelyGetDictionaryByKey:DFK_DATA_SCRUBBER]; + return [[RollbarScrubbingOptions alloc] initWithDictionary:data]; +} + +#pragma mark - Server + +- (RollbarServerConfig *)server { + id data = [self safelyGetDictionaryByKey:DFK_SERVER]; + return [[RollbarServerConfig alloc] initWithDictionary:data]; +} + +#pragma mark - Person + +- (RollbarPerson *)person { + id data = [self safelyGetDictionaryByKey:DFK_PERSON]; + return [[RollbarPerson alloc] initWithDictionary:data]; +} + +#pragma mark - HTTP Proxy Settings + +- (RollbarProxy *)httpProxy { + id data = [self safelyGetDictionaryByKey:DFK_HTTP_PROXY]; + return [[RollbarProxy alloc] initWithDictionary:data]; +} + +#pragma mark - HTTPS Proxy Settings + +- (RollbarProxy *)httpsProxy { + id data = [self safelyGetDictionaryByKey:DFK_HTTPS_PROXY]; + return [[RollbarProxy alloc] initWithDictionary:data]; +} + +#pragma mark - Telemetry + +- (RollbarTelemetryOptions *)telemetry { + id data = [self safelyGetDictionaryByKey:DFK_TELEMETRY]; + return [[RollbarTelemetryOptions alloc] initWithDictionary:data]; +} + +#pragma mark - Custom data + +- (NSDictionary *)customData { + NSMutableDictionary *result = [self safelyGetDictionaryByKey:DFK_CUSTOM]; + return result; +} + +#pragma mark - Payload Content Related + +@synthesize checkIgnoreRollbarData = _checkIgnoreRollbarData; + +@synthesize modifyRollbarData = _modifyRollbarData; + +#pragma mark - overrides + +- (nonnull RollbarMutableConfig *) mutableCopy { + + return [self mutableCopyWithZone:nil]; +} + +//#pragma mark - RollbarPersistent protocol +// +//- (BOOL)loadFromFile:(nonnull NSString *)filePath { +//} +// +//- (BOOL)saveToFile:(nonnull NSString *)filePath { +//} + + +#pragma mark - NSCopying protocol + +-(id) copyWithZone: (NSZone *) zone { + + RollbarConfig *clone = [super copyWithZone:zone]; + if (clone != self) { + clone->_checkIgnoreRollbarData = self->_checkIgnoreRollbarData; + clone->_modifyRollbarData = self->_modifyRollbarData; + } + return clone; +} + +#pragma mark - NSMutableCopying protocol + +-(id) mutableCopyWithZone: (NSZone *) zone { + + RollbarConfig *clone = [super mutableCopyWithZone:zone]; + if (clone != self) { + clone->_checkIgnoreRollbarData = self->_checkIgnoreRollbarData; + clone->_modifyRollbarData = self->_modifyRollbarData; + } + return clone; +} + +@end + +@implementation RollbarMutableConfig + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super init]) { + return self; + } + return nil; +} + +#pragma mark - Payload Content Related + +@dynamic checkIgnoreRollbarData; +@dynamic modifyRollbarData; + +- (void)setCheckIgnoreRollbarData:(BOOL (^)(RollbarData * _Nonnull))checkIgnoreRollbarData { + self->_checkIgnoreRollbarData = checkIgnoreRollbarData; +} + +- (void)setModifyRollbarData:(RollbarData * _Nonnull (^)(RollbarData * _Nonnull))modifyRollbarData { + self->_modifyRollbarData = modifyRollbarData; +} + +#pragma mark - Rollbar destination + +- (RollbarMutableDestination *)destination { + id data = [self safelyGetDictionaryByKey:DFK_DESTINATION]; + id dto = [[RollbarMutableDestination alloc] initWithDictionary:data]; + return dto; +} + - (void)setDestination:(RollbarDestination *)destination { - [self setDataTransferObject:destination forKey:DFK_DESTINATION]; + [self setDataTransferObject:[destination mutableCopy] forKey:DFK_DESTINATION]; } #pragma mark - Developer options -- (RollbarDeveloperOptions *)developerOptions { +- (RollbarMutableDeveloperOptions *)developerOptions { id data = [self safelyGetDictionaryByKey:DFK_DEVELOPER_OPTIONS]; - return [[RollbarDeveloperOptions alloc] initWithDictionary:data]; + return [[RollbarMutableDeveloperOptions alloc] initWithDictionary:data]; } - (void)setDeveloperOptions:(RollbarDeveloperOptions *)developerOptions { - [self setDataTransferObject:developerOptions forKey:DFK_DEVELOPER_OPTIONS]; + [self setDataTransferObject:[developerOptions mutableCopy] forKey:DFK_DEVELOPER_OPTIONS]; } #pragma mark - Logging options -- (RollbarLoggingOptions *)loggingOptions { +- (RollbarMutableLoggingOptions *)loggingOptions { id data = [self safelyGetDictionaryByKey:DFK_LOGGING_OPTIONS]; - return [[RollbarLoggingOptions alloc] initWithDictionary:data]; + return [[RollbarMutableLoggingOptions alloc] initWithDictionary:data]; } -- (void)setLoggingOptions:(RollbarLoggingOptions *)developerOptions { - [self setDataTransferObject:developerOptions forKey:DFK_LOGGING_OPTIONS]; +- (void)setLoggingOptions:(RollbarLoggingOptions *)loggingOptions { + [self setDataTransferObject:[loggingOptions mutableCopy] forKey:DFK_LOGGING_OPTIONS]; } #pragma mark - Notifier -- (RollbarModule *)notifier { +- (RollbarMutableModule *)notifier { id data = [self safelyGetDictionaryByKey:DFK_NOTIFIER]; - return [[RollbarModule alloc] initWithDictionary:data]; + return [[RollbarMutableModule alloc] initWithDictionary:data]; } - (void)setNotifier:(RollbarModule *)developerOptions { @@ -120,31 +356,31 @@ - (void)setNotifier:(RollbarModule *)developerOptions { #pragma mark - Data scrubber -- (RollbarScrubbingOptions *)dataScrubber { +- (RollbarMutableScrubbingOptions *)dataScrubber { id data = [self safelyGetDictionaryByKey:DFK_DATA_SCRUBBER]; - return [[RollbarScrubbingOptions alloc] initWithDictionary:data]; + return [[RollbarMutableScrubbingOptions alloc] initWithDictionary:data]; } - (void)setDataScrubber:(RollbarScrubbingOptions *)value { - [self setDataTransferObject:value forKey:DFK_DATA_SCRUBBER]; + [self setDataTransferObject:[value mutableCopy] forKey:DFK_DATA_SCRUBBER]; } #pragma mark - Server -- (RollbarServerConfig *)server { +- (RollbarMutableServerConfig *)server { id data = [self safelyGetDictionaryByKey:DFK_SERVER]; - return [[RollbarServerConfig alloc] initWithDictionary:data]; + return [[RollbarMutableServerConfig alloc] initWithDictionary:data]; } - (void)setServer:(RollbarServerConfig *)value { - [self setDataTransferObject:value forKey:DFK_SERVER]; + [self setDataTransferObject:[value mutableCopy] forKey:DFK_SERVER]; } #pragma mark - Person -- (RollbarPerson *)person { +- (RollbarMutablePerson *)person { id data = [self safelyGetDictionaryByKey:DFK_PERSON]; - return [[RollbarPerson alloc] initWithDictionary:data]; + return [[RollbarMutablePerson alloc] initWithDictionary:data]; } - (void)setPerson:(RollbarPerson *)value { @@ -153,35 +389,35 @@ - (void)setPerson:(RollbarPerson *)value { #pragma mark - HTTP Proxy Settings -- (RollbarProxy *)httpProxy { +- (RollbarMutableProxy *)httpProxy { id data = [self safelyGetDictionaryByKey:DFK_HTTP_PROXY]; - return [[RollbarProxy alloc] initWithDictionary:data]; + return [[RollbarMutableProxy alloc] initWithDictionary:data]; } - (void)setHttpProxy:(RollbarProxy *)value { - [self setDataTransferObject:value forKey:DFK_HTTP_PROXY]; + [self setDataTransferObject:[value mutableCopy] forKey:DFK_HTTP_PROXY]; } #pragma mark - HTTPS Proxy Settings -- (RollbarProxy *)httpsProxy { +- (RollbarMutableProxy *)httpsProxy { id data = [self safelyGetDictionaryByKey:DFK_HTTPS_PROXY]; - return [[RollbarProxy alloc] initWithDictionary:data]; + return [[RollbarMutableProxy alloc] initWithDictionary:data]; } - (void)setHttpsProxy:(RollbarProxy *)value { - [self setDataTransferObject:value forKey:DFK_HTTPS_PROXY]; + [self setDataTransferObject:[value mutableCopy] forKey:DFK_HTTPS_PROXY]; } #pragma mark - Telemetry -- (RollbarTelemetryOptions *)telemetry { +- (RollbarMutableTelemetryOptions *)telemetry { id data = [self safelyGetDictionaryByKey:DFK_TELEMETRY]; - return [[RollbarTelemetryOptions alloc] initWithDictionary:data]; + return [[RollbarMutableTelemetryOptions alloc] initWithDictionary:data]; } - (void)setTelemetry:(RollbarTelemetryOptions *)value { - [self setDataTransferObject:value forKey:DFK_TELEMETRY]; + [self setDataTransferObject:[value mutableCopy] forKey:DFK_TELEMETRY]; } #pragma mark - Convenience Methods @@ -212,21 +448,21 @@ - (void)setNotifierName:(nullable NSString *)name #pragma mark - Custom data -- (NSDictionary *)customData { +- (NSMutableDictionary *)customData { NSMutableDictionary *result = [self safelyGetDictionaryByKey:DFK_CUSTOM]; return result; } -- (void)setCustomData:(NSDictionary *)value { +- (void)setCustomData:(NSMutableDictionary *)value { [self setDictionary:value forKey:DFK_CUSTOM]; } -//#pragma mark - RollbarPersistent protocol -// -//- (BOOL)loadFromFile:(nonnull NSString *)filePath { -//} -// -//- (BOOL)saveToFile:(nonnull NSString *)filePath { -//} +#pragma mark - overrides + +- (nonnull RollbarConfig *) copy { + + return [self copyWithZone:nil]; +} + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDestination.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDestination.m index fe259455..ff3ffc22 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDestination.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDestination.m @@ -60,24 +60,44 @@ - (NSString *)endpoint { return result; } -- (void)setEndpoint:(NSString *)value { - [self setString:value forKey:DFK_ENDPOINT]; -} - - (NSString *)accessToken { NSString *result = [self safelyGetStringByKey:DFK_ACCESS_TOKEN]; return result; } -- (void)setAccessToken:(NSString *)value { - [self setString:value forKey:DFK_ACCESS_TOKEN]; -} - - (NSString *)environment { NSString *result = [self safelyGetStringByKey:DFK_ENVIRONMENT]; return result; } +@end + +@implementation RollbarMutableDestination + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super init]) { + return self; + } + return nil; +} + +#pragma mark - property accessors + +@dynamic endpoint; +@dynamic accessToken; +@dynamic environment; + +- (void)setEndpoint:(NSString *)value { + [self setString:value forKey:DFK_ENDPOINT]; +} + +- (void)setAccessToken:(NSString *)value { + [self setString:value forKey:DFK_ACCESS_TOKEN]; +} + - (void)setEnvironment:(NSString *)value { [self setString:value forKey:DFK_ENVIRONMENT]; } diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDeveloperOptions.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDeveloperOptions.m index 968b8fac..8dfd570a 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDeveloperOptions.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarDeveloperOptions.m @@ -1,20 +1,27 @@ #import "RollbarDeveloperOptions.h" +#import "../RollbarNotifierFiles.h" #pragma mark - constants static BOOL const DEFAULT_ENABLED_FLAG = YES; static BOOL const DEFAULT_TRANSMIT_FLAG = YES; static BOOL const DEFAULT_SUPPRESS_SDK_INFO_LOGGING_FLAG = NO; -static BOOL const DEFAULT_LOG_PAYLOADS_FLAG = NO; -static NSString * const DEFAULT_PAYLOAD_LOG_FILE = @"rollbar.payloads"; +static BOOL const DEFAULT_LOG_INCOMING_PAYLOADS_FLAG = NO; +static BOOL const DEFAULT_LOG_TRANSMITTED_PAYLOADS_FLAG = NO; +static BOOL const DEFAULT_LOG_DROPPED_PAYLOADS_FLAG = NO; #pragma mark - data field keys static NSString * const DFK_ENABLED = @"enabled"; static NSString * const DFK_TRANSMIT = @"transmit"; static NSString * const DFK_SUPPRESS_SDK_INFO_LOGGING = @"suppressSdkInfoLogging"; -static NSString * const DFK_LOG_PAYLOAD = @"logPayload"; -static NSString * const DFK_LOG_PAYLOAD_FILE = @"logPayloadFile"; + +static NSString * const DFK_LOG_INCOMING_PAYLOADS = @"logIncomingPayloads"; +static NSString * const DFK_LOG_INCOMING_PAYLOADS_FILE = @"logIncomingPayloadsFile"; +static NSString * const DFK_LOG_TRANSMITTED_PAYLOADS = @"logTransmittedPayloads"; +static NSString * const DFK_LOG_TRANSMITTED_PAYLOADS_FILE = @"logTransmittedPayloadsFile"; +static NSString * const DFK_LOG_DROPPED_PAYLOADS = @"logDroppedPayloads"; +static NSString * const DFK_LOG_DROPPED_PAYLOADS_FILE = @"logDroppedPayloadsFile"; #pragma mark - class implementation @@ -24,34 +31,51 @@ @implementation RollbarDeveloperOptions - (instancetype)initWithEnabled:(BOOL)enabled transmit:(BOOL)transmit - logPayload:(BOOL)logPayload - payloadLogFile:(NSString *)payloadLogFile { + logIncomingPayloads:(BOOL)logIncomingPayloads + logTransmittedPayloads:(BOOL)logTransmittedPayloads + logDroppedPayloads:(BOOL)logDroppedPayloads + incomingPayloadsLogFile:(NSString *)logIncomingPayloadsFile + transmittedPayloadsLogFile:(NSString *)logTransmittedPayloadsFile + droppedPayloadsLogFile:(NSString *)logDroppedPayloadsFile { self = [super initWithDictionary:@{ DFK_ENABLED:[NSNumber numberWithBool:enabled], DFK_TRANSMIT:[NSNumber numberWithBool:transmit], DFK_SUPPRESS_SDK_INFO_LOGGING:[NSNumber numberWithBool:DEFAULT_SUPPRESS_SDK_INFO_LOGGING_FLAG], - DFK_LOG_PAYLOAD:[NSNumber numberWithBool:logPayload], - DFK_LOG_PAYLOAD_FILE:payloadLogFile + DFK_LOG_INCOMING_PAYLOADS:[NSNumber numberWithBool:logIncomingPayloads], + DFK_LOG_TRANSMITTED_PAYLOADS:[NSNumber numberWithBool:logTransmittedPayloads], + DFK_LOG_DROPPED_PAYLOADS:[NSNumber numberWithBool:logDroppedPayloads], + DFK_LOG_INCOMING_PAYLOADS_FILE:logIncomingPayloadsFile, + DFK_LOG_TRANSMITTED_PAYLOADS_FILE:logTransmittedPayloadsFile, + DFK_LOG_DROPPED_PAYLOADS_FILE:logDroppedPayloadsFile }]; return self; } - (instancetype)initWithEnabled:(BOOL)enabled transmit:(BOOL)transmit - logPayload:(BOOL)logPayload { + logIncomingPayloads:(BOOL)logIncomingPayloads + logTransmittedPayloads:(BOOL)logTransmittedPayloads + logDroppedPayloads:(BOOL)logDroppedPayloads { return [self initWithEnabled:enabled transmit:transmit - logPayload:logPayload - payloadLogFile:DEFAULT_PAYLOAD_LOG_FILE]; + logIncomingPayloads:logIncomingPayloads + logTransmittedPayloads:logTransmittedPayloads + logDroppedPayloads:logDroppedPayloads + incomingPayloadsLogFile:[RollbarNotifierFiles incomingPayloadsLog] + transmittedPayloadsLogFile:[RollbarNotifierFiles transmittedPayloadsLog] + droppedPayloadsLogFile:[RollbarNotifierFiles droppedPayloadsLog] + ]; } - (instancetype)initWithEnabled:(BOOL)enabled { return [self initWithEnabled:enabled transmit:DEFAULT_TRANSMIT_FLAG - logPayload:DEFAULT_LOG_PAYLOADS_FLAG]; + logIncomingPayloads:DEFAULT_LOG_INCOMING_PAYLOADS_FLAG + logTransmittedPayloads:DEFAULT_LOG_TRANSMITTED_PAYLOADS_FLAG + logDroppedPayloads:DEFAULT_LOG_DROPPED_PAYLOADS_FLAG]; } - (instancetype)init { @@ -65,44 +89,96 @@ - (BOOL)enabled { return [result boolValue]; } -- (void)setEnabled:(BOOL)value { - [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_ENABLED]; -} - - (BOOL)transmit { NSNumber *result = [self safelyGetNumberByKey:DFK_TRANSMIT]; return [result boolValue]; } -- (void)setTransmit:(BOOL)value { - [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_TRANSMIT]; -} - - (BOOL)suppressSdkInfoLogging { NSNumber *result = [self safelyGetNumberByKey:DFK_SUPPRESS_SDK_INFO_LOGGING]; return [result boolValue]; } -- (void)setSuppressSdkInfoLogging:(BOOL)value { - [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_SUPPRESS_SDK_INFO_LOGGING]; +- (BOOL)logIncomingPayloads { + NSNumber *result = [self safelyGetNumberByKey:DFK_LOG_INCOMING_PAYLOADS]; + return [result boolValue]; } -- (BOOL)logPayload { - NSNumber *result = [self safelyGetNumberByKey:DFK_LOG_PAYLOAD]; +- (NSString *)incomingPayloadsLogFile { + NSString *result = [self safelyGetStringByKey:DFK_LOG_INCOMING_PAYLOADS_FILE]; + return result; +} + +- (BOOL)logTransmittedPayloads { + NSNumber *result = [self safelyGetNumberByKey:DFK_LOG_TRANSMITTED_PAYLOADS]; return [result boolValue]; } -- (void)setLogPayload:(BOOL)value { - [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_LOG_PAYLOAD]; +- (NSString *)transmittedPayloadsLogFile { + NSString *result = [self safelyGetStringByKey:DFK_LOG_TRANSMITTED_PAYLOADS_FILE]; + return result; +} + +- (BOOL)logDroppedPayloads { + NSNumber *result = [self safelyGetNumberByKey:DFK_LOG_DROPPED_PAYLOADS]; + return [result boolValue]; } -- (NSString *)payloadLogFile { - NSString *result = [self safelyGetStringByKey:DFK_LOG_PAYLOAD_FILE]; +- (NSString *)droppedPayloadsLogFile { + NSString *result = [self safelyGetStringByKey:DFK_LOG_DROPPED_PAYLOADS_FILE]; return result; } -- (void)setPayloadLogFile:(NSString *)value { - [self setString:value forKey:DFK_LOG_PAYLOAD_FILE]; +@end + +@implementation RollbarMutableDeveloperOptions + +@dynamic enabled; +@dynamic transmit; +@dynamic suppressSdkInfoLogging; +@dynamic logIncomingPayloads; +@dynamic incomingPayloadsLogFile; +@dynamic logTransmittedPayloads; +@dynamic transmittedPayloadsLogFile; +@dynamic logDroppedPayloads; +@dynamic droppedPayloadsLogFile; + +#pragma mark - property accessors + +- (void)setEnabled:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_ENABLED]; +} + +- (void)setTransmit:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_TRANSMIT]; +} + +- (void)setSuppressSdkInfoLogging:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_SUPPRESS_SDK_INFO_LOGGING]; +} + +- (void)setLogIncomingPayloads:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_LOG_INCOMING_PAYLOADS]; +} + +- (void)setIncomingPayloadsLogFile:(NSString *)value { + [self setString:value forKey:DFK_LOG_INCOMING_PAYLOADS_FILE]; +} + +- (void)setLogTransmittedPayloads:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_LOG_TRANSMITTED_PAYLOADS]; +} + +- (void)setTransmittedPayloadsLogFile:(NSString *)value { + [self setString:value forKey:DFK_LOG_TRANSMITTED_PAYLOADS_FILE]; +} + +- (void)setLogDroppedPayloads:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_LOG_DROPPED_PAYLOADS]; +} + +- (void)setDroppedPayloadsLogFile:(NSString *)value { + [self setString:value forKey:DFK_LOG_DROPPED_PAYLOADS_FILE]; } @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarLoggingOptions.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarLoggingOptions.m index fc81ac21..d69ad029 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarLoggingOptions.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarLoggingOptions.m @@ -158,71 +158,108 @@ - (RollbarLevel)logLevel { return [RollbarLevelUtil RollbarLevelFromString:logLevelString]; } -- (void)setLogLevel:(RollbarLevel)level { - NSString *levelString = [RollbarLevelUtil RollbarLevelToString:level]; - [self setString:levelString forKey:DFK_LOG_LEVEL]; -} - - (RollbarLevel)crashLevel { NSString *logLevelString = [self safelyGetStringByKey:DFK_CRASH_LEVEL]; return [RollbarLevelUtil RollbarLevelFromString:logLevelString]; } -- (void)setCrashLevel:(RollbarLevel)level { - NSString *levelString = [RollbarLevelUtil RollbarLevelToString:level]; - [self setString:levelString forKey:DFK_CRASH_LEVEL]; -} - - (NSUInteger)maximumReportsPerMinute { return [self safelyGetUIntegerByKey:DFK_MAX_REPORTS_PER_MINUTE withDefault:DEFAULT_MAX_REPORTS_PER_MINUTE]; } -- (void)setMaximumReportsPerMinute:(NSUInteger)value { - [self setUInteger:value forKey:DFK_MAX_REPORTS_PER_MINUTE]; -} - - (RollbarCaptureIpType)captureIp { NSString *valueString = [self safelyGetStringByKey:DFK_IP_CAPTURE_TYPE]; return [RollbarCaptureIpTypeUtil CaptureIpTypeFromString:valueString]; } +- (nullable NSString *)codeVersion { + return [self getDataByKey:DFK_CODE_VERSION]; +} + +- (nullable NSString *)framework; { + return [self getDataByKey:DFK_FRAMEWORK]; +} + +- (nullable NSString *)requestId { + return [self getDataByKey:DFK_REQUEST_ID]; +} + +- (BOOL)enableOomDetection { + + BOOL result = [self safelyGetBoolByKey:DFK_OOM_DETECTION + withDefault:DEFAULT_OOM_DETECTION]; + return result; +} + +@end + +@implementation RollbarMutableLoggingOptions + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super init]) { + return self; + } + return nil; +} + +#pragma mark - property accessors + +@dynamic logLevel; +@dynamic crashLevel; +@dynamic maximumReportsPerMinute; +@dynamic captureIp; +@dynamic codeVersion; +@dynamic framework; +@dynamic requestId; +@dynamic enableOomDetection; + +- (void)setLogLevel:(RollbarLevel)level { + NSString *levelString = [RollbarLevelUtil RollbarLevelToString:level]; + [self setString:levelString forKey:DFK_LOG_LEVEL]; +} + +- (void)setCrashLevel:(RollbarLevel)level { + NSString *levelString = [RollbarLevelUtil RollbarLevelToString:level]; + [self setString:levelString forKey:DFK_CRASH_LEVEL]; +} + +- (void)setMaximumReportsPerMinute:(NSUInteger)value { + [self setUInteger:value forKey:DFK_MAX_REPORTS_PER_MINUTE]; +} + - (void)setCaptureIp:(RollbarCaptureIpType)value { NSString *valueString = [RollbarCaptureIpTypeUtil CaptureIpTypeToString:value]; [self setString:valueString forKey:DFK_IP_CAPTURE_TYPE]; } -- (nullable NSString *)codeVersion { - return [self getDataByKey:DFK_CODE_VERSION]; -} +//- (nullable NSString *)codeVersion { +// return [self getDataByKey:DFK_CODE_VERSION]; +//} - (void)setCodeVersion:(nullable NSString *)value { [self setData:value byKey:DFK_CODE_VERSION]; } -- (nullable NSString *)framework; { - return [self getDataByKey:DFK_FRAMEWORK]; -} +//- (nullable NSString *)framework; { +// return [self getDataByKey:DFK_FRAMEWORK]; +//} - (void)setFramework:(nullable NSString *)value { [self setData:value byKey:DFK_FRAMEWORK]; } -- (nullable NSString *)requestId { - return [self getDataByKey:DFK_REQUEST_ID]; -} +//- (nullable NSString *)requestId { +// return [self getDataByKey:DFK_REQUEST_ID]; +//} - (void)setRequestId:(nullable NSString *)value { [self setData:value byKey:DFK_REQUEST_ID]; } -- (BOOL)enableOomDetection { - - BOOL result = [self safelyGetBoolByKey:DFK_OOM_DETECTION - withDefault:DEFAULT_OOM_DETECTION]; - return result; -} - - (void)setEnableOomDetection:(BOOL)value { [self setBool:value diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarModule.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarModule.m index 9acf0f3a..1428ed68 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarModule.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarModule.m @@ -18,11 +18,14 @@ @implementation RollbarModule - (instancetype)initWithName:(nullable NSString *)name version:(nullable NSString *)version { - self = [super initWithDictionary:@{ + if (self = [super initWithDictionary:@{ DFK_NAME:name ? name : [NSNull null], DFK_VERSION:version ? version : [NSNull null] - }]; - return self; + }]) { + return self; + } + + return nil;; } - (instancetype)initWithName:(nullable NSString *)name { @@ -36,14 +39,34 @@ - (nullable NSString *)name { return [self getDataByKey:DFK_NAME]; } -- (void)setName:(nullable NSString *)value { - [self setData:value byKey:DFK_NAME]; -} - - (nullable NSString *)version { return [self getDataByKey:DFK_VERSION]; } +@end + + +@implementation RollbarMutableModule + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super initWithDictionary:@{}]) { + return self; + } + return nil; +} + +#pragma mark - property accessors + +@dynamic name; +@dynamic version; + +- (void)setName:(nullable NSString *)value { + [self setData:value byKey:DFK_NAME]; +} + - (void)setVersion:(nullable NSString *)value { [self setData:value byKey:DFK_VERSION]; } diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarPerson.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarPerson.m index 4208154c..9768dba0 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarPerson.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarPerson.m @@ -63,24 +63,45 @@ - (NSString *)ID { return (nil != result) ? result : @""; } -- (void)setID:(NSString *)value { +- (NSString *)username { - [self setData:value byKey:DFK_ID]; + return [self getDataByKey:DFK_USERNAME]; } -- (NSString *)username { +- (NSString *)email { - return [self getDataByKey:DFK_USERNAME]; + return [self getDataByKey:DFK_EMAIL]; } -- (void)setUsername:(NSString *)value { +@end + + +@implementation RollbarMutablePerson + +#pragma mark - initializers + +-(instancetype)init { - [self setData:value byKey:DFK_USERNAME]; + if (self = [super initWithDictionary:@{}]) { + return self; + } + return nil; } -- (NSString *)email { +#pragma mark - property accessors + +@dynamic ID; +@dynamic username; +@dynamic email; + +- (void)setID:(NSString *)value { - return [self getDataByKey:DFK_EMAIL]; + [self setData:value byKey:DFK_ID]; +} + +- (void)setUsername:(NSString *)value { + + [self setData:value byKey:DFK_USERNAME]; } - (void)setEmail:(NSString *)value { diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarProxy.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarProxy.m index dc54804a..a63e8ef4 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarProxy.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarProxy.m @@ -47,25 +47,40 @@ - (BOOL)enabled { return [result boolValue]; } -- (void)setEnabled:(BOOL)value { - [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_ENABLED]; -} - - (NSString *)proxyUrl { NSString *result = [self safelyGetStringByKey:DFK_PROXY_URL]; return result; } -- (void)setProxyUrl:(NSString *)value { - [self setString:value forKey:DFK_PROXY_URL]; -} - - (NSUInteger)proxyPort { NSUInteger result = [self safelyGetUIntegerByKey:DFK_PROXY_PORT withDefault:DEFAULT_PROXY_PORT]; return result; } +@end + +@implementation RollbarMutableProxy + +@dynamic enabled; +@dynamic proxyUrl; +@dynamic proxyPort; + +#pragma mark - property accessors + +- (void)setEnabled:(BOOL)value { + [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_ENABLED]; +} + +//- (NSString *)proxyUrl { +// NSString *result = [self safelyGetStringByKey:DFK_PROXY_URL]; +// return result; +//} + +- (void)setProxyUrl:(NSString *)value { + [self setString:value forKey:DFK_PROXY_URL]; +} + - (void)setProxyPort:(NSUInteger)value { [self setUInteger:value forKey:DFK_PROXY_PORT]; } diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarScrubbingOptions.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarScrubbingOptions.m index 10417026..4a2439d7 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarScrubbingOptions.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarScrubbingOptions.m @@ -33,8 +33,8 @@ - (instancetype)initWithScrubFields:(NSArray *)scrubFields safeListFields:(NSArray *)safeListFields { return [self initWithEnabled:DEFAULT_ENABLED_FLAG - scrubFields:scrubFields - safeListFields:safeListFields + scrubFields:[scrubFields mutableCopy] + safeListFields:[safeListFields mutableCopy] ]; } @@ -65,46 +65,83 @@ - (BOOL)enabled { return [result boolValue]; } +- (NSArray *)scrubFields { + NSArray *result = [self safelyGetArrayByKey:DFK_SCRUB_FIELDS]; + return result; +} + +- (NSArray *)safeListFields { + NSArray *result = [self safelyGetArrayByKey:DFK_SAFELIST_FIELDS]; + return result; +} + +@end + + +@implementation RollbarMutableScrubbingOptions + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super init]) { + return self; + } + return nil; +} + +#pragma mark - property accessors + +@dynamic enabled; + - (void)setEnabled:(BOOL)value { [self setNumber:[[NSNumber alloc] initWithBool:value] forKey:DFK_ENABLED]; } -- (NSArray *)scrubFields { - NSArray *result = [self safelyGetArrayByKey:DFK_SCRUB_FIELDS]; +- (NSMutableArray *)scrubFields { + NSMutableArray *result = [self safelyGetArrayByKey:DFK_SCRUB_FIELDS]; return result; } - (void)setScrubFields:(NSArray *)scrubFields { - [self setArray:scrubFields forKey:DFK_SCRUB_FIELDS]; + [self setArray:[scrubFields mutableCopy] forKey:DFK_SCRUB_FIELDS]; } -- (void)addScrubField:(NSString *)field { - self.scrubFields = - [self.scrubFields arrayByAddingObject:field]; +- (NSMutableArray *)safeListFields { + NSMutableArray *result = [self safelyGetArrayByKey:DFK_SAFELIST_FIELDS]; + return result; } -- (void)removeScrubField:(NSString *)field { - NSMutableArray *mutableCopy = self.scrubFields.mutableCopy; - [mutableCopy removeObject:field]; - self.scrubFields = mutableCopy.copy; +- (void)setSafeListFields:(NSArray *)whitelistFields { + [self setArray:[whitelistFields mutableCopy] forKey:DFK_SAFELIST_FIELDS]; } -- (NSArray *)safeListFields { - NSArray *result = [self safelyGetArrayByKey:DFK_SAFELIST_FIELDS]; - return result; + +#pragma mark - methods + +- (void)addScrubField:(NSString *)field { +// self.scrubFields = +// [self.scrubFields arrayByAddingObject:field]; + [self.scrubFields addObject:field]; } -- (void)setSafeListFields:(NSArray *)whitelistFields { - [self setArray:whitelistFields forKey:DFK_SAFELIST_FIELDS]; +- (void)removeScrubField:(NSString *)field { +// NSMutableArray *mutableCopy = self.scrubFields.mutableCopy; +// [mutableCopy removeObject:field]; +// self.scrubFields = mutableCopy.copy; + [self.scrubFields removeObject:field]; } - (void)addScrubSafeListField:(NSString *)field { - self.safeListFields = [self.safeListFields arrayByAddingObject:field]; +// self.safeListFields = [self.safeListFields arrayByAddingObject:field]; + [self.safeListFields addObject:field]; } - (void)removeScrubSafeListField:(NSString *)field { - NSMutableArray *mutableCopy = self.safeListFields.mutableCopy; - [mutableCopy removeObject:field]; - self.safeListFields = mutableCopy.copy; +// NSMutableArray *mutableCopy = self.safeListFields.mutableCopy; +// [mutableCopy removeObject:field]; +// self.safeListFields = mutableCopy.copy; + [self.safeListFields removeObject:field]; } + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarServerConfig.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarServerConfig.m index e0ecf296..1219bc6b 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarServerConfig.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarServerConfig.m @@ -46,29 +46,57 @@ - (nullable NSString *)host { return [self getDataByKey:DFK_HOST]; } +- (nullable NSString *)root { + return [self getDataByKey:DFK_ROOT]; +} + +- (nullable NSString *)branch { + return [self getDataByKey:DFK_BRANCH]; +} + +- (nullable NSString *)codeVersion { + return [self getDataByKey:DFK_CODE_VERSION]; +} + +@end + + +@implementation RollbarMutableServerConfig + +@dynamic host; +@dynamic root; +@dynamic branch; +@dynamic codeVersion; + +#pragma mark - property accessors + +//- (nullable NSString *)host { +// return [self getDataByKey:DFK_HOST]; +//} + - (void)setHost:(nullable NSString *)value { [self setData:value byKey:DFK_HOST]; } -- (nullable NSString *)root { - return [self getDataByKey:DFK_ROOT]; -} +//- (nullable NSString *)root { +// return [self getDataByKey:DFK_ROOT]; +//} - (void)setRoot:(nullable NSString *)value { [self setData:value byKey:DFK_ROOT]; } -- (nullable NSString *)branch { - return [self getDataByKey:DFK_BRANCH]; -} +//- (nullable NSString *)branch { +// return [self getDataByKey:DFK_BRANCH]; +//} - (void)setBranch:(nullable NSString *)value { [self setData:value byKey:DFK_BRANCH]; } -- (nullable NSString *)codeVersion { - return [self getDataByKey:DFK_CODE_VERSION]; -} +//- (nullable NSString *)codeVersion { +// return [self getDataByKey:DFK_CODE_VERSION]; +//} - (void)setCodeVersion:(nullable NSString *)value { [self setData:value byKey:DFK_CODE_VERSION]; diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m index 33d6eb87..48627c88 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m @@ -73,63 +73,97 @@ - (BOOL)enabled { return result.boolValue; } -- (void)setEnabled:(BOOL)value { - - [self setNumber:[NSNumber numberWithBool:value] - forKey:DFK_ENABLED_FLAG - ]; -} - - (BOOL)captureLog { NSNumber *result = [self safelyGetNumberByKey:DFK_CAPTURE_LOG_FLAG]; return result.boolValue; } -- (void)setCaptureLog:(BOOL)value { - [self setNumber:[NSNumber numberWithBool:value] - forKey:DFK_CAPTURE_LOG_FLAG - ]; -} - - (BOOL)captureConnectivity { NSNumber *result = [self safelyGetNumberByKey:DFK_CAPTURE_CONNECTIVITY_FLAG]; return result.boolValue; } -- (void)setCaptureConnectivity:(BOOL)value { - [self setNumber:[NSNumber numberWithBool:value] - forKey:DFK_CAPTURE_CONNECTIVITY_FLAG - ]; -} - - (NSUInteger)maximumTelemetryData { NSUInteger result = [self safelyGetUIntegerByKey:DFK_MAX_TELEMETRY_DATA withDefault:DEFAULT_MAX_TELEMETRY_DATA]; return result; } -- (void)setMaximumTelemetryData:(NSUInteger)value { - [self setUInteger:value forKey:DFK_MAX_TELEMETRY_DATA]; -} - - (RollbarScrubbingOptions *)viewInputsScrubber { id data = [self safelyGetDictionaryByKey:DFK_VIEW_INPUTS_SCRUBBER]; id dto = [[RollbarScrubbingOptions alloc] initWithDictionary:data]; return dto; } -- (void)setViewInputsScrubber:(RollbarScrubbingOptions *)scrubber { - [self setDataTransferObject:scrubber forKey:DFK_VIEW_INPUTS_SCRUBBER]; -} - - (NSTimeInterval)memoryStatsAutocollectionInterval { NSTimeInterval result = [self safelyGetTimeIntervalByKey:DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS withDefault:DEFAULT_AUTOCOLLECTION_INTERVAL_MEM_STATS]; return result; } +@end + + +@implementation RollbarMutableTelemetryOptions + +#pragma mark - initializers + +-(instancetype)init { + + if (self = [super initWithEnabled:DEFAULT_ENABLED_FLAG + captureLog:DEFAULT_CAPTURE_LOG_FLAG + captureConnectivity:DEFAULT_CAPTURE_CONNECTIVITY_FLAG + viewInputsScrubber:[RollbarMutableScrubbingOptions new] + ]) { + return self; + } + + return nil; +} + +#pragma mark - property accessors + +@dynamic enabled; +@dynamic captureLog; +@dynamic captureConnectivity; +@dynamic maximumTelemetryData; +@dynamic memoryStatsAutocollectionInterval; + +- (void)setEnabled:(BOOL)value { + + [self setNumber:[NSNumber numberWithBool:value] + forKey:DFK_ENABLED_FLAG + ]; +} + +- (void)setCaptureLog:(BOOL)value { + [self setNumber:[NSNumber numberWithBool:value] + forKey:DFK_CAPTURE_LOG_FLAG + ]; +} + +- (void)setCaptureConnectivity:(BOOL)value { + [self setNumber:[NSNumber numberWithBool:value] + forKey:DFK_CAPTURE_CONNECTIVITY_FLAG + ]; +} + +- (void)setMaximumTelemetryData:(NSUInteger)value { + [self setUInteger:value forKey:DFK_MAX_TELEMETRY_DATA]; +} + - (void)setMemoryStatsAutocollectionInterval:(NSTimeInterval)value { [self setTimeInterval:value forKey:DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS]; } +- (RollbarMutableScrubbingOptions *)viewInputsScrubber { + id data = [self safelyGetDictionaryByKey:DFK_VIEW_INPUTS_SCRUBBER]; + id dto = [[RollbarMutableScrubbingOptions alloc] initWithDictionary:data]; + return dto; +} + +- (void)setViewInputsScrubber:(RollbarScrubbingOptions *)scrubber { + [self setDataTransferObject:[scrubber mutableCopy] forKey:DFK_VIEW_INPUTS_SCRUBBER]; +} + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m b/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m index b997b00e..55aece33 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m +++ b/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m @@ -11,25 +11,30 @@ #import "RollbarScrubbingOptions.h" #import "RollbarCrashProcessor.h" #import "RollbarSession.h" +#import "RollbarThread.h" +#import "RollbarLoggingOptions.h" -@implementation Rollbar - -static RollbarLogger *logger = nil; -static RollbarTelemetryOptionsObserver *telemetryOptionsObserver = nil; -static id crashCollector = nil; -static RollbarCrashProcessor *crashProcessor = nil; - -+ (void)initialize { - - if (self == [Rollbar class]) { - - telemetryOptionsObserver = [RollbarTelemetryOptionsObserver new]; - } - - //[[RollbarSession sharedInstance] registerApplicationHooks]; +static void uncaughtExceptionHandler(NSException * _Nonnull exception) { + NSArray *backtrace = [exception callStackSymbols]; + // NSString *platform = [[UIDevice currentDevice] platform]; + // NSString *version = [[UIDevice currentDevice] systemVersion]; + // NSString *message = [NSString stringWithFormat:@"Device: %@. OS: %@. Backtrace:\n%@", + // platform, + // version, + // backtrace]; + NSString *message = [NSString stringWithFormat:@"Backtrace:\n%@", + backtrace]; + //TODO: complete implementation by calling RollbarInfrastructure logging method to capture the exception... + [[RollbarInfrastructure sharedInstance].logger log:RollbarLevel_Critical + exception:exception + data:nil + context:message + ]; } +@implementation Rollbar + + (void)initWithAccessToken:(NSString *)accessToken { [Rollbar initWithAccessToken:accessToken @@ -65,140 +70,89 @@ + (void)initWithAccessToken:(nullable NSString *)accessToken configuration:(nullable RollbarConfig *)configuration crashCollector:(nullable id)crashCollector { - if (logger) { - - RollbarSdkLog(@"Rollbar has already been initialized."); - } else { + RollbarMutableConfig *config = configuration ? [configuration mutableCopy] : [RollbarMutableConfig new]; + if (accessToken && accessToken.length > 0) { - RollbarConfig *config = configuration ? configuration : [RollbarConfig new]; - if (accessToken && accessToken.length > 0) { - - config.destination.accessToken = accessToken; - } - [Rollbar updateConfiguration:config]; - - if (crashCollector) { - - crashProcessor = [[RollbarCrashProcessor alloc] init]; - [crashCollector collectCrashReportsWithObserver:crashProcessor]; - } - - RollbarCrashReportCheck crashRepoertCheck = ^() { - - BOOL result = NO; - if (crashProcessor.totalProcessedReports > 0) { - - result = YES; - } - return result; - }; - [[RollbarSession sharedInstance] enableOomMonitoring:config.loggingOptions.enableOomDetection - withCrashCheck:crashRepoertCheck]; + config.destination.accessToken = accessToken; } -} -+ (RollbarConfig *)currentConfiguration { - - return logger.configuration; + [[RollbarInfrastructure sharedInstance] configureWith:config + andCrashCollector:crashCollector]; } -+ (RollbarLogger *)currentLogger { ++ (RollbarConfig *)configuration { - return logger; + return [RollbarInfrastructure sharedInstance].configuration; } -+ (void)updateConfiguration:(RollbarConfig *)configuration { - - NSUInteger oldReportingRate = 0; - - if (logger) { - oldReportingRate = logger.configuration.loggingOptions.maximumReportsPerMinute; - [logger updateConfiguration:configuration]; - } - else { - logger = [[RollbarLogger alloc] initWithConfiguration:configuration]; - } - - if (oldReportingRate != configuration.loggingOptions.maximumReportsPerMinute) { - [logger updateReportingRate:configuration.loggingOptions.maximumReportsPerMinute]; - } - - if (configuration && configuration.telemetry) { - - [[RollbarTelemetry sharedInstance] configureWithOptions:configuration.telemetry]; - } -} ++ (void)updateWithConfiguration:(RollbarConfig *)configuration { -+ (void)reapplyConfiguration { - - RollbarConfig *config = Rollbar.currentConfiguration; - if (nil != config) { - [Rollbar updateConfiguration:config]; - } + [[RollbarInfrastructure sharedInstance] configureWith:configuration + andCrashCollector:nil]; } #pragma mark - Logging methods + (void)logCrashReport:(NSString *)crashReport { - [logger logCrashReport:crashReport]; + [[RollbarInfrastructure sharedInstance].logger logCrashReport:crashReport]; } + (void)log:(RollbarLevel)level message:(NSString *)message { - [logger log:level - message:message - data:nil - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + message:message + data:nil + context:nil]; } + (void)log:(RollbarLevel)level exception:(NSException *)exception { - [logger log:level - exception:exception - data:nil - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + exception:exception + data:nil + context:nil]; } + (void)log:(RollbarLevel)level error:(NSError *)error { - [logger log:level - error:error - data:nil - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + error:error + data:nil + context:nil]; } + (void)log:(RollbarLevel)level message:(NSString *)message data:(NSDictionary *)data { - [logger log:level - message:message - data:data - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + message:message + data:data + context:nil]; } + (void)log:(RollbarLevel)level exception:(NSException *)exception data:(NSDictionary *)data { - [logger log:level - exception:exception - data:data - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + exception:exception + data:data + context:nil]; } + (void)log:(RollbarLevel)level error:(NSError *)error data:(NSDictionary *)data { - [logger log:level - error:error - data:data - context:nil]; + [[RollbarInfrastructure sharedInstance].logger log:level + error:error + data:data + context:nil]; } + (void)log:(RollbarLevel)level @@ -206,10 +160,10 @@ + (void)log:(RollbarLevel)level data:(NSDictionary *)data context:(NSString *)context { - [logger log:level - message:message - data:data - context:context]; + [[RollbarInfrastructure sharedInstance].logger log:level + message:message + data:data + context:context]; } + (void)log:(RollbarLevel)level @@ -217,10 +171,10 @@ + (void)log:(RollbarLevel)level data:(NSDictionary *)data context:(NSString *)context { - [logger log:level - exception:exception - data:data - context:context]; + [[RollbarInfrastructure sharedInstance].logger log:level + exception:exception + data:data + context:context]; } + (void)log:(RollbarLevel)level @@ -228,10 +182,10 @@ + (void)log:(RollbarLevel)level data:(NSDictionary *)data context:(NSString *)context { - [logger log:level - error:error - data:data - context:context]; + [[RollbarInfrastructure sharedInstance].logger log:level + error:error + data:data + context:context]; } #pragma mark - Convenience logging methods @@ -472,11 +426,15 @@ + (void)criticalError:(NSError *)error data:(NSDictionary *)data + (void)sendJsonPayload:(NSData *)payload { - [logger sendPayload:payload]; + [[RollbarThread sharedInstance] sendPayload:payload + usingConfig:[Rollbar configuration] + ]; } + #pragma mark - Telemetry API + #pragma mark - Dom + (void)recordViewEventForLevel:(RollbarLevel)level diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarConfigUtil.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarConfigUtil.m index 7539bd4e..0c709c53 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarConfigUtil.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarConfigUtil.m @@ -2,10 +2,10 @@ #import "RollbarConfigUtil.h" #import "RollbarConfig.h" +#import "RollbarNotifierFiles.h" #pragma mark - constants -static NSString * const CONFIGURATION_FILENAME = @"rollbar.config"; static NSString *configurationDirectory = nil; static NSString *configurationFilePath = nil; @@ -18,13 +18,13 @@ + (void)initialize { if (self == [RollbarConfigUtil class]) { configurationDirectory = [RollbarCachesDirectory directory]; - configurationFilePath = [configurationDirectory stringByAppendingPathComponent:CONFIGURATION_FILENAME]; + configurationFilePath = [configurationDirectory stringByAppendingPathComponent:[RollbarNotifierFiles config]]; } } + (nonnull NSString *)getDefaultConfigFileName { - return CONFIGURATION_FILENAME; + return [RollbarNotifierFiles config]; } + (nonnull NSString *)getDefaultConfigDirectory { diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.h new file mode 100644 index 00000000..f0dafd3b --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.h @@ -0,0 +1,45 @@ +// +// RollbarDestinationRecord.h +// +// +// Created by Andrey Kornich on 2022-06-28. +// + +#import + +#import "RollbarConfig.h" +#import "RollbarPayloadPostReply.h" + +@class RollbarRegistry; + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarDestinationRecord : NSObject + +@property (readonly, nonnull) NSString *destinationID; + +@property (readwrite) NSUInteger localWindowLimit; + +@property (readonly) NSUInteger localWindowCount; +@property (readonly) NSUInteger serverWindowRemainingCount; +@property (readonly, nullable) NSDate *nextLocalWindowStart; +@property (readonly, nullable) NSDate *nextServerWindowStart; +@property (readonly, nonnull) NSDate *nextEarliestPost; + +@property (readonly, nonnull) RollbarRegistry *registry; + +- (BOOL)canPost; +- (BOOL)canPostWithConfig:(nonnull RollbarConfig *)config; +- (void)recordPostReply:(nullable RollbarPayloadPostReply *)reply; + +- (instancetype)initWithConfig:(nonnull RollbarConfig *)config + andRegistry:(nonnull RollbarRegistry *)registry +NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDestinationID:(nonnull NSString *)destinationID + andRegistry:(nonnull RollbarRegistry *)registry +NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.m new file mode 100644 index 00000000..ccd91b86 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarDestinationRecord.m @@ -0,0 +1,182 @@ +#import "RollbarDestinationRecord.h" +#import "RollbarRegistry.h" + +@implementation RollbarDestinationRecord { + @private +} + +#pragma mark - property accessors + +#pragma mark - initializers + +- (instancetype)initWithConfig:(nonnull RollbarConfig *)config + andRegistry:(nonnull RollbarRegistry *)registry { + + NSAssert(config, @"Config can not be nil!"); + NSAssert(config.destination, @"Config destination can not be nil!"); + NSAssert(registry, @"Registry can not be nil!"); + + if (self = [super init]) { + + self->_registry = registry; + self->_destinationID = [RollbarRegistry destinationID:config.destination]; + self->_localWindowLimit = config.loggingOptions.maximumReportsPerMinute; + self->_localWindowCount = 0; + self->_serverWindowRemainingCount = 0; + self->_nextLocalWindowStart = nil; + self->_nextServerWindowStart = nil; + self->_nextEarliestPost = [NSDate date]; + } +} + +- (instancetype)initWithDestinationID:(nonnull NSString *)destinationID + andRegistry:(nonnull RollbarRegistry *)registry { + + if (self = [super init]) { + + self->_registry = registry; + self->_destinationID = destinationID; + self->_localWindowLimit = 0; + self->_localWindowCount = 0; + self->_serverWindowRemainingCount = 0; + self->_nextLocalWindowStart = nil; + self->_nextServerWindowStart = nil; + self->_nextEarliestPost = [NSDate date]; + } + return self; +} + +#pragma mark - methods + +- (BOOL)canPost { + + if (!self->_nextEarliestPost) { + return NO; + } + + if (NSOrderedDescending == [self->_nextEarliestPost compare:[NSDate date]]) { + return NO; + } + else { + return YES; + } +} + +- (BOOL)canPostWithConfig:(nonnull RollbarConfig *)config { + + + if (self->_nextLocalWindowStart && (self->_localWindowCount >= config.loggingOptions.maximumReportsPerMinute)) { + // we already exceeded local rate limits, let's wait till the next local rate limiting window: + //self->_nextEarliestPost = self->_nextLocalWindowStart; + return NO; + } + + if (!self->_nextEarliestPost) { + return NO; + } + + if (NSOrderedDescending == [self->_nextEarliestPost compare:[NSDate date]]) { + return NO; + } + else { + return YES; + } +} + +- (void)recordPostReply:(nullable RollbarPayloadPostReply *)reply { + + if (!reply) { + //no response from the server to our lates POST of a payload, + //let's hold on on posting to the destination for 1 minute: + self->_nextEarliestPost = [NSDate dateWithTimeIntervalSinceNow:60]; + self->_localWindowCount = 0; + self->_serverWindowRemainingCount = 0; + self->_nextLocalWindowStart = self->_nextEarliestPost; + self->_nextServerWindowStart = nil; + return; // nothing else to do... + } + + switch(reply.statusCode) { + case 429: // too many requests + self->_nextLocalWindowStart = [NSDate dateWithTimeIntervalSinceNow:reply.remainingSeconds];; + self->_serverWindowRemainingCount = 0; + break; + case 403: // access denied + case 404: // not found + //let's hold on on posting to the destination for 1 minute: + self->_nextEarliestPost = [NSDate dateWithTimeIntervalSinceNow:60]; + self->_localWindowCount = 0; + self->_serverWindowRemainingCount = 0; + self->_nextLocalWindowStart = self->_nextEarliestPost; + self->_nextServerWindowStart = nil; + return; // nothing else to do... + case 200: // OK + case 400: // bad request + case 413: // request entity too large + case 422: // unprocessable entity + default: + self->_nextServerWindowStart = [NSDate dateWithTimeIntervalSinceNow:reply.remainingSeconds];; + self->_serverWindowRemainingCount = reply.remainingCount; + if (self->_nextLocalWindowStart ) { + self->_localWindowCount = 0; + self->_nextLocalWindowStart = [NSDate dateWithTimeIntervalSinceNow:60]; + } + else { + self->_localWindowCount += 1; + } + break; + } + + // since we got here, let's calculate value for self->_nextEarliestPost: + + if (self->_nextServerWindowStart && (0 == self->_serverWindowRemainingCount)) { + // server told us to wait until next rate limiting window: + self->_nextEarliestPost = self->_nextServerWindowStart; + return; + } + + if (self->_nextLocalWindowStart && (self->_localWindowCount >= self->_localWindowLimit)) { + // we already exceeded local rate limits, let's wait till the next local rate limiting window: + self->_nextEarliestPost = self->_nextLocalWindowStart; + return; + } + + // looks like no limits are in force now, keep sending ASAP: + self->_nextEarliestPost = [NSDate date]; + return; +} + +#pragma mark - overrides + +- (nonnull NSString *)description { + NSString *description = [NSString stringWithFormat:@"%@:\n" + " destinationID: %@\n" + " localWindowLimit: %lu\n" + " localWindowCount: %lu\n" + " serverWindowRemainingCount: %lu\n" + " nextLocalWindowStart: %@\n" + " nextServerWindowStart: %@\n" + " nextEarliestPost: %@\n" + " canPost: %@\n" + , + super.description, + self->_destinationID, + self->_localWindowLimit, + self->_localWindowCount, + self->_serverWindowRemainingCount, + self->_nextLocalWindowStart, + self->_nextServerWindowStart, + self->_nextEarliestPost, + [self canPost] ? @"YES" : @"NO" + ]; + return description; +} + +//- (NSString *)debugDescription { +// NSString *description = [NSString stringWithFormat:@"totalLoggerRecords: %lu", +// (unsigned long)[self totalLoggerRecords] +// ]; +// return description; +//} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarInfrastructure.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarInfrastructure.m new file mode 100644 index 00000000..72589e23 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarInfrastructure.m @@ -0,0 +1,170 @@ +#import "RollbarInfrastructure.h" +#import "RollbarConfig.h" +#import "RollbarLoggerProtocol.h" +#import "RollbarLogger.h" +#import "RollbarNotifierFiles.h" +#import "RollbarCrashProcessor.h" +#import "RollbarSession.h" +#import "RollbarTelemetry.h" + +@implementation RollbarInfrastructure { + @private + RollbarConfig *_configuration; + RollbarLogger *_logger; + RollbarCrashProcessor *_crashProcessor; +} + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance { + + static id singleton; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + + singleton = [RollbarInfrastructure new]; + + RollbarSdkLog(@"%@ singleton created!", + [RollbarInfrastructure rollbar_objectClassName] + ); + }); + + return singleton; +} + +#pragma mark - instance methods + +- (nonnull instancetype)configureWith:(nonnull RollbarConfig *)config { + + return [self configureWith:config + andCrashCollector:nil]; +} + +- (nonnull instancetype)configureWith:(nonnull RollbarConfig *)config + andCrashCollector:(nullable id)crashCollector { + + [self assertValidConfiguration:config]; + + if (self->_configuration + && self->_configuration.modifyRollbarData == config.modifyRollbarData + && self->_configuration.checkIgnoreRollbarData == config.checkIgnoreRollbarData + && [[self->_configuration serializeToJSONString] isEqualToString:[config serializeToJSONString]] + ) { + return self; // no need to reconfigure with an identical configuration... + } + + self->_configuration = [config copy]; + self->_logger = nil; //will be created as needed using the current self->_configuration... + + RollbarCrashReportCheck crashReportCheck = nil; + if (crashCollector) { + + self->_crashProcessor = [RollbarCrashProcessor new]; + [crashCollector collectCrashReportsWithObserver:self->_crashProcessor]; + crashReportCheck = ^() { + + BOOL result = NO; + if (self->_crashProcessor.totalProcessedReports > 0) { + + result = YES; + } + return result; + }; + } + + [[RollbarSession sharedInstance] enableOomMonitoring:config.loggingOptions.enableOomDetection + withCrashCheck:crashReportCheck]; + + [[RollbarTelemetry sharedInstance] configureWithOptions:config.telemetry]; + + RollbarSdkLog(@"%@ is configured with this RollbarConfig instance: \n%@ \nand crash collector %@", + [RollbarInfrastructure rollbar_objectClassName], + config, + crashCollector + ); + + return self; +} + +- (nonnull id)createLogger { + + return [self createLoggerWithConfig:self.configuration]; +} + +- (nonnull id)createLoggerWithConfig:(nonnull RollbarConfig *)config { + + RollbarLogger *logger = [RollbarLogger loggerWithConfiguration:config]; + return logger; +} + +- (nonnull id)createLoggerWithAccessToken:(nonnull NSString *)token + andEnvironment:(nonnull NSString *)env { + + RollbarMutableConfig *config = [self.configuration mutableCopy]; + config.destination.accessToken = token; + config.destination.environment = env; + id logger = [self createLoggerWithConfig:config]; + return logger; +} + +- (nonnull id)createLoggerWithAccessToken:(nonnull NSString *)token { + + RollbarMutableConfig *config = [self.configuration mutableCopy]; + config.destination.accessToken = token; + id logger = [self createLoggerWithConfig:config]; + return logger; +} + +#pragma mark - properties + +- (nonnull RollbarConfig *)configuration { + + [self assertValidConfiguration:self->_configuration]; + if (YES == [self hasValidConfiguration]) { + return self->_configuration; + } + else { + [self raiseNotConfiguredException]; + return nil; + } +} + +- (nonnull id)logger { + + if (!self->_logger) { + self->_logger = [RollbarLogger loggerWithConfiguration:self.configuration]; + } + + return self->_logger; +} + +#pragma mark - internal methods + +- (void)raiseNotConfiguredException { + [NSException raise:@"RollbarInfrastructureNotConfiguredException" + format:@"Make sure the [[RollbarInfrastructure sharedInstance] configureWith:...] is called " + "providing a valid RollbarConfig instance!" + ]; +} + +- (BOOL)hasValidConfiguration { + + if (!self->_configuration) { + return NO; + } + + //TODO: complete full validation implementation... + + return YES; +} + +- (void)assertValidConfiguration:(nullable RollbarConfig *)config { + + NSAssert(config, + @"Provide valid configuration via [[RollbarInfrastructure sharedInstance] configureWith:...]!"); + + //TODO: complete full validation implementation... +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger+Test.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger+Test.m new file mode 100644 index 00000000..8c47b1c5 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger+Test.m @@ -0,0 +1,137 @@ +#import "RollbarLogger+Test.h" +#import "RollbarNotifierFiles.h" +#import "RollbarThread.h" + +@import RollbarCommon; + +@implementation RollbarLogger (Test) + ++ (void)clearSdkDataStore { + + [RollbarLogger _clearFile:[RollbarLogger _logPayloadsStorePath]]; + [RollbarLogger _clearFile:[RollbarLogger _telemetryItemsStorePath]]; + + [RollbarLogger _clearFile:[RollbarLogger _incomingPayloadsLogPath]]; + [RollbarLogger _clearFile:[RollbarLogger _transmittedPayloadsLogPath]]; + [RollbarLogger _clearFile:[RollbarLogger _droppedPayloadsLogPath]]; +} + + ++ (void)clearSdkFile:(nonnull NSString *)sdkFileName { + + [RollbarLogger _clearFile:[RollbarLogger _getSDKDataFilePath:sdkFileName]]; +} + ++ (nonnull NSArray *)readPayloadsFromSdkIncomingLog { + + [RollbarLogger readPayloadsDataFromFile:[RollbarLogger _incomingPayloadsLogPath]]; +} + ++ (nonnull NSArray *)readPayloadsFromSdkTransmittedLog { + + [RollbarLogger readPayloadsDataFromFile:[RollbarLogger _transmittedPayloadsLogPath]]; +} + ++ (nonnull NSArray *)readPayloadsFromSdkDroppedLog { + + [RollbarLogger readPayloadsDataFromFile:[RollbarLogger _droppedPayloadsLogPath]]; +} + ++ (nonnull NSArray *)readPayloadsDataFromFile:(nonnull NSString *)filePath { + + RollbarFileReader *reader = [[RollbarFileReader alloc] initWithFilePath:filePath + andOffset:0]; + + NSMutableArray *items = [NSMutableArray array]; + [reader enumerateLinesUsingBlock:^(NSString *line, NSUInteger nextOffset, BOOL *stop) { + NSError *error = nil; + NSMutableDictionary *payload = + [NSJSONSerialization JSONObjectWithData:[line dataUsingEncoding:NSUTF8StringEncoding] + options:(NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves) + error:&error + ]; + if ((nil == payload) && (nil != error)) { + RollbarSdkLog(@"Error serializing log item from the store: %@", [error localizedDescription]); + return; + } + else if (nil == payload) { + RollbarSdkLog(@"Error serializing log item from the store!"); + return; + } + + NSMutableDictionary *data = payload[@"data"]; + [items addObject:data]; + }]; + + return items; +} + ++ (void)flushRollbarThread { + + [RollbarLogger performSelector:@selector(_test_doNothing) + onThread:[RollbarLogger _test_rollbarThread] + withObject:nil + waitUntilDone:YES + ]; +} + ++ (void)_clearFile:(nonnull NSString *)filePath { + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + BOOL fileExists = [fileManager fileExistsAtPath:filePath]; + + if (fileExists) { + BOOL success = [fileManager removeItemAtPath:filePath + error:&error]; + if (!success) { + NSLog(@"Error: %@", [error localizedDescription]); + } + [[NSFileManager defaultManager] createFileAtPath:filePath + contents:nil + attributes:nil]; + } +} + ++ (nonnull NSString *)_logPayloadsStorePath { + + return [RollbarLogger _getSDKDataFilePath:[RollbarNotifierFiles payloadsStore]]; +} + ++ (nonnull NSString *)_telemetryItemsStorePath { + + return [RollbarLogger _getSDKDataFilePath:[RollbarNotifierFiles telemetryQueue]]; +} + ++ (nonnull NSString *)_incomingPayloadsLogPath { + + return [RollbarLogger _getSDKDataFilePath:[RollbarNotifierFiles incomingPayloadsLog]]; +} + ++ (nonnull NSString *)_transmittedPayloadsLogPath { + + return [RollbarLogger _getSDKDataFilePath:[RollbarNotifierFiles transmittedPayloadsLog]]; +} + ++ (nonnull NSString *)_droppedPayloadsLogPath { + + return [RollbarLogger _getSDKDataFilePath:[RollbarNotifierFiles droppedPayloadsLog]]; +} + ++ (nonnull NSString *)_getSDKDataFilePath:(nonnull NSString *)sdkFileName { + + NSString *cachesDirectory = [RollbarCachesDirectory directory]; + return [cachesDirectory stringByAppendingPathComponent:sdkFileName]; +} + ++ (NSThread *)_test_rollbarThread { + + return [RollbarThread sharedInstance]; +} + ++ (void)_test_doNothing { + + // no-Op simulation... +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m index c2a67291..852c8a21 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m @@ -7,194 +7,110 @@ @import RollbarCommon; #import "RollbarLogger.h" -#import "RollbarLogger+Test.h" #import "RollbarThread.h" #import "RollbarTelemetryThread.h" #import "RollbarReachability.h" -#import +#import "RollbarPayloadFactory.h" #import "RollbarTelemetry.h" #import "RollbarPayloadTruncator.h" #import "RollbarConfig.h" +#import "RollbarNotifierFiles.h" #import "RollbarPayloadDTOs.h" #define MAX_PAYLOAD_SIZE 128 // The maximum payload size in kb -static NSString * const PAYLOADS_FILE_NAME = @"rollbar.payloads"; -static NSString * const QUEUED_ITEMS_FILE_NAME = @"rollbar.items"; -static NSString * const QUEUED_ITEMS_STATE_FILE_NAME = @"rollbar.state"; - -/// Rollbar API Service enforced payload rate limit: -static NSString * const RESPONSE_HEADER_RATE_LIMIT = @"x-rate-limit-limit"; -/// Rollbar API Service enforced remaining payload count until the limit is reached: -static NSString * const RESPONSE_HEADER_REMAINING_COUNT = @"x-rate-limit-remaining"; -/// Rollbar API Service enforced rate limit reset time for the current limit window: -static NSString * const RESPONSE_HEADER_RESET_TIME = @"x-rate-limit-reset"; -/// Rollbar API Service enforced rate limit remaining seconds of the current limit window: -static NSString * const RESPONSE_HEADER_REMAINING_SECONDS = @"x-rate-limit-remaining-seconds"; - -static NSUInteger MAX_RETRY_COUNT = 5; - -static NSString *payloadsFilePath = nil; -static NSString *oomDetectionFilePath = nil; +static NSString *transmittedPayloadsFilePath = nil; +static NSString *droppedPayloadsFilePath = nil; static NSString *queuedItemsFilePath = nil; -static NSString *stateFilePath = nil; -static NSMutableDictionary *queueState = nil; - -static RollbarThread *rollbarThread = nil; - -#if !TARGET_OS_WATCH -static RollbarReachability *reachability = nil; -static BOOL isNetworkReachable = YES; -#endif @implementation RollbarLogger { - NSDate *nextSendTime; @private NSDictionary *m_osData; } -static RollbarLogger *sharedSingleton = nil; +@synthesize configuration; -/// This is essentially a static constructor for the type. -+ (void)initialize { - - if (self == [RollbarLogger class]) { - - // create working cache directory: - [RollbarCachesDirectory ensureCachesDirectoryExists]; - NSString *cachesDirectory = [RollbarCachesDirectory directory]; - - // make sure we have all the data files set: - queuedItemsFilePath = - [cachesDirectory stringByAppendingPathComponent:QUEUED_ITEMS_FILE_NAME]; - stateFilePath = - [cachesDirectory stringByAppendingPathComponent:QUEUED_ITEMS_STATE_FILE_NAME]; - payloadsFilePath = - [cachesDirectory stringByAppendingPathComponent:PAYLOADS_FILE_NAME]; +#pragma mark - factory methods - // either create or overwrite the payloads log file: - [[NSFileManager defaultManager] createFileAtPath:payloadsFilePath - contents:nil - attributes:nil]; - - // create the queued items file if does not exist already: - if (![[NSFileManager defaultManager] fileExistsAtPath:queuedItemsFilePath]) { - [[NSFileManager defaultManager] createFileAtPath:queuedItemsFilePath - contents:nil - attributes:nil]; - } - - // create state tracking file if does not exist already: - if ([[NSFileManager defaultManager] fileExistsAtPath:stateFilePath]) { - NSData *stateData = [NSData dataWithContentsOfFile:stateFilePath]; - if (stateData) { - NSDictionary *state = [NSJSONSerialization JSONObjectWithData:stateData - options:0 - error:nil]; - queueState = [state mutableCopy]; - } else { - RollbarSdkLog(@"There was an error restoring saved queue state"); - } - } - if (!queueState) { - queueState = [@{@"offset": [NSNumber numberWithUnsignedInt:0], - @"retry_count": [NSNumber numberWithUnsignedInt:0]} mutableCopy]; - [self saveQueueState]; - } - - RollbarConfig *config = [RollbarConfig new]; - - // Setup the worker thread that sends the items that have been queued up in the item file set above: - // TODO: !!! this needs to be redesigned taking in account multiple access tokens and endpoints !!! - RollbarLogger *logger = [[RollbarLogger alloc] initWithConfiguration:config]; - rollbarThread = - [[RollbarThread alloc] initWithNotifier:logger - reportingRate:config.loggingOptions.maximumReportsPerMinute]; - [rollbarThread start]; - -#if !TARGET_OS_WATCH - // Listen for reachability status so that the items are only sent when the internet is available: - reachability = [RollbarReachability reachabilityForInternetConnection]; - isNetworkReachable = [reachability isReachable]; - reachability.reachableBlock = ^(RollbarReachability*reach) { - [logger captureTelemetryDataForNetwork:true]; - isNetworkReachable = YES; - }; - reachability.unreachableBlock = ^(RollbarReachability*reach) { - [logger captureTelemetryDataForNetwork:false]; - isNetworkReachable = NO; - }; - - [reachability startNotifier]; -#endif - } ++ (instancetype)loggerWithAccessToken:(nonnull NSString *)accessToken { + return [[RollbarLogger alloc] initWithAccessToken:accessToken]; } -+ (nonnull RollbarLogger *)sharedInstance { - @synchronized (self) { - if (sharedSingleton == nil) { - sharedSingleton = [[self alloc] init]; - } - return sharedSingleton; - } ++ (instancetype)loggerWithAccessToken:(nonnull NSString *)accessToken + andEnvironment:(nonnull NSString *)environment { + return [[RollbarLogger alloc] initWithAccessToken:accessToken andEnvironment:environment]; } -+ (nonnull RollbarLogger *)createLoggerWithConfig:(nonnull RollbarConfig *)config { - return [[self alloc] initWithConfiguration:config]; ++ (instancetype)loggerWithConfiguration:(nonnull RollbarConfig *)configuration { + return [[RollbarLogger alloc] initWithConfiguration:configuration]; } -/// Designated notifier initializer -/// @param accessToken the access token +#pragma mark - initializers + - (instancetype)initWithAccessToken:(NSString *)accessToken { - RollbarConfig *config = [RollbarConfig new]; + RollbarMutableConfig *config = [RollbarMutableConfig new]; config.destination.accessToken = accessToken; return [self initWithConfiguration:config]; } -/// Designated notifier initializer -/// @param configuration the config object -- (instancetype)initWithConfiguration:(RollbarConfig *)configuration { +- (instancetype)initWithAccessToken:(nonnull NSString *)accessToken + andEnvironment:(nonnull NSString *)environment { + RollbarMutableConfig *config = [RollbarMutableConfig new]; + config.destination.accessToken = accessToken; + config.destination.environment = environment; + return [self initWithConfiguration:config]; +} +- (instancetype)initWithConfiguration:(nonnull RollbarConfig *)configuration { + if ((self = [super init])) { [self updateConfiguration:configuration]; - + NSString *cachesDirectory = [RollbarCachesDirectory directory]; - if (nil != self.configuration.developerOptions.payloadLogFile - && self.configuration.developerOptions.payloadLogFile.length > 0) { + + if (nil != self.configuration.developerOptions.transmittedPayloadsLogFile + && self.configuration.developerOptions.transmittedPayloadsLogFile.length > 0) { - payloadsFilePath = - [cachesDirectory stringByAppendingPathComponent:self.configuration.developerOptions.payloadLogFile]; + transmittedPayloadsFilePath = + [cachesDirectory stringByAppendingPathComponent:self.configuration.developerOptions.transmittedPayloadsLogFile]; } else { - payloadsFilePath = - [cachesDirectory stringByAppendingPathComponent:PAYLOADS_FILE_NAME]; + transmittedPayloadsFilePath = + [cachesDirectory stringByAppendingPathComponent:[RollbarNotifierFiles transmittedPayloadsLog]]; } - self->nextSendTime = [[NSDate alloc] init]; + if (nil != self.configuration.developerOptions.droppedPayloadsLogFile + && self.configuration.developerOptions.droppedPayloadsLogFile.length > 0) { + + droppedPayloadsFilePath = + [cachesDirectory stringByAppendingPathComponent:self.configuration.developerOptions.droppedPayloadsLogFile]; + } + else { + + droppedPayloadsFilePath = + [cachesDirectory stringByAppendingPathComponent:[RollbarNotifierFiles droppedPayloadsLog]]; + } } - + return self; } +#pragma mark - logging methods + - (void)logCrashReport:(NSString *)crashReport { - RollbarConfig *config = self.configuration; - - if (YES == [self shouldSkipReporting:config.loggingOptions.crashLevel]) { + if (YES == [self shouldSkipReporting:self.configuration.loggingOptions.crashLevel]) { return; } - RollbarPayload *payload = [self buildRollbarPayloadWithLevel:config.loggingOptions.crashLevel - message:nil - exception:nil - error:nil - extra:nil - crashReport:crashReport - context:nil]; + RollbarPayloadFactory *payloadFactory = [RollbarPayloadFactory factoryWithConfig:self.configuration]; + + RollbarPayload *payload = [payloadFactory payloadWithLevel:self.configuration.loggingOptions.crashLevel + crashReport:crashReport]; [self report:payload]; } @@ -207,14 +123,12 @@ - (void)log:(RollbarLevel)level return; } - RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level - message:message - exception:nil - error:nil - extra:data - crashReport:nil - context:context - ]; + RollbarPayloadFactory *payloadFactory = [RollbarPayloadFactory factoryWithConfig:self.configuration]; + + RollbarPayload *payload = [payloadFactory payloadWithLevel:level + message:message + data:data + context:context]; [self report:payload]; } @@ -227,14 +141,12 @@ - (void)log:(RollbarLevel)level return; } - RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level - message:nil - exception:exception - error:nil - extra:data - crashReport:nil - context:context - ]; + RollbarPayloadFactory *payloadFactory = [RollbarPayloadFactory factoryWithConfig:self.configuration]; + + RollbarPayload *payload = [payloadFactory payloadWithLevel:level + exception:exception + data:data + context:context]; [self report:payload]; } @@ -247,14 +159,12 @@ - (void)log:(RollbarLevel)level return; } - RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level - message:nil - exception:nil - error:error - extra:data - crashReport:nil - context:context - ]; + RollbarPayloadFactory *payloadFactory = [RollbarPayloadFactory factoryWithConfig:self.configuration]; + + RollbarPayload *payload = [payloadFactory payloadWithLevel:level + error:error + data:data + context:context]; [self report:payload]; } @@ -273,813 +183,19 @@ - (BOOL)shouldSkipReporting:(RollbarLevel)level { return NO; } -- (void)report:(RollbarPayload *)payload { - if (payload) { - [self queuePayload:payload.jsonFriendlyData]; - } -} - -+ (void)saveQueueState { - NSError *error; - NSData *data = [NSJSONSerialization rollbar_dataWithJSONObject:queueState - options:0 - error:&error - safe:true]; - if (error) { - RollbarSdkLog(@"Error: %@", [error localizedDescription]); - } - [data writeToFile:stateFilePath atomically:YES]; -} - -- (void)processSavedItems { - -#if !TARGET_OS_WATCH - if (!isNetworkReachable) { - RollbarSdkLog(@"Processing saved items: no network!"); - // Don't attempt sending if the network is known to be not reachable - return; - } -#endif - - NSUInteger startOffset = [queueState[@"offset"] unsignedIntegerValue]; - - NSFileHandle *fileHandle = - [NSFileHandle fileHandleForReadingAtPath:queuedItemsFilePath]; - [fileHandle seekToEndOfFile]; - __block unsigned long long fileLength = [fileHandle offsetInFile]; - [fileHandle closeFile]; - - if (!fileLength) { - if (NO == self.configuration.developerOptions.suppressSdkInfoLogging) { - RollbarSdkLog(@"Processing saved items: no queued items in the file!"); - } - return; - } - - // Empty out the queued item file if all items have been processed already - if (startOffset == fileLength) { - [@"" writeToFile:queuedItemsFilePath - atomically:YES - encoding:NSUTF8StringEncoding - error:nil]; - - queueState[@"offset"] = [NSNumber numberWithUnsignedInteger:0]; - queueState[@"retry_count"] = [NSNumber numberWithUnsignedInteger:0]; - [RollbarLogger saveQueueState]; - if (NO == self.configuration.developerOptions.suppressSdkInfoLogging) { - RollbarSdkLog(@"Processing saved items: emptied the queued items file."); - } - - return; - } - - // Iterate through the items file and send the items in batches. - RollbarFileReader *reader = - [[RollbarFileReader alloc] initWithFilePath:queuedItemsFilePath - andOffset:startOffset]; - [reader enumerateLinesUsingBlock:^(NSString *line, NSUInteger nextOffset, BOOL *stop) { - NSData *lineData = [line dataUsingEncoding:NSUTF8StringEncoding]; - if (!lineData) { - // All we can do is ignore this line - RollbarSdkLog(@"Error converting file line to NSData: %@", line); - return; - } - NSError *error; - NSJSONReadingOptions serializationOptions = (NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves); - NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:lineData - options:serializationOptions - error:&error]; - - if (!payload) { - // Ignore this line if it isn't valid json and proceed to the next line - RollbarSdkLog(@"Error restoring data from file to JSON: %@", error); - RollbarSdkLog(@"Raw data from file failed conversion to JSON:"); - RollbarSdkLog(@"%@", lineData); -// RollbarSdkLog(@" error code: %@", error.code); -// RollbarSdkLog(@" error domain: %@", error.domain); -// RollbarSdkLog(@" error description: %@", error.description); -// RollbarSdkLog(@" error localized description: %@", error.localizedDescription); -// RollbarSdkLog(@" error failure reason: %@", error.localizedFailureReason); -// RollbarSdkLog(@" error recovery option: %@", error.localizedRecoveryOptions); -// RollbarSdkLog(@" error recovery suggestion: %@", error.localizedRecoverySuggestion); - return; - } - - BOOL shouldContinue = [self sendItem:payload nextOffset:nextOffset]; - - if (!shouldContinue) { - // Stop processing the file so that the current file offset will be - // retried next time the file is processed - *stop = YES; - return; - } - - // The file has had items added since we started iterating through it, - // update the known file length to equal the next offset - if (nextOffset > fileLength) { - fileLength = nextOffset; - } - - }]; -} - -#pragma mark - Payload DTO builders - --(RollbarPerson *)buildRollbarPerson { - - RollbarConfig *config = self.configuration; - if (config && config.person && config.person.ID) { - return config.person; - } - else { - return nil; - } -} - --(RollbarServer *)buildRollbarServer { - - RollbarConfig *config = self.configuration; - if (config && config.server && !config.server.isEmpty) { - return [[RollbarServer alloc] initWithCpu:nil - serverConfig:config.server]; - } - else { - return nil; - } -} - --(NSDictionary *)buildOSData { - - if (self->m_osData) { - return self->m_osData; - } - - NSBundle *mainBundle = [NSBundle mainBundle]; - - NSString *version = nil; - RollbarConfig *config = self.configuration; - if (config - && config.loggingOptions - && config.loggingOptions.codeVersion - && config.loggingOptions.codeVersion.length > 0) { - - version = config.loggingOptions.codeVersion; - } - else { - version = [mainBundle objectForInfoDictionaryKey:(NSString*)kCFBundleVersionKey]; - } - - NSString *shortVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - NSString *bundleName = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]; - NSString *bundleIdentifier = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey]; - - struct utsname systemInfo; - uname(&systemInfo); - NSString *deviceCode = [NSString stringWithCString:systemInfo.machine - encoding:NSUTF8StringEncoding]; - -#if TARGET_OS_IOS | TARGET_OS_TV | TARGET_OS_MACCATALYST - self->m_osData = @{ - @"os": @"iOS", - @"os_version": [[UIDevice currentDevice] systemVersion], - @"device_code": deviceCode, - @"code_version": version ? version : @"", - @"short_version": shortVersion ? shortVersion : @"", - @"bundle_identifier": bundleIdentifier ? bundleIdentifier : @"", - @"app_name": bundleName ? bundleName : @"" - }; -#else - NSOperatingSystemVersion osVer = [[NSProcessInfo processInfo] operatingSystemVersion]; - self->m_osData = @{ - @"os": @"macOS", - @"os_version": [NSString stringWithFormat:@" %tu.%tu.%tu", - osVer.majorVersion, - osVer.minorVersion, - osVer.patchVersion - ], - @"device_code": deviceCode, - @"code_version": version ? version : @"", - @"short_version": shortVersion ? shortVersion : @"", - @"bundle_identifier": bundleIdentifier ? bundleIdentifier : @"", - @"app_name": bundleName ? bundleName : [[NSProcessInfo processInfo] processName] - }; -#endif - - return self->m_osData; -} - --(RollbarClient *)buildRollbarClient { - - NSNumber *timestamp = [NSNumber numberWithInteger:[[NSDate date] timeIntervalSince1970]]; - - RollbarConfig *config = self.configuration; - if (config && config.loggingOptions) { - switch(config.loggingOptions.captureIp) { - case RollbarCaptureIpType_Full: - return [[RollbarClient alloc] initWithDictionary:@{ - @"timestamp": timestamp, - @"ios": [self buildOSData], - @"user_ip": @"$remote_ip" - }]; - case RollbarCaptureIpType_Anonymize: - return [[RollbarClient alloc] initWithDictionary:@{ - @"timestamp": timestamp, - @"ios": [self buildOSData], - @"user_ip": @"$remote_ip_anonymize" - }]; - case RollbarCaptureIpType_None: - //no op - break; - } - } - - return [[RollbarClient alloc] initWithDictionary:@{ - @"timestamp": timestamp, - @"ios": [self buildOSData], - }]; -} - --(RollbarModule *)buildRollbarNotifierModule { - - RollbarConfig *config = self.configuration; - if (config && config.notifier && !config.notifier.isEmpty) { - - RollbarModule *notifierModule = - [[RollbarModule alloc] initWithDictionary:config.notifier.jsonFriendlyData.copy]; - [notifierModule setData:config.jsonFriendlyData byKey:@"configured_options"]; - return notifierModule; - } - - return nil; -} - --(RollbarPayload *)buildRollbarPayloadWithLevel:(RollbarLevel)level - message:(NSString *)message - exception:(NSException *)exception - error:(NSError *)error - extra:(NSDictionary *)extra - crashReport:(NSString *)crashReport - context:(NSString *)context { - - // check critical config settings: - RollbarConfig *config = self.configuration; - if (!config - || !config.destination - || !config.destination.environment - || config.destination.environment.length == 0) { - - return nil; - } - - // compile payload data proper body: - RollbarBody *body = [RollbarBody alloc]; - if (crashReport) { - body = [body initWithCrashReport:crashReport]; - } - else if (error) { - body = [body initWithError:error]; - } - else if (exception) { - body = [body initWithException:exception]; - } - else if (message) { - body = [body initWithMessage:message]; - } - else { - return nil; - } - - if (!body) { - return nil; - } - - // this is done only for backward compatibility for customers that used to rely on this undocumented - // extra data with a message: - if (message && extra) { - [body.message setData:extra byKey:@"extra"]; - } - - // compile payload data: - RollbarData *data = [[RollbarData alloc] initWithEnvironment:config.destination.environment - body:body]; - if (!data) { - return nil; - } - - NSMutableDictionary *customData = - [NSMutableDictionary dictionaryWithDictionary:self.configuration.customData]; - if (crashReport || exception) { - // neither crash report no exception payload objects have placeholders for any extra data - // or an extra message, let's preserve them as the custom data: - if (extra) { - customData[@"error.extra"] = extra; - } - if (message && message.length > 0) { - customData[@"error.message"] = message; - } - } - - data.level = level; - data.language = RollbarAppLanguage_ObjectiveC; - data.platform = @"client"; - data.uuid = [NSUUID UUID]; - data.custom = [[RollbarDTO alloc] initWithDictionary:customData]; - data.notifier = [self buildRollbarNotifierModule]; - data.person = [self buildRollbarPerson]; - data.server = [self buildRollbarServer]; - data.client = [self buildRollbarClient]; - if (context && context.length > 0) { - data.context = context; - } - if (config.loggingOptions) { - data.framework = config.loggingOptions.framework; - if (config.loggingOptions.requestId - && (config.loggingOptions.requestId.length > 0)) { - - [data setData:config.loggingOptions.requestId byKey:@"requestId"]; - } - } - - // Transform payload data, if necessary - if ([self shouldIgnoreRollbarData:data]) { - return nil; - } - data = [self modifyRollbarData:data]; - data = [self scrubRollbarData:data]; - - RollbarPayload *payload = [[RollbarPayload alloc] initWithAccessToken:config.destination.accessToken - data:data]; - - return payload; -} - --(BOOL)shouldIgnoreRollbarData:(nonnull RollbarData *)incomingData { - - BOOL shouldIgnore = NO; - if (self.configuration.checkIgnoreRollbarData) { - @try { - shouldIgnore = self.configuration.checkIgnoreRollbarData(incomingData); - return shouldIgnore; - } @catch(NSException *e) { - RollbarSdkLog(@"checkIgnore error: %@", e.reason); - - // Remove checkIgnore to prevent future exceptions - self.configuration.checkIgnoreRollbarData = nil; - return NO; - } - } - - return shouldIgnore; -} - --(RollbarData *)modifyRollbarData:(nonnull RollbarData *)incomingData { - - if (self.configuration.modifyRollbarData) { - return self.configuration.modifyRollbarData(incomingData); - } - return incomingData; -} - --(RollbarData *)scrubRollbarData:(nonnull RollbarData *)incomingData { - - NSSet *scrubFieldsSet = [self getScrubFields]; - if (!scrubFieldsSet || scrubFieldsSet.count == 0) { - return incomingData; - } - - NSMutableDictionary *mutableJsonFriendlyData = incomingData.jsonFriendlyData.mutableCopy; - for (NSString *key in scrubFieldsSet) { - if ([mutableJsonFriendlyData valueForKeyPath:key]) { - [self createMutablePayloadWithData:mutableJsonFriendlyData forPath:key]; - [mutableJsonFriendlyData setValue:@"*****" forKeyPath:key]; - } - } - - return [[RollbarData alloc] initWithDictionary:mutableJsonFriendlyData]; -} - --(NSSet *)getScrubFields { - - RollbarConfig *config = self.configuration; - if (!config - || !config.dataScrubber - || config.dataScrubber.isEmpty - || !config.dataScrubber.enabled - || !config.dataScrubber.scrubFields - || config.dataScrubber.scrubFields.count == 0) { - - return [NSSet set]; - } - - NSMutableSet *actualFieldsToScrub = config.dataScrubber.scrubFields.mutableCopy; - if (config.dataScrubber.safeListFields.count > 0) { - // actualFieldsToScrub = - // config.dataScrubber.scrubFields - config.dataScrubber.whitelistFields - // while using case insensitive field name comparison: - actualFieldsToScrub = [NSMutableSet new]; - for(NSString *key in config.dataScrubber.scrubFields) { - BOOL isWhitelisted = false; - for (NSString *whiteKey in config.dataScrubber.safeListFields) { - if (NSOrderedSame == [key caseInsensitiveCompare:whiteKey]) { - isWhitelisted = true; - } - } - if (!isWhitelisted) { - [actualFieldsToScrub addObject:key]; - } - } - } - - return actualFieldsToScrub; -} - -#pragma mark - LEGACY payload data builders - -- (void)queuePayload:(NSDictionary *)payload { - - [self performSelector:@selector(queuePayload_OnlyCallOnThread:) - onThread:rollbarThread - withObject:payload - waitUntilDone:NO - ]; -} +#pragma mark - Payload queueing/reporting -- (void)queuePayload_OnlyCallOnThread:(NSDictionary *)payload { - - NSError *error = nil; - NSData *data = [NSJSONSerialization rollbar_dataWithJSONObject:payload - options:0 - error:&error - safe:true]; - if (nil == data) { - - RollbarSdkLog(@"Couldn't generate and save JSON data from: %@", payload); - if (error) { - - RollbarSdkLog(@" Error: %@", [error localizedDescription]); - } - return; - } - - [RollbarFileWriter appendSafelyData:data toFile:queuedItemsFilePath]; - - [[RollbarTelemetry sharedInstance] clearAllData]; -} - -- (BOOL)sendItem:(NSDictionary *)payload - nextOffset:(NSUInteger)nextOffset { - - RollbarPayload *rollbarPayload = - [[RollbarPayload alloc] initWithDictionary:[payload copy]]; - if (nil == rollbarPayload) { - - RollbarSdkLog( - @"Couldn't init and send RollbarPayload with data: %@", - payload - ); -// queueState[@"offset"] = [NSNumber numberWithUnsignedInteger:nextOffset]; -// queueState[@"retry_count"] = [NSNumber numberWithUnsignedInteger:0]; -// [RollbarLogger saveQueueState]; - return YES; // no retry needed - } - - NSMutableDictionary *newPayload = - [NSMutableDictionary dictionaryWithDictionary:payload]; - [RollbarPayloadTruncator truncatePayload:newPayload]; - if (nil == newPayload) { - - RollbarSdkLog( - @"Couldn't send truncated payload that is nil" - ); -// queueState[@"offset"] = [NSNumber numberWithUnsignedInteger:nextOffset]; -// queueState[@"retry_count"] = [NSNumber numberWithUnsignedInteger:0]; -// [RollbarLogger saveQueueState]; - return YES; // no retry needed - } - - NSError *error; - NSData *jsonPayload = [NSJSONSerialization rollbar_dataWithJSONObject:newPayload - options:0 - error:&error - safe:true]; - if (nil == jsonPayload) { - - RollbarSdkLog( - @"Couldn't send jsonPayload that is nil" - ); - if (nil != error) { - - RollbarSdkLog( - @" DETAILS: an error while generating JSON data: %@", - error - ); - } -// queueState[@"offset"] = [NSNumber numberWithUnsignedInteger:nextOffset]; -// queueState[@"retry_count"] = [NSNumber numberWithUnsignedInteger:0]; -// [RollbarLogger saveQueueState]; - return YES; // no retry needed - } - - if (NSOrderedDescending != [nextSendTime compare: [[NSDate alloc] init] ]) { - - NSUInteger retryCount = - [queueState[@"retry_count"] unsignedIntegerValue]; - - RollbarConfig *rollbarConfig = - [[RollbarConfig alloc] initWithDictionary:rollbarPayload.data.notifier.jsonFriendlyData[@"configured_options"]]; - - if (0 == retryCount && YES == rollbarConfig.developerOptions.logPayload) { - if (NO == rollbarConfig.developerOptions.suppressSdkInfoLogging) { - RollbarSdkLog(@"About to send payload: %@", - [[NSString alloc] initWithData:jsonPayload - encoding:NSUTF8StringEncoding] - ); - } - - // - save this payload into a proper payloads log file: - //***************************************************** - - // compose the payloads log file path: - NSString *cachesDirectory = [RollbarCachesDirectory directory]; - NSString *payloadsLogFilePath = - [cachesDirectory stringByAppendingPathComponent:rollbarConfig.developerOptions.payloadLogFile]; - - [RollbarFileWriter appendSafelyData:jsonPayload toFile:payloadsLogFilePath]; - } - - BOOL success = - rollbarConfig ? [self sendPayload:jsonPayload usingConfig:rollbarConfig] - : [self sendPayload:jsonPayload]; // backward compatibility with just upgraded very old SDKs... - - if (!success) { - - if (retryCount < MAX_RETRY_COUNT) { - - queueState[@"retry_count"] = - [NSNumber numberWithUnsignedInteger:retryCount + 1]; - - [RollbarLogger saveQueueState]; - - // Return NO so that the current batch will be retried next time - return NO; - } - } - } - else { - - RollbarSdkLog( - @"Omitting payload until nextSendTime is reached: %@", - [[NSString alloc] initWithData:jsonPayload encoding:NSUTF8StringEncoding] - ); - } - - queueState[@"offset"] = [NSNumber numberWithUnsignedInteger:nextOffset]; - queueState[@"retry_count"] = [NSNumber numberWithUnsignedInteger:0]; - [RollbarLogger saveQueueState]; - - return YES; -} +- (void)report:(RollbarPayload *)payload { -- (BOOL)sendPayload:(nonnull NSData *)payload - usingConfig:(nonnull RollbarConfig *)config { - - if ((nil == payload) - || (nil == self.configuration) - || (nil == self.configuration.destination) - || (nil == self.configuration.destination.endpoint) - || (nil == self.configuration.destination.accessToken) - || (0 == self.configuration.destination.endpoint.length) - || (0 == self.configuration.destination.accessToken.length) - ) { + if (payload) { - return NO; - } - - NSURL *url = [NSURL URLWithString:config.destination.endpoint]; - if (nil == url) { - return NO; - } - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - - [request setHTTPMethod:@"POST"]; - [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [request setValue:config.destination.accessToken forHTTPHeaderField:@"X-Rollbar-Access-Token"]; - [request setHTTPBody:payload]; - - if (YES == config.developerOptions.logPayload) { - NSString *payloadString = [[NSString alloc]initWithData:payload - encoding:NSUTF8StringEncoding - ]; - NSLog(@"%@", payloadString); - //TODO: if config.developerOptions.logPayloadFile is defined, save the payload into the file... - } - - if (NO == config.developerOptions.transmit) { - return YES; // we just successfully short-circuit here... - } - - __block BOOL result = NO; - - // This requires iOS 7.0+ - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - - NSURLSession *session = [NSURLSession sharedSession]; - - if (config.httpProxy.enabled - || config.httpsProxy.enabled) { - - NSDictionary *connectionProxyDictionary = - @{ - @"HTTPEnable" : [NSNumber numberWithBool:config.httpProxy.enabled], - @"HTTPProxy" : config.httpProxy.proxyUrl, - @"HTTPPort" : [NSNumber numberWithUnsignedInteger:config.httpProxy.proxyPort], - @"HTTPSEnable" : [NSNumber numberWithBool:config.httpsProxy.enabled], - @"HTTPSProxy" : config.httpsProxy.proxyUrl, - @"HTTPSPort" : [NSNumber numberWithUnsignedInteger:config.httpsProxy.proxyPort] - }; - - NSURLSessionConfiguration *sessionConfig = - [NSURLSessionConfiguration ephemeralSessionConfiguration]; - sessionConfig.connectionProxyDictionary = connectionProxyDictionary; - session = [NSURLSession sessionWithConfiguration:sessionConfig]; - } - - NSURLSessionDataTask *dataTask = - [session dataTaskWithRequest:request - completionHandler:^( - NSData * _Nullable data, - NSURLResponse * _Nullable response, - NSError * _Nullable error) { - result = [self checkPayloadResponse:response - error:error - data:data]; - dispatch_semaphore_signal(sem); - }]; - [dataTask resume]; - - dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - - return result; -} - -/// This is a DEPRECATED method left for some backward compatibility for very old clients eventually moving to this more recent implementation. -/// Use/maintain sendPayload:usingConfig: instead! -- (BOOL)sendPayload:(NSData *)payload { - - if ((nil == payload) - || (nil == self.configuration) - || (nil == self.configuration.destination) - || (nil == self.configuration.destination.endpoint) - || (nil == self.configuration.destination.accessToken) - || (0 == self.configuration.destination.endpoint.length) - || (0 == self.configuration.destination.accessToken.length) - ) { + [[RollbarThread sharedInstance] persistPayload:payload + withConfig:self.configuration]; - return NO; - } - - NSURL *url = [NSURL URLWithString:self.configuration.destination.endpoint]; - if (nil == url) { - return NO; - } - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - - [request setHTTPMethod:@"POST"]; - [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [request setValue:self.configuration.destination.accessToken forHTTPHeaderField:@"X-Rollbar-Access-Token"]; - [request setHTTPBody:payload]; - - if (YES == self.configuration.developerOptions.logPayload) { - NSString *payloadString = [[NSString alloc]initWithData:payload - encoding:NSUTF8StringEncoding - ]; - NSLog(@"%@", payloadString); - //TODO: if self.configuration.logPayloadFile is defined, save the payload into the file... - } - - if (NO == self.configuration.developerOptions.transmit) { - return YES; // we just successfully short-circuit here... - } - - __block BOOL result = NO; - - // This requires iOS 7.0+ - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - - NSURLSession *session = [NSURLSession sharedSession]; - - if (self.configuration.httpProxy.enabled - || self.configuration.httpsProxy.enabled) { - - NSDictionary *connectionProxyDictionary = - @{ - @"HTTPEnable" : [NSNumber numberWithInt:self.configuration.httpProxy.enabled], - @"HTTPProxy" : self.configuration.httpProxy.proxyUrl, - @"HTTPPort" : @(self.configuration.httpProxy.proxyPort), - @"HTTPSEnable" : [NSNumber numberWithInt:self.configuration.httpsProxy.enabled], - @"HTTPSProxy" : self.configuration.httpsProxy.proxyUrl, - @"HTTPSPort" : @(self.configuration.httpsProxy.proxyPort) - }; - - NSURLSessionConfiguration *sessionConfig = - [NSURLSessionConfiguration ephemeralSessionConfiguration]; - sessionConfig.connectionProxyDictionary = connectionProxyDictionary; - session = [NSURLSession sessionWithConfiguration:sessionConfig]; - } - - NSURLSessionDataTask *dataTask = - [session dataTaskWithRequest:request - completionHandler:^( - NSData * _Nullable data, - NSURLResponse * _Nullable response, - NSError * _Nullable error) { - result = [self checkPayloadResponse:response - error:error - data:data]; - dispatch_semaphore_signal(sem); - }]; - [dataTask resume]; - - dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - - return result; -} - -- (BOOL)checkPayloadResponse:(NSURLResponse *)response - error:(NSError *)error - data:(NSData *)data { - - if (NO == self.configuration.developerOptions.suppressSdkInfoLogging) { - - RollbarSdkLog(@"HTTP response from Rollbar: %@", response); - } - - // Lookup rate limiting headers and adjust reporting rate accordingly: - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - NSDictionary *httpHeaders = [httpResponse allHeaderFields]; - //int rateLimit = [[httpHeaders valueForKey:RESPONSE_HEADER_RATE_LIMIT] intValue]; - int rateLimitLeft = - [[httpHeaders valueForKey:RESPONSE_HEADER_REMAINING_COUNT] intValue]; - int rateLimitSeconds = - [[httpHeaders valueForKey:RESPONSE_HEADER_REMAINING_SECONDS] intValue]; - if (rateLimitLeft > 0) { - nextSendTime = [[NSDate alloc] init]; - } - else { - nextSendTime = - [[NSDate alloc] initWithTimeIntervalSinceNow:rateLimitSeconds]; - } - - if (error) { - RollbarSdkLog(@"There was an error reporting to Rollbar"); - RollbarSdkLog(@"Error: %@", [error localizedDescription]); - } else { - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; - if ([httpResponse statusCode] == 200) { - if (NO == self.configuration.developerOptions.suppressSdkInfoLogging) { - RollbarSdkLog(@"Success"); - } - return YES; - } else { - RollbarSdkLog(@"There was a problem reporting to Rollbar"); - if (data) { - RollbarSdkLog( - @"Response: %@", - [NSJSONSerialization JSONObjectWithData:data - options:0 - error:nil]); - } - } - } - return NO; -} - -#pragma mark - Payload truncate - -- (void)createMutablePayloadWithData:(NSMutableDictionary *)data - forPath:(NSString *)path { - - NSArray *pathComponents = [path componentsSeparatedByString:@"."]; - NSString *currentPath = @""; - - for (int i=0; i *)readLogItemsFromStore { - - NSString *filePath = [RollbarLogger _logItemsStorePath]; - return [RollbarLogger readPayloadsDataFromFile:filePath]; -} - -+ (nonnull NSArray *)readPayloadsFromSdkLog { - - NSString *filePath = [RollbarLogger _payloadsLogStorePath]; - return [RollbarLogger readPayloadsDataFromFile:filePath]; -} - -+ (nonnull NSArray *)readPayloadsDataFromFile:(nonnull NSString *)filePath { - - RollbarFileReader *reader = [[RollbarFileReader alloc] initWithFilePath:filePath - andOffset:0]; - - NSMutableArray *items = [NSMutableArray array]; - [reader enumerateLinesUsingBlock:^(NSString *line, NSUInteger nextOffset, BOOL *stop) { - NSError *error = nil; - NSMutableDictionary *payload = - [NSJSONSerialization JSONObjectWithData:[line dataUsingEncoding:NSUTF8StringEncoding] - options:(NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves) - error:&error - ]; - if ((nil == payload) && (nil != error)) { - RollbarSdkLog(@"Error serializing log item from the store: %@", [error localizedDescription]); - return; - } - else if (nil == payload) { - RollbarSdkLog(@"Error serializing log item from the store!"); - return; - } - - NSMutableDictionary *data = payload[@"data"]; - [items addObject:data]; - }]; - - return items; -} - -+ (void)flushRollbarThread { - - [RollbarLogger performSelector:@selector(_test_doNothing) - onThread:[RollbarLogger _test_rollbarThread] - withObject:nil - waitUntilDone:YES - ]; -} - -+ (void)_clearFile:(nonnull NSString *)filePath { - - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error; - BOOL fileExists = [fileManager fileExistsAtPath:filePath]; - - if (fileExists) { - BOOL success = [fileManager removeItemAtPath:filePath - error:&error]; - if (!success) { - NSLog(@"Error: %@", [error localizedDescription]); - } - [[NSFileManager defaultManager] createFileAtPath:filePath - contents:nil - attributes:nil]; - } -} - -+ (nonnull NSString *)_logItemsStorePath { - - return [RollbarLogger _getSDKDataFilePath:QUEUED_ITEMS_FILE_NAME]; -} - -+ (nonnull NSString *)_logItemsStoreStatePath { - - return [RollbarLogger _getSDKDataFilePath:QUEUED_ITEMS_STATE_FILE_NAME]; -} - -+ (nonnull NSString *)_telemetryItemsStorePath { - - return [RollbarLogger _getSDKDataFilePath:QUEUED_TELEMETRY_ITEMS_FILE_NAME]; -} - -+ (nonnull NSString *)_payloadsLogStorePath { - - return [RollbarLogger _getSDKDataFilePath:PAYLOADS_FILE_NAME]; -} - -+ (nonnull NSString *)_getSDKDataFilePath:(nonnull NSString *)sdkFileName { - - NSString *cachesDirectory = [RollbarCachesDirectory directory]; - return [cachesDirectory stringByAppendingPathComponent:sdkFileName]; -} - -+ (NSThread *)_test_rollbarThread { - - return rollbarThread; -} - -+ (void)_test_doNothing { - - // no-Op simulation... -} - @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.h new file mode 100644 index 00000000..b4894b79 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.h @@ -0,0 +1,42 @@ +// +// RollbarNotifierFiles.h +// +// +// Created by Andrey Kornich on 2022-06-06. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarNotifierFiles : NSObject + +#pragma mark - notifier files + ++ (nonnull NSString * const)payloadsStore; + ++ (nonnull NSString * const)telemetryQueue; + ++ (nonnull NSString * const)runtimeSession; + ++ (nonnull NSString * const)appQuit; + ++ (nonnull NSString * const)incomingPayloadsLog; + ++ (nonnull NSString * const)transmittedPayloadsLog; + ++ (nonnull NSString * const)droppedPayloadsLog; + ++ (nonnull NSString * const)config; + +#pragma mark - static utility nature + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.m new file mode 100644 index 00000000..d8b7f8cd --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarNotifierFiles.m @@ -0,0 +1,59 @@ +#import "RollbarNotifierFiles.h" + +static NSString * const PAYLOADS_STORE_FILE_NAME = @"rollbar.db"; + +static NSString * const INCOMING_PAYLOADS_FILE_NAME = @"rollbar.incoming"; +static NSString * const TRANSMITTED_PAYLOADS_FILE_NAME = @"rollbar.transmitted"; +static NSString * const DROPPED_PAYLOADS_FILE_NAME = @"rollbar.dropped"; + +static NSString * const QUEUED_TELEMETRY_ITEMS_FILE_NAME = @"rollbar.telemetry"; + +static NSString * const SESSION_FILE_NAME = @"rollbar.session"; +static NSString * const APP_QUIT_FILE_NAME = @"rollbar.appquit"; + +static NSString * const CONFIG_FILE_NAME = @"rollbar.config"; + +@implementation RollbarNotifierFiles + ++ (nonnull NSString * const)payloadsStore { + + return PAYLOADS_STORE_FILE_NAME; +} + ++ (nonnull NSString * const)telemetryQueue { + + return QUEUED_TELEMETRY_ITEMS_FILE_NAME; +} + ++ (nonnull NSString * const)runtimeSession { + + return SESSION_FILE_NAME; +} + ++ (nonnull NSString * const)appQuit { + + return APP_QUIT_FILE_NAME; +} + ++ (nonnull NSString * const)incomingPayloadsLog { + + return INCOMING_PAYLOADS_FILE_NAME; +} + ++ (nonnull NSString * const)transmittedPayloadsLog { + + return TRANSMITTED_PAYLOADS_FILE_NAME; +} + ++ (nonnull NSString * const)droppedPayloadsLog { + + return DROPPED_PAYLOADS_FILE_NAME; +} + ++ (nonnull NSString * const)config { + + return CONFIG_FILE_NAME; +} + + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.h new file mode 100644 index 00000000..a71f64c1 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.h @@ -0,0 +1,44 @@ +// +// RollbarPayloadFactory.h +// +// +// Created by Andrey Kornich on 2022-06-14. +// + +#import + +#import "RollbarLevel.h" + +@class RollbarConfig; +@class RollbarPayload; + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarPayloadFactory : NSObject + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + crashReport:(nonnull NSString *)crashReport; + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + message:(nonnull NSString *)message + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + exception:(nonnull NSException *)exception + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + error:(nonnull NSError *)error + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +- (instancetype)initWithConfig:(nonnull RollbarConfig *)config NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)factoryWithConfig:(nonnull RollbarConfig *)config; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.m new file mode 100644 index 00000000..551f9812 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadFactory.m @@ -0,0 +1,325 @@ +#import "RollbarPayloadFactory.h" + +#import "RollbarConfig.h" +#import "RollbarLoggingOptions.h" +#import "RollbarScrubbingOptions.h" +#import "RollbarDestination.h" + +#import "RollbarPayload.h" +#import "RollbarBody.h" +#import "RollbarMessage.h" +#import "RollbarData.h" +#import "RollbarPerson.h" +#import "RollbarServer.h" +#import "RollbarClient.h" +#import "RollbarModule.h" + +#import + +#if TARGET_OS_IOS | TARGET_OS_TV | TARGET_OS_MACCATALYST +@import UIKit; +#endif + +@implementation RollbarPayloadFactory { + + @private + RollbarConfig *_config; // expected to be nonnull... + NSMutableDictionary *_osData; +} + ++ (instancetype)factoryWithConfig:(nonnull RollbarConfig *)config { + + RollbarPayloadFactory *factory = [[RollbarPayloadFactory alloc] initWithConfig:config]; + return factory; +} + +- (instancetype)initWithConfig:(nonnull RollbarConfig *)config { + + NSAssert(config, @"The config must be initialized!"); + + if (self = [super init]) { + + self->_config = config; + } + + return self; +} + +#pragma mark - Patload factory methods + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + crashReport:(nonnull NSString *)crashReport { + + RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level + message:nil + exception:nil + error:nil + extra:nil + crashReport:crashReport + context:nil]; + return payload; +} + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + message:(nonnull NSString *)message + data:(nullable NSDictionary *)data + context:(nullable NSString *)context { + + RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level + message:message + exception:nil + error:nil + extra:data + crashReport:nil + context:context]; + return payload; +} + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + exception:(nonnull NSException *)exception + data:(nullable NSDictionary *)data + context:(nullable NSString *)context { + + RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level + message:nil + exception:exception + error:nil + extra:data + crashReport:nil + context:context]; + return payload; +} + +- (nullable RollbarPayload *)payloadWithLevel:(RollbarLevel)level + error:(nonnull NSError *)error + data:(nullable NSDictionary *)data + context:(nullable NSString *)context { + + RollbarPayload *payload = [self buildRollbarPayloadWithLevel:level + message:nil + exception:nil + error:error + extra:data + crashReport:nil + context:context]; + return payload; +} + +#pragma mark - Payload Composition + +-(nullable RollbarPayload *)buildRollbarPayloadWithLevel:(RollbarLevel)level + message:(NSString *)message + exception:(NSException *)exception + error:(NSError *)error + extra:(NSDictionary *)extra + crashReport:(NSString *)crashReport + context:(NSString *)context { + + // check critical config settings: + if (!self->_config.destination + || !self->_config.destination.environment + || self->_config.destination.environment.length == 0) { + + return nil; + } + + // compile payload data proper body: + RollbarBody *body = [RollbarBody alloc]; + if (crashReport) { + body = [body initWithCrashReport:crashReport]; + } + else if (error) { + body = [body initWithError:error]; + } + else if (exception) { + body = [body initWithException:exception]; + } + else if (message) { + body = [body initWithMessage:message]; + } + else { + return nil; + } + + if (!body) { + return nil; + } + + // this is done only for backward compatibility for customers that used to rely on this undocumented + // extra data with a message: + if (message && extra) { + [body.message setData:extra.mutableCopy byKey:@"extra"]; + } + + // compile payload data: + RollbarData *data = [[RollbarData alloc] initWithEnvironment:self->_config.destination.environment + body:body]; + if (!data) { + return nil; + } + + NSMutableDictionary *customData = + [NSMutableDictionary dictionaryWithDictionary:self->_config.customData]; + if (crashReport || exception) { + // neither crash report no exception payload objects have placeholders for any extra data + // or an extra message, let's preserve them as the custom data: + if (extra) { + customData[@"error.extra"] = extra.mutableCopy; + } + if (message && message.length > 0) { + customData[@"error.message"] = message; + } + } + + data.level = level; + data.language = RollbarAppLanguage_ObjectiveC; + data.platform = @"client"; + data.uuid = [NSUUID UUID]; + data.custom = [[RollbarDTO alloc] initWithDictionary:customData]; + data.notifier = [self buildRollbarNotifierModule]; + data.person = [self buildRollbarPerson]; + data.server = [self buildRollbarServer]; + data.client = [self buildRollbarClient]; + if (context && context.length > 0) { + data.context = context; + } + if (self->_config.loggingOptions) { + data.framework = self->_config.loggingOptions.framework; + if (self->_config.loggingOptions.requestId + && (self->_config.loggingOptions.requestId.length > 0)) { + + [data setData:self->_config.loggingOptions.requestId byKey:@"requestId"]; + } + } + + RollbarPayload *payload = [[RollbarPayload alloc] initWithAccessToken:self->_config.destination.accessToken + data:data]; + + return payload; +} + +-(RollbarModule *)buildRollbarNotifierModule { + + if (self->_config.notifier && !self->_config.notifier.isEmpty) { + + RollbarModule *notifierModule = + [[RollbarModule alloc] initWithDictionary:self->_config.notifier.jsonFriendlyData.copy]; + [notifierModule setData:self->_config.jsonFriendlyData byKey:@"configured_options"]; + return notifierModule; + } + + return nil; +} + +-(RollbarClient *)buildRollbarClient { + + NSNumber *timestamp = [NSNumber numberWithInteger:[[NSDate date] timeIntervalSince1970]]; + + if (self->_config.loggingOptions) { + switch(self->_config.loggingOptions.captureIp) { + case RollbarCaptureIpType_Full: + return [[RollbarClient alloc] initWithDictionary:@{ + @"timestamp": timestamp, + @"ios": [self buildOSData], + @"user_ip": @"$remote_ip" + }]; + case RollbarCaptureIpType_Anonymize: + return [[RollbarClient alloc] initWithDictionary:@{ + @"timestamp": timestamp, + @"ios": [self buildOSData], + @"user_ip": @"$remote_ip_anonymize" + }]; + case RollbarCaptureIpType_None: + //no op + break; + } + } + + return [[RollbarClient alloc] initWithDictionary:@{ + @"timestamp": timestamp, + @"ios": [self buildOSData], + }]; +} + +-(RollbarPerson *)buildRollbarPerson { + + if (self->_config.person && self->_config.person.ID) { + return self->_config.person; + } + else { + return nil; + } +} + +-(RollbarServer *)buildRollbarServer { + + if (self->_config.server && !self->_config.server.isEmpty) { + return [[RollbarServer alloc] initWithCpu:nil + serverConfig:self->_config.server]; + } + else { + return nil; + } +} + +-(NSDictionary *)buildOSData { + + //TODO: redo this implementation based on the helper utils used by RollbarSession or on RollbarSession itself... + + if (self->_osData) { + return self->_osData; + } + + NSBundle *mainBundle = [NSBundle mainBundle]; + + NSString *version = nil; + if (self->_config.loggingOptions + && self->_config.loggingOptions.codeVersion + && self->_config.loggingOptions.codeVersion.length > 0) { + + version = self->_config.loggingOptions.codeVersion; + } + else { + version = [mainBundle objectForInfoDictionaryKey:(NSString*)kCFBundleVersionKey]; + } + + NSString *shortVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + NSString *bundleName = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]; + NSString *bundleIdentifier = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey]; + + struct utsname systemInfo; + uname(&systemInfo); + NSString *deviceCode = [NSString stringWithCString:systemInfo.machine + encoding:NSUTF8StringEncoding]; + +#if TARGET_OS_IOS | TARGET_OS_TV | TARGET_OS_MACCATALYST + self->_osData = @{ + @"os": @"iOS", + @"os_version": [[UIDevice currentDevice] systemVersion], + @"device_code": deviceCode, + @"code_version": version ? version : @"", + @"short_version": shortVersion ? shortVersion : @"", + @"bundle_identifier": bundleIdentifier ? bundleIdentifier : @"", + @"app_name": bundleName ? bundleName : @"" + }.mutableCopy; +#else + NSOperatingSystemVersion osVer = [[NSProcessInfo processInfo] operatingSystemVersion]; + self->_osData = @{ + @"os": @"macOS", + @"os_version": [NSString stringWithFormat:@" %tu.%tu.%tu", + osVer.majorVersion, + osVer.minorVersion, + osVer.patchVersion + ], + @"device_code": deviceCode, + @"code_version": version ? version : @"", + @"short_version": shortVersion ? shortVersion : @"", + @"bundle_identifier": bundleIdentifier ? bundleIdentifier : @"", + @"app_name": bundleName ? bundleName : [[NSProcessInfo processInfo] processName] + }.mutableCopy; +#endif + + return self->_osData; +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.h new file mode 100644 index 00000000..cf36b9c6 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.h @@ -0,0 +1,35 @@ +// +// RollbarPayloadPostReply.h +// +// +// Created by Andrey Kornich on 2022-06-13. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarPayloadPostReply : NSObject + +@property (readonly) NSInteger statusCode; +@property (readonly) NSUInteger rateLimit; +@property (readonly) NSUInteger remainingCount; +@property (readonly) NSUInteger remainingSeconds; +@property (readonly) NSDate *nextPostTime; + +- (instancetype)initWithStatusCode:(NSUInteger)statusCode + rateLimit:(NSUInteger)rateLimit + remainingCount:(NSUInteger)remainingCount + remainingSeconds:(NSUInteger)remainingSeconds; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + ++ (nullable RollbarPayloadPostReply *)replyFromHttpResponse:(nonnull NSHTTPURLResponse *)httpResponse; ++ (nonnull RollbarPayloadPostReply *)greenReply; ++ (nonnull RollbarPayloadPostReply *)yellowReply; ++ (nonnull RollbarPayloadPostReply *)redReply; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.m new file mode 100644 index 00000000..9f0ae7d1 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadPostReply.m @@ -0,0 +1,109 @@ +#import "RollbarPayloadPostReply.h" + +/// Rollbar API Service enforced payload rate limit: +static NSString * const RESPONSE_HEADER_RATE_LIMIT = @"x-rate-limit-limit"; +/// Rollbar API Service enforced remaining payload count until the limit is reached: +static NSString * const RESPONSE_HEADER_REMAINING_COUNT = @"x-rate-limit-remaining"; +/// Rollbar API Service enforced rate limit reset time for the current limit window: +static NSString * const RESPONSE_HEADER_RESET_TIME = @"x-rate-limit-reset"; +/// Rollbar API Service enforced rate limit remaining seconds of the current limit window: +static NSString * const RESPONSE_HEADER_REMAINING_SECONDS = @"x-rate-limit-remaining-seconds"; + +@implementation RollbarPayloadPostReply + ++ (nullable RollbarPayloadPostReply *)replyFromHttpResponse:(nonnull NSHTTPURLResponse *)httpResponse { + + NSUInteger rateLimit = + [NSNumber numberWithLongLong:[httpResponse valueForHTTPHeaderField:RESPONSE_HEADER_RATE_LIMIT]] + .unsignedIntegerValue; + + NSUInteger remainingCount = + [NSNumber numberWithLongLong:[httpResponse valueForHTTPHeaderField:RESPONSE_HEADER_REMAINING_COUNT]] + .unsignedIntegerValue; + + NSUInteger remainingSeconds = + [NSNumber numberWithLongLong:[httpResponse valueForHTTPHeaderField:RESPONSE_HEADER_REMAINING_SECONDS]] + .unsignedIntegerValue; + + return [[RollbarPayloadPostReply alloc] initWithStatusCode:httpResponse.statusCode + rateLimit:rateLimit + remainingCount:remainingCount + remainingSeconds:remainingSeconds + ]; +} + +- (instancetype)initWithStatusCode:(NSUInteger)statusCode + rateLimit:(NSUInteger)rateLimit + remainingCount:(NSUInteger)remainingCount + remainingSeconds:(NSUInteger)remainingSeconds { + + if (self = [super init]) { + self->_statusCode = statusCode; + self->_rateLimit = rateLimit; + self->_remainingCount = remainingCount; + self->_remainingSeconds = remainingSeconds; + + if (self->_remainingCount > 0) { + self->_nextPostTime = [[NSDate alloc] init]; + } + else { + self->_nextPostTime = [[NSDate alloc] initWithTimeIntervalSinceNow:self->_remainingSeconds]; + } + } + + return self; +} + +#pragma mark - overrides + +- (nonnull NSString *)description { + NSString *description = [NSString stringWithFormat:@"%@:\n" + " statusCode: %li\n" + " rateLimit: %lu\n" + " remainingCount: %lu\n" + " remainingSeconds: %lu\n" + " nextPostTime: %@\n" + , + super.description, + self->_statusCode, + self->_rateLimit, + self->_remainingCount, + self->_remainingSeconds, + self->_nextPostTime + ]; + return description; +} + +#pragma mark - factory methods + ++ (nonnull RollbarPayloadPostReply *)greenReply { + + // the last POST was OK and can continue POSTing: + return [[RollbarPayloadPostReply alloc] initWithStatusCode:200 // OK + rateLimit:1 + remainingCount:1 + remainingSeconds:1 + ]; +} + ++ (nonnull RollbarPayloadPostReply *)yellowReply { + + // the last POST was OK but can not continue POSTing for another 10 [sec]: + return [[RollbarPayloadPostReply alloc] initWithStatusCode:200 // OK + rateLimit:1 + remainingCount:0 + remainingSeconds:10 + ]; +} + ++ (nonnull RollbarPayloadPostReply *)redReply { + + // the last POST failed due too many requests and can not continue POSTing for another 10 [sec]: + return [[RollbarPayloadPostReply alloc] initWithStatusCode:429 // too many requests... + rateLimit:1 + remainingCount:0 + remainingSeconds:10 + ]; +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.h new file mode 100644 index 00000000..910208d7 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.h @@ -0,0 +1,101 @@ +// +// RollbarPayloadRepository.h +// +// +// Created by Andrey Kornich on 2022-07-27. +// + +#import + +#import "RollbarPayload.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarPayloadRepository : NSObject + +#pragma mark - factory methods + ++ (instancetype)repositoryWithFlag:(BOOL)inMemory; ++ (instancetype)repositoryWithPath:(nonnull NSString *)storePath; + ++ (instancetype)inMemoryRepository; ++ (instancetype)persistentRepository; ++ (instancetype)persistentRepositoryWithPath:(nonnull NSString *)storePath; + +#pragma mark - instantiation blocking + ++ (instancetype)alloc NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +#pragma mark - Destinations related methods + +- (nullable NSDictionary *)addDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken; + +- (nonnull NSString *)getIDofDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken; + +- (nullable NSDictionary *)getDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken; + +- (nullable NSDictionary *)getDestinationByID:(nonnull NSString *)destinationID; + +- (nonnull NSArray *> *)getAllDestinations; + +- (BOOL)removeDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken; + +- (BOOL)removeDestinationByID:(nonnull NSString *)destinationID; + +- (BOOL)removeUnusedDestinations; + +- (BOOL)removeAllDestinations; + +#pragma mark - Payloads related methods + +- (nullable NSDictionary *)addPayload:(nonnull NSString *)payload + withConfig:(nonnull NSString *)config + andDestinationID:(nonnull NSString *)destinationID; + +- (nullable NSDictionary *)getPayloadByID:(nonnull NSString *)payloadID; + +- (nonnull NSArray *> *)getAllPayloadsWithDestinationID:(nonnull NSString *)destinationID; + +- (nonnull NSArray *> *)getPayloadsWithDestinationID:(nonnull NSString *)destinationID + andLimit:(NSUInteger)limit; + +- (nonnull NSArray *> *)getPayloadsWithDestinationID:(nonnull NSString *)destinationID + andOffset:(NSUInteger)offset + andLimit:(NSUInteger)limit; + +- (nonnull NSArray *> *)getPayloadsWithLimit:(NSUInteger)limit; + +- (nonnull NSArray *> *)getPayloadsWithOffset:(NSUInteger)offset + andLimit:(NSUInteger)limit; + +- (nonnull NSArray *> *)getAllPayloads; + +- (BOOL)removePayloadByID:(nonnull NSString *)payloadID; + +- (BOOL)removePayloadsOlderThan:(nonnull NSDate *)cutoffTime; + +- (BOOL)removeAllPayloads; + +#pragma mark - unit testing helper methods + +- (BOOL)checkIfTableExists_Destinations; + +- (BOOL)checkIfTableExists_Payloads; + +- (BOOL)checkIfTableExists_Unknown; + +- (BOOL)clearDestinations; + +- (BOOL)clearPayloads; + +- (BOOL)clear; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.m new file mode 100644 index 00000000..c1192fa7 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadRepository.m @@ -0,0 +1,620 @@ +#import "RollbarPayloadRepository.h" + +#import "RollbarNotifierFiles.h" + +@import RollbarCommon; + +#import "sqlite3.h" + +//====================================================================================================================== +#pragma mark - Sqlite command execution callbacks +#pragma mark - +//====================================================================================================================== + +typedef int (^SqliteCallback)(void *info, int columns, char **data, char **column); + +static int defaultOnSelectCallback(void *info, int columns, char **data, char **column) +{ + RollbarSdkLog(@"Columns: %d", columns); + for (int i = 0; i < columns; i++) { + RollbarSdkLog(@"Column: %s", column[i]); + RollbarSdkLog(@"Data: %s", data[i]); + } + + return SQLITE_OK; +} + +static int checkIfTableExistsCallback(void *info, int columns, char **data, char **column) +{ + defaultOnSelectCallback(info, columns, data, column); + + BOOL *answerFlag = (BOOL *)info; + if (answerFlag) { + + *answerFlag = (columns > 0) ? YES : NO; + } + + return SQLITE_OK; +} + +static int selectSingleRowCallback(void *info, int columns, char **data, char **column) +{ + defaultOnSelectCallback(info, columns, data, column); + + NSDictionary *__strong*result = (NSDictionary *__strong*)info; + + NSMutableDictionary *row = + [NSMutableDictionary dictionaryWithCapacity:columns]; + + for (int i = 0; i < columns; i++) { + NSString *key = [NSString stringWithFormat:@"%s", column[i]]; + NSString *value = [NSString stringWithFormat:@"%s", data[i]]; + row[key] = value; + } + + *result = [row copy]; + + return SQLITE_OK; +} + +static int selectMultipleRowsCallback(void *info, int columns, char **data, char **column) +{ + defaultOnSelectCallback(info, columns, data, column); + + NSMutableArray *> *__strong*result = + (NSMutableArray *> *__strong*)info; + if (!*result) { + *result = [NSMutableArray *> array]; + } + + NSMutableDictionary *row = + [NSMutableDictionary dictionaryWithCapacity:columns]; + + for (int i = 0; i < columns; i++) { + NSString *key = [NSString stringWithFormat:@"%s", column[i]]; + NSString *value = [NSString stringWithFormat:@"%s", data[i]]; + row[key] = value; + } + + [*result addObject:[row copy]]; + + return SQLITE_OK; +} + +//====================================================================================================================== +#pragma mark - Peyloads Repository +#pragma mark - +//====================================================================================================================== + +/// Peristent Rollbar payloads repository +@implementation RollbarPayloadRepository { + + @private + NSString *_storePath; + sqlite3 *_db; + char *_sqliteErrorMessage; + +} + +#pragma mark - factory methods + ++ (instancetype)repositoryWithFlag:(BOOL)inMemory { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository alloc]; + if (inMemory) { + return [repo initInMemoryOnly]; + } + else { + return [repo init]; + } +} + ++ (instancetype)repositoryWithPath:(nonnull NSString *)storePath { + + return [[RollbarPayloadRepository alloc] initWithStore:storePath]; +} + ++ (instancetype)inMemoryRepository { + + return [RollbarPayloadRepository repositoryWithFlag:YES]; +} + ++ (instancetype)persistentRepository { + + return [RollbarPayloadRepository repositoryWithFlag:NO]; +} + ++ (instancetype)persistentRepositoryWithPath:(nonnull NSString *)storePath { + + return [RollbarPayloadRepository repositoryWithPath:storePath]; +} + +#pragma mark - class initializer + ++ (void)initialize { + +} + +#pragma mark - instance initializers + +- (instancetype)initInMemoryOnly { + + if (self = [super init]) { + + [self initDB:YES]; + return self; + } + + return nil; +} + +- (instancetype)initWithStore:(nonnull NSString *)storePath { + + if (self = [super init]) { + + self->_storePath = storePath; + [self initDB:NO]; + return self; + } + + return nil; +} + +- (instancetype)init { + + // create working cache directory: + [RollbarCachesDirectory ensureCachesDirectoryExists]; + NSString *cachesDirectory = [RollbarCachesDirectory directory]; + NSString *storePath = [cachesDirectory stringByAppendingPathComponent:[RollbarNotifierFiles payloadsStore]]; + + return [self initWithStore:storePath]; +} + +#pragma mark - Destinations related methods + + +- (nullable NSDictionary *)addDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken { + + NSString *sql = [NSString stringWithFormat: + @"INSERT INTO destinations (endpoint, access_token) VALUES ('%@', '%@')", + endpoint, + accessToken + ]; + + if (NO == [self executeSql:sql]) { + return nil; + } + + sqlite3_int64 destinationID = sqlite3_last_insert_rowid(self->_db); + + return @ { + @"id": [NSString stringWithFormat:@"%lli", destinationID], //[NSNumber numberWithLongLong:destinationID], + @"endpoint": endpoint, + @"access_token": accessToken + }; +} + +- (nonnull NSString *)getIDofDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken { + + NSString *sql = [NSString stringWithFormat: + @"SELECT id FROM destinations WHERE endpoint = '%@' AND access_token = '%@'", + endpoint, + accessToken + ]; + + NSDictionary *result = [self selectSingleRowWithSql:sql + andCallback:selectSingleRowCallback]; + + if (!result || (0 == result.count)) { + result = [self addDestinationWithEndpoint:endpoint + andAccesToken:accessToken]; + } + + NSString *destinationID = nil; + if (result) { + destinationID = result[@"id"]; + } + + if (!destinationID) { + destinationID = @""; + } + + return destinationID; +} + +- (nullable NSDictionary *)getDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken { + + NSString *sql = [NSString stringWithFormat: + @"SELECT * FROM destinations WHERE endpoint = '%@' AND access_token = '%@'", + endpoint, + accessToken + ]; + + NSDictionary *result = + [self selectSingleRowWithSql:sql andCallback:selectSingleRowCallback]; + + return result; +} + +- (nullable NSDictionary *)getDestinationByID:(nonnull NSString *)destinationID { + + NSString *sql = [NSString stringWithFormat: + @"SELECT * FROM destinations WHERE id = '%@'", + destinationID + ]; + + NSDictionary *result = + [self selectSingleRowWithSql:sql andCallback:selectSingleRowCallback]; + + return result; +} + +- (nonnull NSArray *> *)getAllDestinations { + + NSString *sql = @"SELECT * FROM destinations"; + + NSArray *> *result = + [self selectMultipleRowsWithSql:sql andCallback:selectMultipleRowsCallback]; + + return result; +} + +- (BOOL)removeDestinationWithEndpoint:(nonnull NSString *)endpoint + andAccesToken:(nonnull NSString *)accessToken { + + NSString *sql = + [NSString stringWithFormat: @"DELETE FROM destinations WHERE endpoint = '%@' AND access_token = '%@'", + endpoint, + accessToken + ]; + + return [self executeSql:sql]; +} + +- (BOOL)removeDestinationByID:(nonnull NSString *)destinationID { + + NSString *sql = + [NSString stringWithFormat: @"DELETE FROM destinations WHERE id = '%@'", destinationID]; + + return [self executeSql:sql]; +} + +- (BOOL)removeUnusedDestinations { + + NSString *sql = + @"DELETE FROM destinations WHERE NOT EXISTS (SELECT 1 FROM payloads WHERE payloads.destination_key = destinations.id)"; + return [self executeSql:sql]; +} + +- (BOOL)removeAllDestinations { + + NSString *sql = @"DELETE FROM destinations"; + return [self executeSql:sql]; +} + +#pragma mark - Payloads related methods + +- (nullable NSDictionary *)addPayload:(nonnull NSString *)payload + withConfig:(nonnull NSString *)config + andDestinationID:(nonnull NSString *)destinationID { + + NSNumber *timeStamp = [NSNumber numberWithInteger:[[NSDate date] timeIntervalSince1970]]; + + NSString *escapedPayload = [payload stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; + NSString *escapedConfig = [config stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; + + NSString *sql = [NSString stringWithFormat: + @"INSERT INTO payloads (config_json, payload_json, destination_key, created_at) VALUES ('%@', '%@', '%@', '%ld')", + escapedConfig, + escapedPayload, + destinationID, + [timeStamp integerValue] + ]; + + if (NO == [self executeSql:sql]) { + return nil; + } + + sqlite3_int64 payloadID = sqlite3_last_insert_rowid(self->_db); + + return @ { + @"id": [NSString stringWithFormat:@"%lli", payloadID], //[NSNumber numberWithLongLong:destinationID], + @"config_json": config, + @"payload_json": payload, + @"destination_key": destinationID + }; +} + +- (nullable NSDictionary *)getPayloadByID:(nonnull NSString *)payloadID { + + NSString *sql = [NSString stringWithFormat: + @"SELECT * FROM payloads WHERE id = '%@'", + payloadID + ]; + + NSDictionary *result = + [self selectSingleRowWithSql:sql andCallback:selectSingleRowCallback]; + + return result; +} + +- (nonnull NSArray *> *)getAllPayloadsWithDestinationID:(nonnull NSString *)destinationID { + + NSString *sql = [NSString stringWithFormat: + @"SELECT * FROM payloads WHERE destination_key = '%@'", + destinationID + ]; + + NSArray *> *result = + [self selectMultipleRowsWithSql:sql andCallback:selectMultipleRowsCallback]; + + return result; +} + +- (nonnull NSArray *> *)getPayloadsWithDestinationID:(nonnull NSString *)destinationID + andLimit:(NSUInteger)limit { + + return [self getPayloadsWithDestinationID:destinationID andOffset:0 andLimit:limit]; +} + +- (nonnull NSArray *> *)getPayloadsWithDestinationID:(nonnull NSString *)destinationID + andOffset:(NSUInteger)offset + andLimit:(NSUInteger)limit { + + + NSString *sql = [NSString stringWithFormat: + @"SELECT * FROM payloads WHERE destination_key = '%@' LIMIT %lu OFFSET %lu", + destinationID, + limit, + offset + ]; + + NSArray *> *result = + [self selectMultipleRowsWithSql:sql andCallback:selectMultipleRowsCallback]; + + return result; +} + +- (nonnull NSArray *> *)getPayloadsWithLimit:(NSUInteger)limit { + + return [self getPayloadsWithOffset:0 andLimit:limit]; +} + +- (nonnull NSArray *> *)getPayloadsWithOffset:(NSUInteger)offset + andLimit:(NSUInteger)limit { + + NSString *sql = + [NSString stringWithFormat:@"SELECT * FROM payloads LIMIT %lu OFFSET %lu", limit, offset]; + + NSArray *> *result = + [self selectMultipleRowsWithSql:sql andCallback:selectMultipleRowsCallback]; + + return result; +} + +- (nonnull NSArray *> *)getAllPayloads { + + NSString *sql = @"SELECT * FROM payloads"; + + NSArray *> *result = + [self selectMultipleRowsWithSql:sql andCallback:selectMultipleRowsCallback]; + + return result; +} + +- (BOOL)removePayloadByID:(nonnull NSString *)payloadID { + + NSString *sql = + [NSString stringWithFormat: @"DELETE FROM payloads WHERE id = '%@'", payloadID]; + + return [self executeSql:sql]; +} + +- (BOOL)removePayloadsOlderThan:(nonnull NSDate *)cutoffTime { + + NSTimeInterval interval = [cutoffTime timeIntervalSince1970]; + + NSString *sql = [NSString stringWithFormat: + @"DELETE FROM payloads WHERE created_at <= '%li'", + (long int)interval + ]; + return [self executeSql:sql]; +} + +- (BOOL)removeAllPayloads { + + NSString *sql = @"DELETE FROM payloads"; + return [self executeSql:sql]; +} + +#pragma mark - unit testing helper methods + +- (BOOL)checkIfTableExists_Destinations { + + BOOL result = [self checkIfTableExists:@"destinations"]; + return result; +} + +- (BOOL)checkIfTableExists_Payloads { + + BOOL result = [self checkIfTableExists:@"payloads"]; + return result; +} + +- (BOOL)checkIfTableExists_Unknown { + + BOOL result = [self checkIfTableExists:@"unknown"]; + return result; +} + +- (BOOL)clearDestinations; { + + return [self removeAllDestinations]; +} + +- (BOOL)clearPayloads { + + return [self removeAllPayloads]; +} + +- (BOOL)clear { + + BOOL success = [self clearPayloads]; + if (success) { + success = [self clearDestinations]; + } + return success; +} + +#pragma mark - internal methods + +- (void)initDB:(BOOL)inMemory { + + if ([self openDB:inMemory]) { + + [self ensureDestinationsTable]; + [self ensurePayloadsTable]; + } + else { + + RollbarSdkLog(@"Can not open database: %@!!!", inMemory ? @"in memory" : self->_storePath); + } +} + +- (BOOL)openDB:(BOOL)inMemory { + + int sqliteDbFlags = inMemory + ? (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY) + : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + int result = sqlite3_open_v2([self->_storePath UTF8String], &self->_db, sqliteDbFlags, NULL); + if (result != SQLITE_OK) { + + RollbarSdkLog(@"sqlite3_open: %s", sqlite3_errmsg(self->_db)); + return NO; + } + + [self checkDbFile]; + + return YES; +} + +- (BOOL)ensureDestinationsTable { + + NSString *sql = [NSString stringWithFormat: + @"CREATE TABLE IF NOT EXISTS destinations (id INTEGER NOT NULL PRIMARY KEY, endpoint TEXT NOT NULL, access_token TEXT NOT NULL, CONSTRAINT unique_destination UNIQUE(endpoint, access_token))" + ]; + return [self executeSql:sql]; +} + +- (BOOL)ensurePayloadsTable { + + + + NSString *sql = [NSString stringWithFormat: + @"CREATE TABLE IF NOT EXISTS payloads (id INTEGER NOT NULL PRIMARY KEY, config_json TEXT NOT NULL, payload_json TEXT NOT NULL, created_at INTEGER NOT NULL, destination_key INTEGER NOT NULL, FOREIGN KEY(destination_key) REFERENCES destinations(id) ON UPDATE CASCADE ON DELETE CASCADE)" + ]; + return [self executeSql:sql]; +} + +- (void)releaseDB { + + sqlite3_close(self->_db); + self->_db = nil; +} + +- (BOOL)checkIfTableExists:(nonnull NSString *)tableName { + + [self checkDbFile]; + + NSString *sql = [NSString stringWithFormat: + @"SELECT name FROM sqlite_master WHERE type='table' AND name='%@'", tableName]; + char *sqliteErrorMessage; + BOOL answerFlag = NO; + int sqlResult = sqlite3_exec(self->_db, [sql UTF8String], checkIfTableExistsCallback, &answerFlag, &sqliteErrorMessage); + if (sqlResult != SQLITE_OK) { + + RollbarSdkLog(@"sqlite3_exec: %s during %@", sqliteErrorMessage, sql); + sqlite3_free(sqliteErrorMessage); + } + + return answerFlag; +} + +- (BOOL)executeSql:(nonnull NSString *)sql { + + [self checkDbFile]; + + char *sqliteErrorMessage; + int sqlResult = sqlite3_exec(self->_db, [sql UTF8String], NULL, NULL, &sqliteErrorMessage); + if (sqlResult != SQLITE_OK) { + + RollbarSdkLog(@"sqlite3_exec: %s during %@", sqliteErrorMessage, sql); + sqlite3_free(sqliteErrorMessage); + return NO; + } + return YES; +} + +- (nullable NSDictionary *)selectSingleRowWithSql:(NSString *)sql + andCallback:(int(*)(void*,int,char**,char**))callback { + + [self checkDbFile]; + + NSDictionary *result = nil; + char *sqliteErrorMessage; + NSDictionary *selectedRow = nil; + int sqlResult = sqlite3_exec(self->_db, [sql UTF8String], callback, &result, &sqliteErrorMessage); + if (sqlResult != SQLITE_OK) { + + RollbarSdkLog(@"sqlite3_exec: %s during %@", sqliteErrorMessage, sql); + sqlite3_free(sqliteErrorMessage); + } + + return result; +} + +- (nonnull NSArray *> *)selectMultipleRowsWithSql:(NSString *)sql + andCallback:(int(*)(void*,int,char**,char**))callback { + + [self checkDbFile]; + + NSMutableArray *> *result = nil; + + char *sqliteErrorMessage; + int sqlResult = sqlite3_exec(self->_db, [sql UTF8String], callback, &result, &sqliteErrorMessage); + if (sqlResult != SQLITE_OK) { + + RollbarSdkLog(@"sqlite3_exec: %s during %@", sqliteErrorMessage, sql); + sqlite3_free(sqliteErrorMessage); + } + + if (result) { + return [result copy]; + } + else { + return [NSArray *> array]; + } +} + +- (void)checkDbFile { + + if (self->_storePath && ![[NSFileManager defaultManager] fileExistsAtPath:self->_storePath]) { + RollbarSdkLog(@"Persistent payloads store was not created: %@!!!", self->_storePath); + [self openDB:NO]; + [self ensureDestinationsTable]; + [self ensurePayloadsTable]; + return; + } + NSAssert(self->_storePath ? + [[NSFileManager defaultManager] fileExistsAtPath:self->_storePath] + : YES, + @"Persistent payloads store was not created: %@!!!", self->_storePath + ); + +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadTruncator.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadTruncator.m index 6a698182..7c8234b0 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadTruncator.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarPayloadTruncator.m @@ -56,37 +56,55 @@ +(void)truncatePayload:(NSMutableDictionary*)payload ]; if (continueTruncation) { - continueTruncation = + @try { + continueTruncation = [RollbarPayloadTruncator truncatePayload:payload toTotalBytes:limit byReducingItems:pathToCrashThreads keepingHeadsCount:payloadHeadCrashThreadsToKeep keepingTailsCount:payloadTailCrashThreadsToKeep - ]; + ]; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); + } + } unsigned long stringLimit = maxStringBytesLimit; while (continueTruncation && (stringLimit >= minStringBytesLimit)) { - continueTruncation = [RollbarPayloadTruncator truncatePayload:payload - toTotalBytes:limit - byLimitingStringBytes:stringLimit - ]; - stringLimit /= 2; + @try { + continueTruncation = [RollbarPayloadTruncator truncatePayload:payload + toTotalBytes:limit + byLimitingStringBytes:stringLimit + ]; + stringLimit /= 2; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); + } } if (continueTruncation) { - continueTruncation = [RollbarPayloadTruncator truncatePayload:payload - toTotalBytes:limit - withExceptionMessageLimit:maxExceptionMessageChars - andTraceFramesLimit:maxTraceFrames - ]; + @try { + continueTruncation = [RollbarPayloadTruncator truncatePayload:payload + toTotalBytes:limit + withExceptionMessageLimit:maxExceptionMessageChars + andTraceFramesLimit:maxTraceFrames + ]; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); + } } if (continueTruncation) { - - [RollbarPayloadTruncator limitRawCrashReportInPayload:payload]; + + @try { + [RollbarPayloadTruncator limitRawCrashReportInPayload:payload]; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); + } + } } @@ -182,51 +200,60 @@ +(BOOL)truncatePayload:(NSMutableDictionary*)payload +(void)itereateObjectStructure:(id)obj whileTuncatingStrings:(unsigned long)stringBytesLimit { - if ([obj isKindOfClass:[NSMutableString class]] && [RollbarPayloadTruncator isMutable:obj]) { - - //truncate the string obj: - [obj setString:[RollbarPayloadTruncator truncateString:obj - toTotalBytes:stringBytesLimit] - ]; - } - else if ([obj isKindOfClass:[NSArray class]]) { - - //recurse the collection obj's items: - [obj enumerateObjectsUsingBlock: ^(id item, NSUInteger idx, BOOL *stop) { - [RollbarPayloadTruncator itereateObjectStructure:item - whileTuncatingStrings:stringBytesLimit - ]; - }]; - } - else if ([obj isKindOfClass:[NSDictionary class]]) { - - //recurse the collection obj's items: - [obj enumerateKeysAndObjectsUsingBlock: ^(id key, id item, BOOL *stop) { - if (![key isEqualToString:@"raw"]) { - if ([item isKindOfClass:[NSMutableString class]] && ![RollbarPayloadTruncator isMutable:item]) { - NSMutableString *mutableItem = [item mutableCopy]; - [obj setObject:mutableItem forKey:key]; - [RollbarPayloadTruncator itereateObjectStructure:mutableItem - whileTuncatingStrings:stringBytesLimit]; - } else { - [RollbarPayloadTruncator itereateObjectStructure:item - whileTuncatingStrings:stringBytesLimit]; + @try { + if ([obj isKindOfClass:[NSMutableString class]] && [RollbarPayloadTruncator isMutable:obj]) { + + //truncate the string obj: + [obj setString:[RollbarPayloadTruncator truncateString:obj + toTotalBytes:stringBytesLimit] + ]; + } + else if ([obj isKindOfClass:[NSArray class]]) { + + //recurse the collection obj's items: + [obj enumerateObjectsUsingBlock: ^(id item, NSUInteger idx, BOOL *stop) { + [RollbarPayloadTruncator itereateObjectStructure:item + whileTuncatingStrings:stringBytesLimit + ]; + }]; + } + else if ([obj isKindOfClass:[NSDictionary class]]) { + + //recurse the collection obj's items: + [obj enumerateKeysAndObjectsUsingBlock: ^(id key, id item, BOOL *stop) { + if (![key isEqualToString:@"raw"]) { + if ([item isKindOfClass:[NSString class]]) { + NSMutableString *strItem = ((NSString *)item).mutableCopy; + @try { + obj[key] = strItem; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); + } + [RollbarPayloadTruncator itereateObjectStructure:strItem + whileTuncatingStrings:stringBytesLimit]; + } else { + [RollbarPayloadTruncator itereateObjectStructure:item + whileTuncatingStrings:stringBytesLimit]; + } } - } - }]; - } - else if ([obj isKindOfClass:[NSSet class]]) { - - //recurse the collection obj's items: - [obj enumerateObjectsUsingBlock: ^(id item, BOOL *stop) { - [RollbarPayloadTruncator itereateObjectStructure:item - whileTuncatingStrings:stringBytesLimit - ]; - }]; - } - else { - //nothing really... + }]; + } + else if ([obj isKindOfClass:[NSSet class]]) { + + //recurse the collection obj's items: + [obj enumerateObjectsUsingBlock: ^(id item, BOOL *stop) { + [RollbarPayloadTruncator itereateObjectStructure:item + whileTuncatingStrings:stringBytesLimit + ]; + }]; + } + else { + //nothing really... + } + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload truncation EXCEPTION: %@", exception); } + } +(BOOL)isMutable:(id)str { @@ -246,16 +273,6 @@ +(BOOL)truncatePayload:(NSMutableDictionary*)payload return FALSE; //payload is small enough, no need to truncate further... } -// NSMutableArray *items = [payload mutableArrayValueForKeyPath:pathToItems]; -// if (items.count <= (headsCount + tailsCount)) { -// return TRUE; -// } -// -// unsigned long totalItemsToRemove = items.count - headsCount - tailsCount; -// [items removeObjectsInRange:NSMakeRange(headsCount, totalItemsToRemove)]; -// -// return TRUE; - id value = [payload valueForKeyPath:pathToItems]; if (value == [NSNull null] || [value isKindOfClass:[NSNull class]]) { diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.h new file mode 100644 index 00000000..c02a16e8 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.h @@ -0,0 +1,28 @@ +// +// RollbarRegistry.h +// +// +// Created by Andrey Kornich on 2022-06-28. +// + +#import + +#import "RollbarDestinationRecord.h" +#import "RollbarConfig.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarRegistry : NSObject + +- (nonnull RollbarDestinationRecord *)getRecordForConfig:(nonnull RollbarConfig *)config; +- (nonnull RollbarDestinationRecord *)getRecordForEndpoint:(nonnull NSString *)endpoint + andAccessToken:(nonnull NSString *)accessToken; +- (NSUInteger)totalDestinationRecords; + ++ (nonnull NSString *)destinationID:(nonnull RollbarDestination *)destination; ++ (nonnull NSString *)destinationIDwithEndpoint:(nonnull NSString *)endpoint + andAccessToken:(nonnull NSString *)accessToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.m new file mode 100644 index 00000000..0bdc899b --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarRegistry.m @@ -0,0 +1,99 @@ +#import "RollbarRegistry.h" +#import "RollbarDestinationRecord.h" + +const NSUInteger DEFAULT_RegistryCapacity = 10; + +@implementation RollbarRegistry { + @private + NSMutableDictionary *_destinationRecords; +} + +- (instancetype)init { + + if (self = [super init]) { + self->_destinationRecords = [NSMutableDictionary dictionaryWithCapacity:DEFAULT_RegistryCapacity]; + } + return self; +} + +- (nonnull RollbarDestinationRecord *)getRecordForConfig:(nonnull RollbarConfig *)config { + + NSAssert(config, @"Config must not be null!"); + NSAssert(config.destination, @"Destination must not be null!"); + NSAssert(config.destination.endpoint, @"destination.endpoint must not be null!"); + NSAssert(config.destination.accessToken, @"destination.accessToken must not be null!"); + + //NSString *destinationID = [RollbarRegistry destinationID:config.destination]; + RollbarDestinationRecord *destinationRecord = [self getRecordForEndpoint:config.destination.endpoint + andAccessToken:config.destination.accessToken]; + + if (destinationRecord.localWindowLimit < config.loggingOptions.maximumReportsPerMinute) { + + // we use lagest configured limit per destination: + destinationRecord.localWindowLimit = config.loggingOptions.maximumReportsPerMinute; + } + + return destinationRecord; +} + +- (nonnull RollbarDestinationRecord *)getRecordForEndpoint:(nonnull NSString *)endpoint + andAccessToken:(nonnull NSString *)accessToken { + + NSAssert(endpoint, @"endpoint must not be null!"); + NSAssert(accessToken, @"accessToken must not be null!"); + + NSString *destinationID = [RollbarRegistry destinationIDwithEndpoint:endpoint andAccessToken:accessToken]; + RollbarDestinationRecord *destinationRecord = self->_destinationRecords[destinationID]; + if (!destinationRecord) { + destinationRecord = [[RollbarDestinationRecord alloc] initWithDestinationID:destinationID + andRegistry:self + ]; + self->_destinationRecords[destinationID] = destinationRecord; + } + +// if (destinationRecord.localWindowLimit < config.loggingOptions.maximumReportsPerMinute) { +// +// // we use lagest configured limit per destination: +// destinationRecord.localWindowLimit = config.loggingOptions.maximumReportsPerMinute; +// } + + return destinationRecord; +} + + + + + +- (NSUInteger)totalDestinationRecords { + return self->_destinationRecords.count; +} + +#pragma mark - overrides + +- (nonnull NSString *)description { + NSString *description = [NSString stringWithFormat:@"%@ - totalDestinationRecords: %lu, records: %@", + super.description, + (unsigned long)[self totalDestinationRecords], + self->_destinationRecords + ]; + return description; +} + +#pragma mark - class methods + ++ (nonnull NSString *)destinationID:(nonnull RollbarDestination *)destination { + + return [RollbarRegistry destinationIDwithEndpoint:destination.endpoint + andAccessToken:destination.accessToken]; +} + ++ (nonnull NSString *)destinationIDwithEndpoint:(nonnull NSString *)endpoint + andAccessToken:(nonnull NSString *)accessToken { + + NSString *destinationID = [NSString stringWithFormat:@"%@|%@", + endpoint, + accessToken]; + return destinationID; +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.h new file mode 100644 index 00000000..ae11e523 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.h @@ -0,0 +1,21 @@ +// +// RollbarSender.h +// +// +// Created by Andrey Kornich on 2022-06-10. +// + +#import + +@class RollbarConfig; +@class RollbarPayloadPostReply; + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarSender : NSObject + +- (nullable RollbarPayloadPostReply *)sendPayload:(nonnull NSData *)payload + usingConfig:(nonnull RollbarConfig *)config; +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.m new file mode 100644 index 00000000..8b209ab9 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarSender.m @@ -0,0 +1,213 @@ +#import "RollbarSender.h" +#import "RollbarConfig.h" +#import "RollbarDestination.h" +#import "RollbarProxy.h" +#import "RollbarDeveloperOptions.h" +#import "RollbarPayloadPostReply.h" +#import "RollbarNotifierFiles.h" + +@implementation RollbarSender + ++ (void)trace:(nonnull NSString *)message + withOptions:(nullable RollbarDeveloperOptions *)developerOptions { + + if (!developerOptions) { + // then, let's use default developer options: + developerOptions = [RollbarDeveloperOptions new]; + } + + if (NO == developerOptions.suppressSdkInfoLogging) { + RollbarSdkLog(message); + } +} + ++ (void)assertError:(nonnull NSString *)error + withOptions:(nullable RollbarDeveloperOptions *)developerOptions { + + [RollbarSender trace:error withOptions:developerOptions]; + + NSAssert(false, error); +} + +- (nullable RollbarPayloadPostReply *)sendPayload:(nonnull NSData *)payload + usingConfig:(nonnull RollbarConfig *)config { + + RollbarPayloadPostReply *reply = nil; + + if (YES == config.developerOptions.transmit) { + reply = [self transmitPayload:payload + toDestination:config.destination + usingDeveloperOptions:config.developerOptions + andHttpProxySettings:config.httpProxy + andHttpsProxySettings:config.httpsProxy + ]; + } + else { + reply = [RollbarPayloadPostReply greenReply]; // we just successfully short-circuit here... + } + + NSString *payloadString = [[NSString alloc] initWithData:payload + encoding:NSUTF8StringEncoding]; + if (reply && (200 == reply.statusCode)) { + [RollbarSender trace:[NSString stringWithFormat:@"Transmitted payload: %@", payloadString] + withOptions:config.developerOptions]; +// if ((YES == config.developerOptions.logTransmittedPayloads) && config.developerOptions.transmittedPayloadLogFile) { +// [RollbarFileWriter appendSafelyData:payload toFile:config.developerOptions.transmittedPayloadLogFile]; +// //TODO: complete implementation (log into payloads log file)... +// } + } + else if (reply) { + [RollbarSender trace:[NSString stringWithFormat:@"Failed to transmit payload (%li status code): %@", (long)reply.statusCode, payloadString] + withOptions:config.developerOptions]; + } + else { + [RollbarSender trace:[NSString stringWithFormat:@"Failed to transmit payload (no reply): %@", payloadString] + withOptions:config.developerOptions]; + } + + return reply; +} + +- (nullable RollbarPayloadPostReply *)transmitPayload:(nonnull NSData *)payload + toDestination:(nonnull RollbarDestination *)destination + usingDeveloperOptions:(nullable RollbarDeveloperOptions *)developerOptions + andHttpProxySettings:(nullable RollbarProxy *)httpProxySettings + andHttpsProxySettings:(nullable RollbarProxy *)httpsProxySettings { + + NSAssert(payload, @"The payload must be initialized!"); + + NSAssert(destination, @"The destination must be initialized!"); + NSAssert(destination.endpoint, @"The destination endpoint must be initialized!"); + NSAssert(destination.accessToken, @"The destination access token must be initialized!"); + + if (!developerOptions) { + // then, let's use default developer options: + developerOptions = [RollbarDeveloperOptions new]; + } + + if (!httpProxySettings) { + // then, let's use default proxy settingd: + httpProxySettings = [RollbarProxy new]; + } + + if (!httpsProxySettings) { + // then, let's use default proxy settingd: + httpsProxySettings = [RollbarProxy new]; + } + + NSHTTPURLResponse *response = [self postPayload:payload + toDestination:destination + usingDeveloperOptions:developerOptions + andHttpProxySettings:httpProxySettings + andHttpsProxySettings:httpsProxySettings]; + + RollbarPayloadPostReply *reply = [RollbarPayloadPostReply replyFromHttpResponse:response]; + + return reply; +} + +- (nullable NSHTTPURLResponse *)postPayload:(nonnull NSData *)payload + toDestination:(nonnull RollbarDestination *)destination + usingDeveloperOptions:(nonnull RollbarDeveloperOptions *)developerOptions + andHttpProxySettings:(nonnull RollbarProxy *)httpProxySettings + andHttpsProxySettings:(nonnull RollbarProxy *)httpsProxySettings { + + + NSURL *url = [NSURL URLWithString:destination.endpoint]; + if (nil == url) { + NSString *message = + [NSString stringWithFormat:@"The destination endpoint URL is malformed: %@", destination.endpoint]; + [RollbarSender assertError:message + withOptions:developerOptions]; + return nil; + } + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setValue:destination.accessToken forHTTPHeaderField:@"X-Rollbar-Access-Token"]; + [request setHTTPBody:payload]; + + //__block BOOL result = NO; + __block NSHTTPURLResponse *httpResponse = nil; + + // This requires iOS 7.0+ + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + + NSURLSession *session = [NSURLSession sharedSession]; + + if (httpProxySettings.enabled + || httpsProxySettings.enabled) { + + NSDictionary *connectionProxyDictionary = + @{ + @"HTTPEnable" : [NSNumber numberWithBool:httpProxySettings.enabled], + @"HTTPProxy" : httpProxySettings.proxyUrl, + @"HTTPPort" : [NSNumber numberWithUnsignedInteger:httpProxySettings.proxyPort], + @"HTTPSEnable" : [NSNumber numberWithBool:httpsProxySettings.enabled], + @"HTTPSProxy" : httpsProxySettings.proxyUrl, + @"HTTPSPort" : [NSNumber numberWithUnsignedInteger:httpsProxySettings.proxyPort] + }; + + NSURLSessionConfiguration *sessionConfig = + [NSURLSessionConfiguration ephemeralSessionConfiguration]; + sessionConfig.connectionProxyDictionary = connectionProxyDictionary; + session = [NSURLSession sessionWithConfiguration:sessionConfig]; + } + + NSURLSessionDataTask *dataTask = + [session dataTaskWithRequest:request + completionHandler:^( + NSData * _Nullable data, + NSURLResponse * _Nullable response, + NSError * _Nullable error) { + httpResponse = [self checkPayloadResponse:response + error:error + data:data + usingDeveloperOptions:developerOptions]; + dispatch_semaphore_signal(sem); + }]; + [dataTask resume]; + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + return httpResponse; +} + +- (nullable NSHTTPURLResponse *)checkPayloadResponse:(NSURLResponse *)response + error:(NSError *)error + data:(NSData *)data + usingDeveloperOptions:(nonnull RollbarDeveloperOptions *)developerOptions { + + if (error) { + [RollbarSender trace:@"There was an error reporting to Rollbar:" + withOptions:developerOptions]; + [RollbarSender trace:[NSString stringWithFormat:@" Error: %@", [error localizedDescription]] + withOptions:developerOptions]; + return nil; + } + + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + if (httpResponse && (200 == [httpResponse statusCode])) { + [RollbarSender trace:[NSString stringWithFormat:@"OK response from Rollar: %@", httpResponse] + withOptions:developerOptions]; + return httpResponse; + } + + [RollbarSender trace:@"There was a problem reporting to Rollbar:" + withOptions:developerOptions]; + [RollbarSender trace:[NSString stringWithFormat:@" Response: %@", response] + withOptions:developerOptions]; + [RollbarSender trace:[NSString stringWithFormat:@" Response data: %@", data] + withOptions:developerOptions]; + // [RollbarSender trace:[NSString stringWithFormat:@" Response data: %@", + // [NSJSONSerialization JSONObjectWithData:data + // options:0 + // error:nil] + // ] + // withOptions:developerOptions]; + + return nil; +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.h index 03d01d7c..3c9c4b36 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.h +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.h @@ -31,13 +31,14 @@ NS_ASSUME_NONNULL_BEGIN + (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; +- (void)dealloc NS_UNAVAILABLE; +- (id)copy NS_UNAVAILABLE; +- (id)mutableCopy NS_UNAVAILABLE; + - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_UNAVAILABLE; - (instancetype)initWithBlock:(void (^)(void))block NS_UNAVAILABLE; -- (void)dealloc NS_UNAVAILABLE; -- (id)copy NS_UNAVAILABLE; -- (id)mutableCopy NS_UNAVAILABLE; @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.m index fd22485f..605aef66 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarSession.m @@ -8,6 +8,7 @@ #import "RollbarSession.h" #import "RollbarSessionState.h" #import "RollbarLogger.h" +#import "RollbarNotifierFiles.h" #import "Rollbar.h" #import @@ -29,9 +30,6 @@ #pragma mark - constants -static NSString * const SESSION_FILE_NAME = @"rollbar.session"; -static NSString * const APP_QUIT_FILE_NAME = @"rollbar.appquit"; - static char *appQuitFilePath; #pragma mark - RollbarSession (Protected) @@ -107,7 +105,7 @@ - (void)enableOomMonitoring:(BOOL)enableOomDetection crashCheck ? crashCheck : [self registerDefaultCrashCheck]; if (YES == [RollbarCachesDirectory ensureCachesDirectoryExists]) { - appQuitFilePath = strdup([[RollbarCachesDirectory getCacheFilePath:APP_QUIT_FILE_NAME] UTF8String]); + appQuitFilePath = strdup([[RollbarCachesDirectory getCacheFilePath:[RollbarNotifierFiles appQuit]] UTF8String]); } [self deduceOomTermination]; @@ -444,7 +442,7 @@ - (instancetype)init { self = [super init]; if (self) { - self->_stateFilePath = [RollbarCachesDirectory getCacheFilePath:SESSION_FILE_NAME]; + self->_stateFilePath = [RollbarCachesDirectory getCacheFilePath:[RollbarNotifierFiles runtimeSession]]; if (NO == [RollbarCachesDirectory ensureCachesDirectoryExists]) { @@ -452,7 +450,7 @@ - (instancetype)init { return self; } - if (YES == [RollbarCachesDirectory checkCacheFileExists:SESSION_FILE_NAME]) { + if (YES == [RollbarCachesDirectory checkCacheFileExists:[RollbarNotifierFiles runtimeSession]]) { self->_state = [self loadSessionState]; } diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m index 9037b1ad..6544a2e7 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m @@ -2,6 +2,7 @@ #import "RollbarTelemetry.h" #import "RollbarCachesDirectory.h" +#import "RollbarNotifierFiles.h" #import "RollbarTelemetryOptions.h" #import "RollbarScrubbingOptions.h" @@ -17,7 +18,6 @@ #import "RollbarTelemetryManualBody.h" static NSUInteger const DEFAULT_DATA_LIMIT = 10; -static NSString * const TELEMETRY_FILE_NAME = @"rollbar.telemetry"; static BOOL captureLog = false; @@ -87,7 +87,7 @@ - (instancetype)init { // Create cache file NSString *cachesDirectory = [RollbarCachesDirectory directory]; - _dataFilePath = [cachesDirectory stringByAppendingPathComponent:TELEMETRY_FILE_NAME]; + _dataFilePath = [cachesDirectory stringByAppendingPathComponent:[RollbarNotifierFiles telemetryQueue]]; _viewInputsToScrub = [NSMutableSet new]; @@ -116,6 +116,11 @@ - (nonnull instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *) return self; } +- (nonnull RollbarTelemetryOptions *)telemetryOptions { + + return [RollbarTelemetryThread sharedInstance].telemetryOptions; +} + - (void)setCaptureLog:(BOOL)shouldCapture { dispatch_async(queue, ^{ diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h index cc7053fc..57db5069 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h @@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN /// @param telemetryOptions desired Telemetry options - (instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions; +@property (readonly, nonnull) RollbarTelemetryOptions *telemetryOptions; + /// Signifies that the thread is active or not. @property(atomic) BOOL active; diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m index 9e6c14b5..98db8c5c 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m @@ -86,6 +86,11 @@ - (instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetr return self; } +- (nonnull RollbarTelemetryOptions *)telemetryOptions { + + return self->_telemetryOptions; +} + - (BOOL)setupTimer { self->_collectionTimeInterval = [RollbarTelemetryThread calculateCollectionTimeInterval:self->_telemetryOptions]; diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.h index 38f12483..75f580ca 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.h +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.h @@ -1,20 +1,49 @@ @import Foundation; -@class RollbarLogger; +#import "RollbarConfig.h" +#import "RollbarPayload.h" + +NS_ASSUME_NONNULL_BEGIN @interface RollbarThread : NSThread +/// Signifies that the thread is active or not. +@property(atomic) BOOL active; + +//- (void)persist:(nonnull RollbarPayload *)payload; + +- (void)persistPayload:(nonnull RollbarPayload *)payload + withConfig:(nonnull RollbarConfig *)config; + +- (RollbarTriStateFlag)sendPayload:(nonnull NSData *)payload + usingConfig:(nonnull RollbarConfig *)config; + +/// Hides the initializer. +- (instancetype)init NS_UNAVAILABLE; + /// Hides the initializer. -- (instancetype)init +- (instancetype)initWithTarget:(id)target + selector:(SEL)selector + object:(nullable id)argument NS_UNAVAILABLE; -/// Initializer -/// @param logger a Rollbar logger to use. -/// @param reportsPerMinute maximum allowed reporting rate. -- (instancetype)initWithNotifier:(RollbarLogger*)logger - reportingRate:(NSUInteger)reportsPerMinute; +- (instancetype)initWithBlock:(void (^)(void))block NS_UNAVAILABLE; -/// Signifies that the thread is active or not. -@property(atomic) BOOL active; + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance; + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +- (void)dealloc NS_UNAVAILABLE; +- (id)copy NS_UNAVAILABLE; +- (id)mutableCopy NS_UNAVAILABLE; @end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.m index 6ae5e250..8b7f329b 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarThread.m @@ -1,57 +1,101 @@ @import RollbarCommon; #import "RollbarThread.h" -#import "RollbarLogger.h" -#import "RollbarConfig.h" -#import "RollbarDeveloperOptions.h" +#import "RollbarReachability.h" +#import "RollbarTelemetry.h" +#import "RollbarNotifierFiles.h" +#import "RollbarPayloadTruncator.h" +#import "RollbarData.h" +#import "RollbarModule.h" +#import "RollbarProxy.h" +#import "RollbarSender.h" +#import "RollbarPayloadPostReply.h" +#import "RollbarRegistry.h" +#import "RollbarPayloadRepository.h" + +static NSTimeInterval const DEFAULT_PAYLOAD_LIFETIME_SECONDS = 24 * 60 * 60; +// hours-per day * 60 min-per-hour * 60 sec-per-min = 1 day in sec @implementation RollbarThread { - @private RollbarLogger *_logger; - @private NSUInteger _maxReportsPerMinute; - @private NSTimer *_timer; -} -- (instancetype)initWithNotifier:(RollbarLogger*)logger - reportingRate:(NSUInteger)reportsPerMinute { +@private + NSUInteger _maxReportsPerMinute; + NSTimeInterval _payloadLifetimeInSeconds; + NSTimer *_timer; + NSString *_payloadsRepoFilePath; + RollbarRegistry *_registry; + RollbarPayloadRepository *_payloadsRepo; +#if !TARGET_OS_WATCH + RollbarReachability *_reachability; + BOOL _isNetworkReachable; +#endif + +} + +- (instancetype)initWithTarget:(id)target + selector:(SEL)selector + object:(nullable id)argument { + if ((self = [super initWithTarget:self selector:@selector(run) object:nil])) { - self.name = @"RollbarThread"; - _logger = logger; + [self setupDataStorage]; - if(reportsPerMinute > 0) { - _maxReportsPerMinute = reportsPerMinute; - } else { - _maxReportsPerMinute = 60; - } + self->_maxReportsPerMinute = 240;//60; + self->_payloadLifetimeInSeconds = DEFAULT_PAYLOAD_LIFETIME_SECONDS; + self->_registry = [RollbarRegistry new]; +#if !TARGET_OS_WATCH + self->_reachability = nil; + self->_isNetworkReachable = YES; +#endif + + self.name = [RollbarThread rollbar_objectClassName];//NSStringFromClass([RollbarThread class]); self.active = YES; + + +#if !TARGET_OS_WATCH + // Listen for reachability status so that the items are only sent when the internet is available: + self->_reachability = [RollbarReachability reachabilityForInternetConnection]; + self->_isNetworkReachable = [self->_reachability isReachable]; + + __weak typeof(self) weakSelf = self; + //OR __unsafe_unretained typeof(self) weakSelf = self; + self->_reachability.reachableBlock = ^(RollbarReachability*reach) { + [weakSelf captureTelemetryDataForNetwork:true]; + self->_isNetworkReachable = YES; + }; + self->_reachability.unreachableBlock = ^(RollbarReachability*reach) { + [weakSelf captureTelemetryDataForNetwork:false]; + self->_isNetworkReachable = NO; + }; + + [self->_reachability startNotifier]; +#endif + } + [self start]; + return self; } -- (void)checkItems { +- (void)setupDataStorage { - if (self.cancelled) { - if (_timer) { - [_timer invalidate]; - _timer = nil; - } - [NSThread exit]; - } + // create working cache directory: + [RollbarCachesDirectory ensureCachesDirectoryExists]; + NSString *cachesDirectory = [RollbarCachesDirectory directory]; - @autoreleasepool { - - if ((nil != _logger) && (NO == _logger.configuration.developerOptions.suppressSdkInfoLogging)) { - - RollbarSdkLog(@"Checking items..."); - } - - [_logger processSavedItems]; - } + // setup persistent payloads store/repo: + self->_payloadsRepoFilePath = + [cachesDirectory stringByAppendingPathComponent:[RollbarNotifierFiles payloadsStore]]; + self->_payloadsRepo = + [RollbarPayloadRepository persistentRepositoryWithPath:self->_payloadsRepoFilePath]; + NSAssert([[NSFileManager defaultManager] fileExistsAtPath:self->_payloadsRepoFilePath], + @"Persistent payloads store was not created: %@!!!", self->_payloadsRepoFilePath + ); } - (void)run { @@ -76,4 +120,569 @@ - (void)run { } } +#pragma mark - persisting payload items + +- (void)persistPayload:(nonnull RollbarPayload *)payload + withConfig:(nonnull RollbarConfig *)config { + + [self performSelector:@selector(queuePayload_OnlyCallOnThisThread:) + onThread:self //[RollbarThread sharedInstance] + withObject:@[payload, config] + waitUntilDone:NO + ]; +} + ++ (BOOL)shouldIgnorePayload:(nonnull RollbarPayload *)payload + withConfig:(nonnull RollbarConfig *)config { + + if (config.checkIgnoreRollbarData) { + + BOOL shouldIgnore = NO; + @try { + shouldIgnore = config.checkIgnoreRollbarData(payload.data); + } + @catch(NSException *e) { + RollbarSdkLog(@"checkIgnore error: %@", e.reason); + NSAssert(false, @"Provided checkIgnore implementation throws an exception!"); + shouldIgnore = NO; + } + @finally { + return shouldIgnore; + } + } + + return NO; +} + ++ (nullable RollbarPayload *)modifyPayload:(nonnull RollbarPayload *)payload + withConfig:(nonnull RollbarConfig *)config { + + if (config.modifyRollbarData) { + + @try { + payload.data = config.modifyRollbarData(payload.data); + } + @catch(NSException *e) { + RollbarSdkLog(@"modifyRollbarData error: %@", e.reason); + NSAssert(false, @"Provided modifyRollbarData implementation throws an exception!"); + //return null; + } + } + return payload; +} + ++ (nullable RollbarPayload *)scrubPayload:(nonnull RollbarPayload *)payload + withConfig:(nonnull RollbarConfig *)config { + + NSSet *scrubFieldsSet = [RollbarThread getScrubFields:config.dataScrubber]; + if (scrubFieldsSet.count == 0) { + return payload; + } + + NSMutableDictionary *mutableJsonFriendlyData = payload.data.jsonFriendlyData.mutableCopy; + for (NSString *key in scrubFieldsSet) { + if ([mutableJsonFriendlyData valueForKeyPath:key]) { + [RollbarThread createMutableDataWithData:mutableJsonFriendlyData + forPath:key]; + [mutableJsonFriendlyData setValue:@"*****" + forKeyPath:key]; + } + } + + payload.data = [[RollbarData alloc] initWithDictionary:mutableJsonFriendlyData]; + + return payload; +} + ++ (nonnull NSSet *)getScrubFields:(nullable RollbarScrubbingOptions *)scrubbingOptions { + + if (!scrubbingOptions + || scrubbingOptions.isEmpty + || !scrubbingOptions.enabled + || !scrubbingOptions.scrubFields + || scrubbingOptions.scrubFields.count == 0) { + + return [NSSet set]; + } + + NSMutableSet *actualFieldsToScrub = scrubbingOptions.scrubFields.mutableCopy; + if (scrubbingOptions.safeListFields.count > 0) { + // actualFieldsToScrub = + // config.dataScrubber.scrubFields - config.dataScrubber.whitelistFields + // while using case insensitive field name comparison: + actualFieldsToScrub = [NSMutableSet new]; + for(NSString *key in scrubbingOptions.scrubFields) { + BOOL isWhitelisted = false; + for (NSString *whiteKey in scrubbingOptions.safeListFields) { + if (NSOrderedSame == [key caseInsensitiveCompare:whiteKey]) { + isWhitelisted = true; + } + } + if (!isWhitelisted) { + [actualFieldsToScrub addObject:key]; + } + } + } + + return actualFieldsToScrub; +} + ++ (void)createMutableDataWithData:(NSMutableDictionary *)data + forPath:(NSString *)path { + + NSArray *pathComponents = [path componentsSeparatedByString:@"."]; + NSString *currentPath = @""; + + for (int i=0; i_payloadsRepo getIDofDestinationWithEndpoint:config.destination.endpoint + andAccesToken:config.destination.accessToken]; + + NSString *configJson = [config serializeToJSONString]; + NSAssert(configJson && configJson.length > 0, @"invalid configJson!"); + if (!configJson || (0 == configJson.length)) { + RollbarSdkLog(@"invalid configJson!"); + return; + } + + //[payload.data.notifier setData:config.jsonFriendlyData byKey:@"configured_options"]; + NSString *payloadJson = [payload serializeToJSONString]; + NSDictionary *payloadDataRow = [self->_payloadsRepo addPayload:payloadJson + withConfig:configJson + andDestinationID:destinationID]; + if (!payloadDataRow || !payloadDataRow[@"id"]) { + RollbarSdkLog(@"*** Couldn't add a payload to the repo: %@", payloadJson); + RollbarSdkLog(@"*** with config: %@", configJson); + RollbarSdkLog(@"*** with destinationID: %@", destinationID); + RollbarSdkLog(@"*** Resulting payloadDataRow: %@", payloadDataRow); + } + + NSAssert(payloadDataRow && payloadDataRow[@"id"], @"Couldn't add a payload to the repo: %@", payloadJson); +} + +#pragma mark - processing persisted payload items + +- (void)checkItems { + + if (self.cancelled) { + if (_timer) { + [_timer invalidate]; + _timer = nil; + } + [NSThread exit]; + } + + @autoreleasepool { + + // if ((nil != _logger) && (NO == _logger.configuration.developerOptions.suppressSdkInfoLogging)) { + // + // RollbarSdkLog(@"Checking items..."); + // } + + [self processSavedItems]; + } +} + +- (BOOL)checkProcessStalePayload:(nonnull NSDictionary *)payloadDataRow { + + // let's make sure we are not dealng with a stale payload: + NSString *timestampValue = payloadDataRow[@"created_at"]; + NSScanner *scanner = [NSScanner scannerWithString:timestampValue]; + double payloadTimestamp; + BOOL timestampParsingSuccess = [scanner scanDouble:&payloadTimestamp]; + if (!timestampParsingSuccess + || ((payloadTimestamp + self->_payloadLifetimeInSeconds) < [NSDate date].timeIntervalSince1970) + ) { + // we either have some sort of timestamp corruption or + // we are processing a stale payload let's just drop it and call it done: + [self->_payloadsRepo removePayloadByID:payloadDataRow[@"id"]]; + + RollbarConfig *config = [[RollbarConfig alloc] initWithJSONString:payloadDataRow[@"config_json"]]; + + if (config && config.developerOptions.logTransmittedPayloads) { + NSString *payloadsLogFile = config.developerOptions.droppedPayloadsLogFile; + if (payloadsLogFile && (payloadsLogFile.length > 0)) { + NSString *cachesDirectory = [RollbarCachesDirectory directory]; + NSString *payloadsLogFilePath = [cachesDirectory stringByAppendingPathComponent:payloadsLogFile]; + RollbarPayload *payload = [[RollbarPayload alloc] initWithJSONString:payloadDataRow[@"payload_json"]]; + [RollbarFileWriter appendSafelyData: payload.serializeToJSONData toFile:payloadsLogFilePath]; + } + } + + if (config && !config.developerOptions.suppressSdkInfoLogging) { + RollbarSdkLog(@"Dropped a stale payload: %@", payloadDataRow[@"payload_json"]); + } + + return YES; + } + + return NO; +} + +- (void)processSavedPayload:(nonnull NSDictionary *)payloadDataRow { + + if ([self checkProcessStalePayload:payloadDataRow]) { + return; + } + + NSString *destinationKey = payloadDataRow[@"destination_key"]; + NSAssert(destinationKey && destinationKey.length > 0, @"destination_key is expected to be defined!"); + NSDictionary *destination = [self->_payloadsRepo getDestinationByID:destinationKey]; + + //TODO: remove this code-block before the upcoming major release: + if (!destination) { + RollbarSdkLog(@"Aha!"); + [self->_payloadsRepo removePayloadByID:payloadDataRow[@"id"]]; + return; + } + + NSAssert(destination, @"destination can not be nil!"); + NSAssert(destination[@"endpoint"], @"destination endpoint can not be nil!"); + NSAssert(destination[@"access_token"], @"destination access_token can not be nil!"); + RollbarDestinationRecord *destinationRecord = [self->_registry getRecordForEndpoint:destination[@"endpoint"] + andAccessToken:destination[@"access_token"]]; + NSString *configJson = payloadDataRow[@"config_json"]; + NSAssert(configJson && configJson.length > 0, @"config_json is expected to be defined!"); + RollbarConfig *config = [[RollbarConfig alloc] initWithJSONString:configJson]; + NSAssert(config, @"config is expected to be defined!"); + + if (![destinationRecord canPostWithConfig:config]) { + return; + } + + NSString *payloadJson = payloadDataRow[@"payload_json"]; + RollbarPayload *payload = [[RollbarPayload alloc] initWithJSONString:payloadJson]; + NSAssert(payload, @"payload is expected to be defined!"); + + NSError *error; + NSData *jsonPayload = [NSJSONSerialization rollbar_dataWithJSONObject:payload.jsonFriendlyData + options:0 + error:&error + safe:true]; + if (nil == jsonPayload) { + RollbarSdkLog(@"Couldn't send jsonPayload that is nil"); + if (nil != error) { + RollbarSdkLog(@" DETAILS: an error while generating JSON data: %@", error); + } + // there is nothing we can do with this payload - let's drop it: + RollbarSdkLog(@"Dropping unprocessable payload: %@", payloadJson); + if (![self->_payloadsRepo removePayloadByID:payloadDataRow[@"id"]]) { + RollbarSdkLog(@"Couldn't remove payload data row with ID: %@", payloadDataRow[@"id"]); + } + return; + } + + RollbarTriStateFlag success = RollbarTriStateFlag_On; + if (!config) { + success = [self sendPayload:jsonPayload]; // backward compatibility with just upgraded very old SDKs... + } + else if (config.developerOptions.transmit) { + success = [self sendPayload:jsonPayload usingConfig:config]; + } + + NSString *payloadsLogFile = nil; + NSString *sdkLogTrace = (RollbarTriStateFlag_None == success) ? nil + : [NSString stringWithFormat:@"%@ payload: %@", + (RollbarTriStateFlag_On == success) ? @"Transmitted" : @"Dropped", + [[NSString alloc] initWithData:jsonPayload encoding:NSUTF8StringEncoding] + ]; + switch(success) { + case RollbarTriStateFlag_On: + // The payload is fully processed and transmitted. + // It can be removed from the repo: + if (![self->_payloadsRepo removePayloadByID:payloadDataRow[@"id"]]) { + RollbarSdkLog(@"Couldn't remove payload data row with ID: %@", payloadDataRow[@"id"]); + } + if (config.developerOptions.logTransmittedPayloads) { + payloadsLogFile = config.developerOptions.transmittedPayloadsLogFile; + } + break; + case RollbarTriStateFlag_Off: + // The payload is fully processed but not accepted by the server due to some invalid content. + // It must be removed from the repo: + if (![self->_payloadsRepo removePayloadByID:payloadDataRow[@"id"]]) { + RollbarSdkLog(@"Couldn't remove payload data row with ID: %@", payloadDataRow[@"id"]); + } + if (config.developerOptions.logDroppedPayloads) { + payloadsLogFile = config.developerOptions.droppedPayloadsLogFile; + } + break; + case RollbarTriStateFlag_None: + default: + // Nothing obviously wrong with the payload but it was not actually tranmitted successfully. + // Let's try again some other time. Keep it in the repo for now... + break; + } + + if (payloadsLogFile) { + NSString *cachesDirectory = [RollbarCachesDirectory directory]; + NSString *payloadsLogFilePath = [cachesDirectory stringByAppendingPathComponent:payloadsLogFile]; + [RollbarFileWriter appendSafelyData:jsonPayload toFile:payloadsLogFilePath]; + } + if (!config.developerOptions.suppressSdkInfoLogging) { + NSString *sdkLogTrace = nil; + switch(success) { + case RollbarTriStateFlag_On: + RollbarSdkLog(@"Transmitted payload: %@", + [[NSString alloc] initWithData:jsonPayload encoding:NSUTF8StringEncoding]); + break; + case RollbarTriStateFlag_Off: + RollbarSdkLog(@"Dropped payload: %@", + [[NSString alloc] initWithData:jsonPayload encoding:NSUTF8StringEncoding]); + break; + case RollbarTriStateFlag_None: + RollbarSdkLog(@"Couldn't transmit (and will try) payload: %@", + [[NSString alloc] initWithData:jsonPayload encoding:NSUTF8StringEncoding]); + break; + } + } +} + +- (void)processSavedItems { + +#if !TARGET_OS_WATCH + if (!self->_isNetworkReachable) { + RollbarSdkLog(@"Processing saved items: no network!"); + // Don't attempt sending if the network is known to be not reachable + return; + } +#endif + + NSArray *> *payloads = [self->_payloadsRepo getPayloadsWithOffset:0 andLimit:5]; + for(NSDictionary *payload in payloads) { + @try { + [self processSavedPayload:payload]; + } @catch (NSException *exception) { + RollbarSdkLog(@"Payload processing EXCEPTION: %@", exception); + } @finally { + } + } +} + +- (RollbarTriStateFlag)sendPayload:(nonnull NSData *)payload + usingConfig:(nonnull RollbarConfig *)config { + + if (!payload || !config) { + + return RollbarTriStateFlag_Off; //obviously invalid payload to sent or invalid destination... + } + + RollbarDestinationRecord *record = [self->_registry getRecordForConfig:config]; + if (![record canPost]) { + return RollbarTriStateFlag_None; // nothing obviously wrong with the payload - just can not send at the moment + } + + RollbarPayloadPostReply *reply = [[RollbarSender new] sendPayload:payload + usingConfig:config + ]; + [record recordPostReply:reply]; + + if (!reply) { + return RollbarTriStateFlag_None; // nothing obviously wrong with the payload - just there was no deterministic + // reply from the destination server + } + + switch(reply.statusCode) { + case 200: // OK + return RollbarTriStateFlag_On; // the payload was successfully transmitted + case 400: // bad request + case 413: // request entity too large + case 422: // unprocessable entity + return RollbarTriStateFlag_Off; // unecceptable request/payload - should be dropped + case 403: // access denied + case 404: // not found + case 429: // too many requests + default: + return RollbarTriStateFlag_None; // worth retrying later + } +} + +/// This is a DEPRECATED method left for some backward compatibility for very old clients eventually moving to this more recent implementation. +/// Use/maintain sendPayload:usingConfig: instead! +- (RollbarTriStateFlag)sendPayload:(NSData *)payload { + + return RollbarTriStateFlag_Off; +} + +#pragma mark - Network telemetry data + +- (void)captureTelemetryDataForNetwork:(BOOL)reachable { + +#if !TARGET_OS_WATCH + if ([RollbarTelemetry sharedInstance].telemetryOptions.captureConnectivity + && self->_isNetworkReachable != reachable) { + + NSString *status = reachable ? @"Connected" : @"Disconnected"; + NSString *networkType = @"Unknown"; + NetworkStatus networkStatus = [self->_reachability currentReachabilityStatus]; + switch(networkStatus) { + case ReachableViaWiFi: + networkType = @"WiFi"; + break; + case ReachableViaWWAN: + networkType = @"Cellular"; + break; + default: + // no-op... + break; + } + [[RollbarTelemetry sharedInstance] recordConnectivityEventForLevel:RollbarLevel_Warning + status:status + extraData:@{@"network": networkType} + ]; + } +#endif +} + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance { + + static id singleton; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + + singleton = [[self alloc] initWithTarget:self + selector:@selector(run) + object:nil]; + }); + + return singleton; +} + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/Rollbar.h b/RollbarNotifier/Sources/RollbarNotifier/include/Rollbar.h index c25923bb..20eee813 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/Rollbar.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/Rollbar.h @@ -3,22 +3,29 @@ @import Foundation; +#import "RollbarInfrastructure.h" #import "RollbarLevel.h" #import "RollbarTelemetry.h" #import "RollbarTelemetryType.h" -@class RollbarConfig; -@class RollbarLogger; -@protocol RollbarCrashCollector; +#import "RollbarCrashCollectorProtocol.h" +#import "RollbarConfig.h" +//#import "RollbarLogger.h" -@interface Rollbar : NSObject -#pragma mark - Class Initializers +#pragma mark - Initialization Facade Protocol + +/// Rollbar facade initialization protocol +@protocol RollbarFacadeInitialization + +@required /// Class initializer. /// @param accessToken Rollbar project access token + (void)initWithAccessToken:(nonnull NSString *)accessToken; +@optional + /// Class initializer. /// @param configuration a Rollbar configuration + (void)initWithConfiguration:(nonnull RollbarConfig *)configuration; @@ -34,25 +41,32 @@ /// @param crashCollector a crash collector + (void)initWithConfiguration:(nonnull RollbarConfig *)configuration crashCollector:(nullable id)crashCollector; + @end -#pragma mark - Shared/global notifier -/// The shared singleton logger. -+ (nonnull RollbarLogger *)currentLogger; +#pragma mark - Configuration Facade Protocol -#pragma mark - Configuration +/// Rollbar facade configuration protocol +@protocol RollbarFacadeConfiguration // + +@required /// The shared Rollbar master configuration. -+ (nullable RollbarConfig *)currentConfiguration; ++ (nonnull RollbarConfig *)configuration; /// Updates with a shared configuration. /// @param configuration a new Rollbar configuration -+ (void)updateConfiguration:(nonnull RollbarConfig *)configuration; ++ (void)updateWithConfiguration:(nonnull RollbarConfig *)configuration; -/// Forces fundamental configuration update -+ (void)reapplyConfiguration; +@end -#pragma mark - Logging methods + +#pragma mark - Logging Facade Protocol + +/// Rollbar facade logging protocol +@protocol RollbarFacadeLogging // + +@required /// Logs a crash report /// @param crashReport a crash report @@ -130,7 +144,7 @@ data:(nullable NSDictionary *)data context:(nullable NSString *)context; -#pragma mark - Convenience logging methods +@optional /// Performs debug level logging /// @param message a message @@ -403,14 +417,15 @@ data:(nullable NSDictionary *)data context:(nullable NSString *)context; +@end + -#pragma mark - Send manually constructed JSON payload +#pragma mark - Telemetry Facade Protocol -/// Posts a Json payload -/// @param payload a Json payload -+ (void)sendJsonPayload:(nonnull NSData *)payload; +/// Rollbar facade telemetry protocol +@protocol RollbarFacadeTelemetry // -#pragma mark - Telemetry API +@required /// Captures a view telemetry event /// @param level Rollbar log level @@ -508,4 +523,52 @@ @end + +#pragma mark - Rollbar Facade Utility Class + +/// Rollbar facade utility class +@interface Rollbar +: NSObject + +#pragma mark - Send manually constructed JSON payload + +/// Posts a Json payload +/// @param payload a Json payload ++ (void)sendJsonPayload:(nonnull NSData *)payload; + +#pragma mark - Instantiation blockers + ++ (nonnull instancetype)new NS_UNAVAILABLE; ++ (nonnull instancetype)allocWithZone:(nullable struct _NSZone *)zone NS_UNAVAILABLE; ++ (nonnull instancetype)alloc NS_UNAVAILABLE; ++ (nullable id)copyWithZone:(nullable struct _NSZone *)zone NS_UNAVAILABLE; ++ (nullable id)mutableCopyWithZone:(nullable struct _NSZone *)zone NS_UNAVAILABLE; + +- (nonnull instancetype)init NS_UNAVAILABLE; +- (void)dealloc NS_UNAVAILABLE; +- (nonnull id)copy NS_UNAVAILABLE; +- (nonnull id)mutableCopy NS_UNAVAILABLE; + +@end + + +#pragma mark - Exception Raising + +// Macros that help in throwing an exception with its source location metadata included: +#define __ThrowException(name, reason, class, function, file, line, info) [NSException exceptionWithName:name reason:[NSString stringWithFormat:@"%s:%i (%@:%s) %@", file, line, class, function, reason] userInfo:info]; +#define ThrowException(name, reason, info) __ThrowException(name, reason, [self class], _cmd, __FILE__, __LINE__, info) + + +#pragma mark - Unhandled Exception Handler + +/// Globa;l uncaught exception handler that sends provided exception data to Rollbar via preconfigured RollbarInfrastructure's shared instnace. +/// @param exception an exception to report to Rolbar +// Add a call to the: NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); +// to the end of your: -(BOOL)application:didFinishLaunchingWithOptions: method in AppDelegate. +// Make sure that the [RollbarInfrastructure sharedInstance] was already configured as early as possible within the: +// -(BOOL)application:didFinishLaunchingWithOptions: method in AppDelegate. +static void uncaughtExceptionHandler(NSException * _Nonnull exception); + + + #endif //Rollbar_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarConfig.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarConfig.h index 077aed3e..e0bd1470 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarConfig.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarConfig.h @@ -1,71 +1,152 @@ #ifndef RollbarConfig_h #define RollbarConfig_h +#import "RollbarDestination.h" +#import "RollbarDeveloperOptions.h" +#import "RollbarProxy.h" +#import "RollbarScrubbingOptions.h" +#import "RollbarServerConfig.h" +#import "RollbarPerson.h" +#import "RollbarModule.h" +#import "RollbarTelemetryOptions.h" +#import "RollbarLoggingOptions.h" +#import "RollbarData.h" #import "RollbarCaptureIpType.h" #import "RollbarLevel.h" @import RollbarCommon; -@class RollbarDestination; -@class RollbarDeveloperOptions; -@class RollbarProxy; -@class RollbarScrubbingOptions; -@class RollbarServerConfig; -@class RollbarPerson; -@class RollbarModule; -@class RollbarTelemetryOptions; -@class RollbarLoggingOptions; -@class RollbarData; - NS_ASSUME_NONNULL_BEGIN -/// Rollbar configuration structured model +@class RollbarMutableConfig; + +typedef BOOL (^RollbarCheckIgnoreData)(RollbarData *rollbarData); +typedef RollbarData *_Nonnull(^RollbarModifyData)(RollbarData *rollbarData); + + +/// Immutable Rollbar configuration structured model @interface RollbarConfig : RollbarDTO { + + RollbarCheckIgnoreData _checkIgnoreRollbarData; + RollbarModifyData _modifyRollbarData; + + //TODO: to be removed: BOOL _isRootConfiguration; } +#pragma mark - Factory Methods + ++ (nonnull RollbarConfig *)configWithAccessToken:(nonnull NSString *)token; + ++ (nonnull RollbarConfig *)configWithAccessToken:(nonnull NSString *)token + environment:(nonnull NSString *)env; + ++ (nonnull RollbarMutableConfig *)mutableConfigWithAccessToken:(nonnull NSString *)token; + ++ (nonnull RollbarMutableConfig *)mutableConfigWithAccessToken:(nonnull NSString *)token + environment:(nonnull NSString *)env; + #pragma mark - properties /// Destination related settings -@property (nonnull, nonatomic, strong) RollbarDestination *destination; +@property (nonnull, nonatomic, readonly, strong) RollbarDestination *destination; /// Developer related settings -@property (nonnull, nonatomic, strong) RollbarDeveloperOptions *developerOptions; +@property (nonnull, nonatomic, readonly, strong) RollbarDeveloperOptions *developerOptions; /// Logging related settings -@property (nonnull, nonatomic, strong) RollbarLoggingOptions *loggingOptions; +@property (nonnull, nonatomic, readonly, strong) RollbarLoggingOptions *loggingOptions; /// HTTP proxy related settings -@property (nonnull, nonatomic, strong) RollbarProxy *httpProxy; +@property (nonnull, nonatomic, readonly, strong) RollbarProxy *httpProxy; /// HTTPS proxy related settings -@property (nonnull, nonatomic, strong) RollbarProxy *httpsProxy; +@property (nonnull, nonatomic, readonly, strong) RollbarProxy *httpsProxy; /// Data scrubbing related settings -@property (nonnull, nonatomic, strong) RollbarScrubbingOptions *dataScrubber; +@property (nonnull, nonatomic, readonly, strong) RollbarScrubbingOptions *dataScrubber; /// Server related settings -@property (nonnull, nonatomic, strong) RollbarServerConfig *server; +@property (nonnull, nonatomic, readonly, strong) RollbarServerConfig *server; /// Person/user related settings -@property (nonnull, nonatomic, strong) RollbarPerson *person; +@property (nonnull, nonatomic, readonly, strong) RollbarPerson *person; /// Notifier related settings -@property (nonnull, nonatomic, strong) RollbarModule *notifier; +@property (nonnull, nonatomic, readonly, strong) RollbarModule *notifier; /// Telemetry related settings -@property (nonnull, nonatomic, strong) RollbarTelemetryOptions *telemetry; +@property (nonnull, nonatomic, readonly, strong) RollbarTelemetryOptions *telemetry; #pragma mark - Custom data -@property (nonatomic, strong) NSDictionary *customData; +@property (nonatomic, readonly, strong) NSDictionary *customData; #pragma mark - Payload Content Related /// Decides whether or not to send provided payload data. Returns true to ignore, false to send -@property (nullable, nonatomic, copy) BOOL (^checkIgnoreRollbarData)(RollbarData *rollbarData); +@property (nullable, atomic, readonly, copy) RollbarCheckIgnoreData checkIgnoreRollbarData; + /// Modifies payload data before sending -@property (nullable, nonatomic, copy) RollbarData *(^modifyRollbarData)(RollbarData *rollbarData); +@property (nullable, atomic, readonly, copy) RollbarModifyData modifyRollbarData; + +#pragma mark - overrides + +- (nonnull RollbarMutableConfig *) mutableCopy; + +@end + + +/// Mutable Rollbar configuration structured model +@interface RollbarMutableConfig : RollbarConfig + +#pragma mark - initializers + +- (instancetype)init +NS_DESIGNATED_INITIALIZER; + +#pragma mark - properties + +/// Destination related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableDestination *destination; + +/// Developer related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableDeveloperOptions *developerOptions; +/// Logging related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableLoggingOptions *loggingOptions; + +/// HTTP proxy related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableProxy *httpProxy; + +/// HTTPS proxy related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableProxy *httpsProxy; + +/// Data scrubbing related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableScrubbingOptions *dataScrubber; + +/// Server related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableServerConfig *server; + +/// Person/user related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutablePerson *person; + +/// Notifier related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableModule *notifier; + +/// Telemetry related settings +@property (nonnull, nonatomic, readwrite, strong) RollbarMutableTelemetryOptions *telemetry; + +#pragma mark - Custom data + +@property (nonatomic, readwrite, strong) NSMutableDictionary *customData; + +#pragma mark - Payload Content Related + +/// Decides whether or not to send provided payload data. Returns true to ignore, false to send +@property (nullable, atomic, readwrite, copy) RollbarCheckIgnoreData checkIgnoreRollbarData; + +/// Modifies payload data before sending +@property (nullable, atomic, readwrite, copy) RollbarModifyData modifyRollbarData; #pragma mark - Convenience Methods @@ -94,6 +175,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)setNotifierName:(nullable NSString *)name version:(nullable NSString *)version; +#pragma mark - overrides + +- (nonnull RollbarConfig *) copy; @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDestination.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDestination.h index 94f3594f..0a39de03 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDestination.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDestination.h @@ -11,13 +11,13 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - properties /// Endpoint URI -@property (nonatomic, copy) NSString *endpoint; +@property (nonatomic, readonly, copy) NSString *endpoint; /// Project access token -@property (nonatomic, copy) NSString *accessToken; +@property (nonatomic, readonly, copy) NSString *accessToken; /// Environment name -@property (nonatomic, copy) NSString *environment; +@property (nonatomic, readonly, copy) NSString *environment; #pragma mark - initializers @@ -41,6 +41,46 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface RollbarMutableDestination : RollbarDestination + +#pragma mark - initializers + +- (instancetype)init +NS_DESIGNATED_INITIALIZER; + +#pragma mark - properties + +/// Endpoint URI +@property (nonatomic, readwrite, copy) NSString *endpoint; + +/// Project access token +@property (nonatomic, readwrite, copy) NSString *accessToken; + +/// Environment name +@property (nonatomic, readwrite, copy) NSString *environment; + +//#pragma mark - initializers +// +///// Initializer +///// @param endpoint endpoint URI +///// @param accessToken Rollbar project access token +///// @param environment environment name +//- (instancetype)initWithEndpoint:(NSString *)endpoint +// accessToken:(NSString *)accessToken +// environment:(NSString *)environment; +// +///// Initializer +///// @param accessToken Rollbar project access token +///// @param environment environment name +//- (instancetype)initWithAccessToken:(NSString *)accessToken +// environment:(NSString *)environment; +// +///// Initializer +///// @param accessToken Rollbar project access token +//- (instancetype)initWithAccessToken:(NSString *)accessToken; + +@end + NS_ASSUME_NONNULL_END #endif //RollbarDestination_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDeveloperOptions.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDeveloperOptions.h index 0ecff82b..9bd7aa9b 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDeveloperOptions.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarDeveloperOptions.h @@ -11,43 +11,103 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - properties /// Rollbar operation enabled flag -@property (nonatomic) BOOL enabled; +@property (nonatomic, readonly) BOOL enabled; /// Enables/disables actual transmission of the payloads -@property (nonatomic) BOOL transmit; +@property (nonatomic, readonly) BOOL transmit; /// A flag to suppress internal SDK's informational logging -@property (nonatomic) BOOL suppressSdkInfoLogging; +@property (nonatomic, readonly) BOOL suppressSdkInfoLogging; -/// Flags if the processed payloads to be logged locally -@property (nonatomic) BOOL logPayload; +/// Flags if the incoming payloads to be logged locally +@property (nonatomic, readonly) BOOL logIncomingPayloads; -/// Log file to use for local logged payloads -@property (nonatomic, copy) NSString *payloadLogFile; +/// Flags if the transmitted payloads to be logged locally +@property (nonatomic, readonly) BOOL logTransmittedPayloads; + +/// Flags if the dropped payloads to be logged locally +@property (nonatomic, readonly) BOOL logDroppedPayloads; + +/// Log file to use for local logged incoming payloads +@property (nonatomic, readonly, copy) NSString *incomingPayloadsLogFile; + +/// Log file to use for local logged transmitted payloads +@property (nonatomic, readonly, copy) NSString *transmittedPayloadsLogFile; + +/// Log file to use for local logged dropped payloads +@property (nonatomic, readonly, copy) NSString *droppedPayloadsLogFile; #pragma mark - initializers /// Initializer /// @param enabled enabled flag /// @param transmit payloads transmission flag -/// @param logPayload local payloads logging flag -/// @param logPayloadFile pocal payloads logging file +/// @param logIncomingPayloads flag to log incoming payloads locally +/// @param logTransmittedPayloads flag to log transmitted payloads locally +/// @param logDroppedPayloads flag to log dropped payloads locally +/// @param logIncomingPayloadsFile file to log incoming payloads to +/// @param logTransmittedPayloadsFile file to log transmitted payloads to +/// @param logDroppedPayloadsFile file to log dropped payloads to - (instancetype)initWithEnabled:(BOOL)enabled transmit:(BOOL)transmit - logPayload:(BOOL)logPayload - payloadLogFile:(NSString *)logPayloadFile; + logIncomingPayloads:(BOOL)logIncomingPayloads + logTransmittedPayloads:(BOOL)logTransmittedPayloads + logDroppedPayloads:(BOOL)logDroppedPayloads + incomingPayloadsLogFile:(NSString *)logIncomingPayloadsFile + transmittedPayloadsLogFile:(NSString *)logTransmittedPayloadsFile + droppedPayloadsLogFile:(NSString *)logDroppedPayloadsFile; /// Initializer /// @param enabled enabled flag /// @param transmit payloads transmission flag -/// @param logPayload local payloads logging flag +/// @param logIncomingPayloads flag to log incoming payloads locally +/// @param logTransmittedPayloads flag to log transmitted payloads locally +/// @param logDroppedPayloads flag to log dropped payloads locally - (instancetype)initWithEnabled:(BOOL)enabled transmit:(BOOL)transmit - logPayload:(BOOL)logPayload; + logIncomingPayloads:(BOOL)logIncomingPayloads + logTransmittedPayloads:(BOOL)logTransmittedPayloads + logDroppedPayloads:(BOOL)logDroppedPayloads; + +/// Initializer +/// @param enabled flag enabling/suspending the SDK operation - (instancetype)initWithEnabled:(BOOL)enabled; @end +@interface RollbarMutableDeveloperOptions : RollbarDeveloperOptions + +#pragma mark - properties + +/// Rollbar operation enabled flag +@property (nonatomic, readwrite) BOOL enabled; + +/// Enables/disables actual transmission of the payloads +@property (nonatomic, readwrite) BOOL transmit; + +/// A flag to suppress internal SDK's informational logging +@property (nonatomic, readwrite) BOOL suppressSdkInfoLogging; + +/// Flags if the incoming payloads to be logged locally +@property (nonatomic, readwrite) BOOL logIncomingPayloads; + +/// Flags if the transmitted payloads to be logged locally +@property (nonatomic, readwrite) BOOL logTransmittedPayloads; + +/// Flags if the dropped payloads to be logged locally +@property (nonatomic, readwrite) BOOL logDroppedPayloads; + +/// Log file to use for local logged incoming payloads +@property (nonatomic, readwrite, copy) NSString *incomingPayloadsLogFile; + +/// Log file to use for local logged transmitted payloads +@property (nonatomic, readwrite, copy) NSString *transmittedPayloadsLogFile; + +/// Log file to use for local logged dropped payloads +@property (nonatomic, readwrite, copy) NSString *droppedPayloadsLogFile; + +@end + NS_ASSUME_NONNULL_END #endif //RollbarDeveloperOptions_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarInfrastructure.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarInfrastructure.h new file mode 100644 index 00000000..c464b56f --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarInfrastructure.h @@ -0,0 +1,65 @@ +// +// RollbarInfrastructure.h +// +// +// Created by Andrey Kornich on 2022-06-09. +// + +#ifndef RollbarInfrastructure_h +#define RollbarInfrastructure_h + +#import + +#import "RollbarLoggerProtocol.h" +#import "RollbarConfig.h" +#import "RollbarCrashCollectorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarInfrastructure : NSObject + +#pragma mark - propeties + +@property(readonly, nonnull) RollbarConfig *configuration; +@property(readonly, nonnull) id logger; + +#pragma mark - instance methods + +- (nonnull instancetype)configureWith:(nonnull RollbarConfig *)config; +- (nonnull instancetype)configureWith:(nonnull RollbarConfig *)config + andCrashCollector:(nullable id)crashCollector; +- (nonnull id)createLogger; +- (nonnull id)createLoggerWithConfig:(nonnull RollbarConfig *)config; +- (nonnull id)createLoggerWithAccessToken:(nonnull NSString *)token + andEnvironment:(nonnull NSString *)env; +- (nonnull id)createLoggerWithAccessToken:(nonnull NSString *)token; + +#pragma mark - class methods + +//+ (nonnull id)sharedLogger; +//+ (nonnull id)newLogger; +//+ (nonnull id)newLoggerWithConfig:(nonnull RollbarConfig *)config; +//+ (nonnull id)newLoggerWithAccessToken:(nonnull NSString *)token +// andEnvironment:(nonnull NSString *)env; +//+ (nonnull id)newLoggerWithAccessToken:(nonnull NSString *)token; + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance; + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; +- (void)dealloc NS_UNAVAILABLE; +- (id)copy NS_UNAVAILABLE; +- (id)mutableCopy NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif //RollbarInfrastructure_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger+Test.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger+Test.h index 02768c68..79e84b75 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger+Test.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger+Test.h @@ -14,21 +14,21 @@ NS_ASSUME_NONNULL_BEGIN ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. + (void)flushRollbarThread; -/// Reads all the payloads queued up in the persistent store +/// Reads all payloads from the default incoming payloads log ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. -+ (nonnull NSArray *)readLogItemsFromStore; ++ (nonnull NSArray *)readPayloadsFromSdkIncomingLog; -/// Reads all payloads from the default payloads log +/// Reads all payloads from the default transmitted payloads log ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. -+ (nonnull NSArray *)readPayloadsFromSdkLog; ++ (nonnull NSArray *)readPayloadsFromSdkTransmittedLog; -/// Reads all payloads from the provided file path +/// Reads all payloads from the default dropped payloads log ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. -+ (nonnull NSArray *)readPayloadsDataFromFile:(nonnull NSString *)filePath; ++ (nonnull NSArray *)readPayloadsFromSdkDroppedLog; -/// Clears all the payloads queued up in the persistent store +/// Reads all payloads from the provided file path ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. -+ (void)clearLogItemsStore; ++ (nonnull NSArray *)readPayloadsDataFromFile:(nonnull NSString *)filePath; /// Clears all the SDK persisted data ///@note THIS METHOD IS TO BE USED FOR TESTING PURPOSES ONLY. diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger.h index c724ddb1..3b48e453 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLogger.h @@ -1,103 +1,86 @@ #ifndef RollbarLogger_h #define RollbarLogger_h +#import "RollbarLoggerProtocol.h" @import Foundation; -@class RollbarConfig; - -#import "RollbarLevel.h" - NS_ASSUME_NONNULL_BEGIN /// Models interface of a Rollbar logger -@interface RollbarLogger : NSObject +@interface RollbarLogger : NSObject -/// The shared instance of the logger -+ (nonnull RollbarLogger *)sharedInstance; -/// Creates new instance of the logger using provided configuration instance -/// @param config configuration to be used by the new instance of the logger -+ (nonnull RollbarLogger *)createLoggerWithConfig:(nonnull RollbarConfig *)config; +#pragma mark - //TODO: to be removed -/// Notifier's config object -@property (nullable, atomic, strong) RollbarConfig *configuration; +- (void)updateConfiguration:(nonnull RollbarConfig *)configuration; +//- (void)updateAccessToken:(nonnull NSString *)accessToken; +//- (void)updateReportingRate:(NSUInteger)maximumReportsPerMinute; -/// Disallowed initializer -- (instancetype)init -NS_UNAVAILABLE; +//- (BOOL)sendItem:(nonnull NSDictionary *)payload +// nextOffset:(NSUInteger)nextOffset; +//- (BOOL)sendPayload:(nonnull NSData *)payload; -/// Designated notifier initializer -/// @param accessToken the access token -- (instancetype)initWithAccessToken:(nonnull NSString *)accessToken; -/// Designated notifier initializer -/// @param configuration the config object + +#pragma mark - factory methods + ++ (instancetype)loggerWithAccessToken:(nonnull NSString *)accessToken; ++ (instancetype)loggerWithAccessToken:(nonnull NSString *)accessToken + andEnvironment:(nonnull NSString *)environment; ++ (instancetype)loggerWithConfiguration:(nonnull RollbarConfig *)configuration; + +#pragma mark - initializers + +- (instancetype)initWithAccessToken:(nonnull NSString *)accessToken; +- (instancetype)initWithAccessToken:(nonnull NSString *)accessToken + andEnvironment:(nonnull NSString *)environment; - (instancetype)initWithConfiguration:(nonnull RollbarConfig *)configuration NS_DESIGNATED_INITIALIZER; -/// Processes persisted payloads -- (void)processSavedItems; - -/// Captures a crash report -/// @param crashReport the crash report -- (void)logCrashReport:(nonnull NSString *)crashReport; - -/// Captures a log entry -/// @param level Rollbar error/log level -/// @param message message -/// @param data extra data -/// @param context extra context -- (void)log:(RollbarLevel)level - message:(nonnull NSString *)message - data:(nullable NSDictionary *)data - context:(nullable NSString *)context; - -/// Captures a log entry -/// @param level Rollbar error/log level -/// @param exception exception -/// @param data extra data -/// @param context extra context -- (void)log:(RollbarLevel)level - exception:(nonnull NSException *)exception - data:(nullable NSDictionary *)data - context:(nullable NSString *)context; - -/// Capture a log entry based on an NSError -/// @param level Rollbar error/log level -/// @param error an NSError -/// @param data extra data -/// @param context extra context -- (void)log:(RollbarLevel)level - error:(nonnull NSError *)error - data:(nullable NSDictionary *)data - context:(nullable NSString *)context; - -/// Sends an item batch in a blocking manner. -/// @param payload an item to send -/// @param nextOffset the offset in the item queue file of the item immediately after this batch. -/// If the send is successful or the retry limit is hit, nextOffset will be saved to the queueState as the offset to use for the next batch -/// @return YES if this batch should be discarded if it was successful or a retry limit was hit. Otherwise NO is returned if this batch should be retried. -- (BOOL)sendItem:(nonnull NSDictionary *)payload - nextOffset:(NSUInteger)nextOffset; - - -/// Sends a fully composed JSON payload. -/// @param payload complete Rollbar payload as JSON string -/// @return YES if successful. NO if not. -- (BOOL)sendPayload:(nonnull NSData *)payload; - -/// Updates key configuration elements -/// @param configuration the Rollbar configuration object -- (void)updateConfiguration:(nonnull RollbarConfig *)configuration; -// isRoot:(BOOL)isRoot; - -/// Updates the Rollbar project access token -/// @param accessToken the Rollbar project access token -- (void)updateAccessToken:(nonnull NSString *)accessToken; +/// Disallowed initializer +- (instancetype)init +NS_UNAVAILABLE; -/// Updates allowed reporting rate -/// @param maximumReportsPerMinute the maximum allowed reports transmission rate -- (void)updateReportingRate:(NSUInteger)maximumReportsPerMinute; +//#pragma mark - properties +// +///// Notifier's config object +//@property (nullable, atomic, strong) RollbarConfig *configuration; +// +//#pragma mark - logging methods +// +///// Captures a crash report +///// @param crashReport the crash report +//- (void)logCrashReport:(nonnull NSString *)crashReport; +// +///// Captures a log entry +///// @param level Rollbar error/log level +///// @param message message +///// @param data extra data +///// @param context extra context +//- (void)log:(RollbarLevel)level +// message:(nonnull NSString *)message +// data:(nullable NSDictionary *)data +// context:(nullable NSString *)context; +// +///// Captures a log entry +///// @param level Rollbar error/log level +///// @param exception exception +///// @param data extra data +///// @param context extra context +//- (void)log:(RollbarLevel)level +// exception:(nonnull NSException *)exception +// data:(nullable NSDictionary *)data +// context:(nullable NSString *)context; +// +///// Capture a log entry based on an NSError +///// @param level Rollbar error/log level +///// @param error an NSError +///// @param data extra data +///// @param context extra context +//- (void)log:(RollbarLevel)level +// error:(nonnull NSError *)error +// data:(nullable NSDictionary *)data +// context:(nullable NSString *)context; @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggerProtocol.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggerProtocol.h new file mode 100644 index 00000000..dea3969f --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggerProtocol.h @@ -0,0 +1,61 @@ +// +// RollbarLoggerProtocol.h +// +// +// Created by Andrey Kornich on 2022-07-06. +// + +#ifndef RollbarLoggerProtocol_h +#define RollbarLoggerProtocol_h + +#import + +#import "RollbarLevel.h" +#import "RollbarConfig.h" + +@protocol RollbarLogger + +#pragma mark - properties + +/// Notifier's config object +@property (nullable, atomic, strong) RollbarConfig *configuration; + +#pragma mark - logging methods + +/// Captures a crash report +/// @param crashReport the crash report +- (void)logCrashReport:(nonnull NSString *)crashReport; + +/// Captures a log entry +/// @param level Rollbar error/log level +/// @param message message +/// @param data extra data +/// @param context extra context +- (void)log:(RollbarLevel)level + message:(nonnull NSString *)message + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +/// Captures a log entry +/// @param level Rollbar error/log level +/// @param exception exception +/// @param data extra data +/// @param context extra context +- (void)log:(RollbarLevel)level + exception:(nonnull NSException *)exception + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +/// Capture a log entry based on an NSError +/// @param level Rollbar error/log level +/// @param error an NSError +/// @param data extra data +/// @param context extra context +- (void)log:(RollbarLevel)level + error:(nonnull NSError *)error + data:(nullable NSDictionary *)data + context:(nullable NSString *)context; + +@end + +#endif /* RollbarLoggerProtocol_h */ diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggingOptions.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggingOptions.h index 3094129c..bddecb36 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggingOptions.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarLoggingOptions.h @@ -13,28 +13,28 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Properties /// A minimum threshold level to log from -@property (nonatomic) RollbarLevel logLevel; +@property (nonatomic, readonly) RollbarLevel logLevel; /// A log level to use for crsh reports -@property (nonatomic) RollbarLevel crashLevel; +@property (nonatomic, readonly) RollbarLevel crashLevel; /// Reporting rate limit -@property (nonatomic) NSUInteger maximumReportsPerMinute; +@property (nonatomic, readonly) NSUInteger maximumReportsPerMinute; /// A way of capturing IP addresses -@property (nonatomic) RollbarCaptureIpType captureIp; +@property (nonatomic, readonly) RollbarCaptureIpType captureIp; /// A code version to mark payloads with -@property (nonatomic, copy, nullable) NSString *codeVersion; +@property (nonatomic, copy, nullable, readonly) NSString *codeVersion; /// A framework tag to mark payloads with -@property (nonatomic, copy, nullable) NSString *framework; +@property (nonatomic, copy, nullable, readonly, readonly) NSString *framework; /// A request ID to mark payloads with -@property (nonatomic, copy, nullable) NSString *requestId; +@property (nonatomic, copy, nullable, readonly) NSString *requestId; /// A flag enabling potential OOM (Out-of-Memory) detection -@property (nonatomic) BOOL enableOomDetection; +@property (nonatomic, readonly) BOOL enableOomDetection; #pragma mark - Initializers @@ -129,6 +129,41 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface RollbarMutableLoggingOptions : RollbarLoggingOptions + +#pragma mark - initializers + +- (instancetype)init +NS_DESIGNATED_INITIALIZER; + +#pragma mark - Properties + +/// A minimum threshold level to log from +@property (nonatomic, readwrite) RollbarLevel logLevel; + +/// A log level to use for crsh reports +@property (nonatomic, readwrite) RollbarLevel crashLevel; + +/// Reporting rate limit +@property (nonatomic, readwrite) NSUInteger maximumReportsPerMinute; + +/// A way of capturing IP addresses +@property (nonatomic, readwrite) RollbarCaptureIpType captureIp; + +/// A code version to mark payloads with +@property (nonatomic, copy, nullable, readwrite) NSString *codeVersion; + +/// A framework tag to mark payloads with +@property (nonatomic, copy, nullable, readwrite) NSString *framework; + +/// A request ID to mark payloads with +@property (nonatomic, copy, nullable, readwrite) NSString *requestId; + +/// A flag enabling potential OOM (Out-of-Memory) detection +@property (nonatomic, readwrite) BOOL enableOomDetection; + +@end + NS_ASSUME_NONNULL_END #endif //RollbarLoggingOptions_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarModule.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarModule.h index fe400e9d..77d42b81 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarModule.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarModule.h @@ -8,16 +8,6 @@ NS_ASSUME_NONNULL_BEGIN /// Module element of a payload @interface RollbarModule : RollbarDTO -#pragma mark - properties - -/// Optional: name -/// Name of the library -@property (nonatomic, copy, nullable) NSString *name; - -/// Optional: version -/// Library version string -@property (nonatomic, copy, nullable) NSString *version; - #pragma mark - initializers /// Initializer @@ -30,6 +20,42 @@ NS_ASSUME_NONNULL_BEGIN /// @param name module name - (instancetype)initWithName:(nullable NSString *)name; +- (instancetype)init +NS_UNAVAILABLE; + +- (instancetype)new +NS_UNAVAILABLE; + +#pragma mark - properties + +/// Optional: name +/// Name of the library +@property (nonatomic, copy, nullable, readonly) NSString *name; + +/// Optional: version +/// Library version string +@property (nonatomic, copy, nullable, readonly) NSString *version; + +@end + + +/// Mutable Module element of a payload +@interface RollbarMutableModule : RollbarModule + +#pragma mark - initializers + +- (instancetype)init; + +#pragma mark - properties + +/// Optional: name +/// Name of the library +@property (nonatomic, copy, nullable, readwrite) NSString *name; + +/// Optional: version +/// Library version string +@property (nonatomic, copy, nullable, readwrite) NSString *version; + @end NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarPerson.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarPerson.h index 381b77c3..98af25c1 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarPerson.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarPerson.h @@ -9,27 +9,6 @@ NS_ASSUME_NONNULL_BEGIN /// Models a monitored system user. @interface RollbarPerson : RollbarDTO -#pragma mark - properties - -/// Required: id. -/// -/// A string up to 40 characters identifying this user in your system. -/// -/// The user affected by this event. Will be indexed by ID, username, and email. -/// People are stored in Rollbar keyed by ID. If you send a multiple different usernames/emails for the -/// same ID, the last received values will overwrite earlier ones. -@property (nonatomic, copy, nonnull) NSString *ID; - -/// Optional: username. -/// -/// A string up to 255 characters -@property (nonatomic, copy, nullable) NSString *username; - -/// Optional: email. -/// -/// A string up to 255 characters -@property (nonatomic, copy, nullable) NSString *email; - #pragma mark - initializers /// Initialiazes a RollbarPerson. @@ -59,6 +38,63 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; +- (instancetype)new +NS_UNAVAILABLE; + +#pragma mark - properties + +/// Required: id. +/// +/// A string up to 40 characters identifying this user in your system. +/// +/// The user affected by this event. Will be indexed by ID, username, and email. +/// People are stored in Rollbar keyed by ID. If you send a multiple different usernames/emails for the +/// same ID, the last received values will overwrite earlier ones. +@property (nonatomic, copy, nonnull, readonly) NSString *ID; + +/// Optional: username. +/// +/// A string up to 255 characters +@property (nonatomic, copy, nullable, readonly) NSString *username; + +/// Optional: email. +/// +/// A string up to 255 characters +@property (nonatomic, copy, nullable, readonly) NSString *email; + +@end + + +/// Mutable RollbarPerson DTO. +/// Models a monitored system user. +@interface RollbarMutablePerson : RollbarPerson + +#pragma mark - initializers + +/// Hides perameterless initializer. +- (instancetype)init; + +#pragma mark - properties + +/// Required: id. +/// +/// A string up to 40 characters identifying this user in your system. +/// +/// The user affected by this event. Will be indexed by ID, username, and email. +/// People are stored in Rollbar keyed by ID. If you send a multiple different usernames/emails for the +/// same ID, the last received values will overwrite earlier ones. +@property (nonatomic, copy, nonnull, readwrite) NSString *ID; + +/// Optional: username. +/// +/// A string up to 255 characters +@property (nonatomic, copy, nullable, readwrite) NSString *username; + +/// Optional: email. +/// +/// A string up to 255 characters +@property (nonatomic, copy, nullable, readwrite) NSString *email; + @end NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarProxy.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarProxy.h index 0ee782a1..39de2097 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarProxy.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarProxy.h @@ -11,13 +11,13 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - properties /// Enables/disables usage of these proxy settings -@property (nonatomic) BOOL enabled; +@property (nonatomic, readonly) BOOL enabled; /// Proxy URI to use -@property (nonatomic, copy) NSString *proxyUrl; +@property (nonatomic, readonly, copy) NSString *proxyUrl; /// Proxy port to use -@property (nonatomic) NSUInteger proxyPort; +@property (nonatomic, readonly) NSUInteger proxyPort; #pragma mark - initializers @@ -31,6 +31,21 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface RollbarMutableProxy : RollbarProxy + +#pragma mark - properties + +/// Enables/disables usage of these proxy settings +@property (nonatomic, readwrite) BOOL enabled; + +/// Proxy URI to use +@property (nonatomic, readwrite, copy) NSString *proxyUrl; + +/// Proxy port to use +@property (nonatomic, readwrite) NSUInteger proxyPort; + +@end + NS_ASSUME_NONNULL_END #endif //RollbarProxy_h diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarScrubbingOptions.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarScrubbingOptions.h index c67f28b2..5558d6d2 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarScrubbingOptions.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarScrubbingOptions.h @@ -8,13 +8,60 @@ NS_ASSUME_NONNULL_BEGIN /// Scrubbing setting of a configuration @interface RollbarScrubbingOptions : RollbarDTO +#pragma mark - initializers + +/// Initializer +/// @param enabled scrubbing enabling flag +/// @param scrubFields scrubbing fields +/// @param safeListFields safe list of fields (to never scrub) +- (instancetype)initWithEnabled:(BOOL)enabled + scrubFields:(NSArray *)scrubFields + safeListFields:(NSArray *)safeListFields; + +/// Initializer +/// @param scrubFields scrubbing fields +/// @param safeListFields safe list of fields (to never scrub) +- (instancetype)initWithScrubFields:(NSArray *)scrubFields + safeListFields:(NSArray *)safeListFields; + +/// Initializer +/// @param scrubFields scrubbing fields +- (instancetype)initWithScrubFields:(NSArray *)scrubFields; + +#pragma mark - properties + +/// Enables scrubbing +@property (nonatomic, readonly) BOOL enabled; + +/// Fields to scrub from the payload +@property (nonatomic, nonnull, readonly, strong) NSArray *scrubFields; + +/// Fields to not scrub from the payload even if they mention among scrubFields +@property (nonatomic, nonnull, readonly, strong) NSArray *safeListFields; + +@end + + +/// Mutable scrubbing setting of a configuration +@interface RollbarMutableScrubbingOptions : RollbarScrubbingOptions + +#pragma mark - initializers + +- (instancetype)init +NS_DESIGNATED_INITIALIZER; + #pragma mark - properties /// Enables scrubbing -@property (nonatomic) BOOL enabled; +@property (nonatomic, readwrite) BOOL enabled; /// Fields to scrub from the payload -@property (nonnull, nonatomic, strong) NSArray *scrubFields; +@property (nonatomic, nonnull, readwrite, strong) NSMutableArray *scrubFields; + +/// Fields to not scrub from the payload even if they mention among scrubFields +@property (nonatomic, nonnull, readwrite, strong) NSMutableArray *safeListFields; + +#pragma mark - methods /// Adds a scrubbing field to use /// @param field a scrubbing field @@ -24,9 +71,6 @@ NS_ASSUME_NONNULL_BEGIN /// @param field a scrubbing field - (void)removeScrubField:(NSString *)field; -/// Fields to not scrub from the payload even if they mention among scrubFields -@property (nonnull, nonatomic, strong) NSArray *safeListFields; - /// Adds a scrubbing field to the safe list /// @param field a scrubbing field - (void)addScrubSafeListField:(NSString *)field; @@ -35,26 +79,6 @@ NS_ASSUME_NONNULL_BEGIN /// @param field a scrubbing field - (void)removeScrubSafeListField:(NSString *)field; -#pragma mark - initializers - -/// Initializer -/// @param enabled scrubbing enabling flag -/// @param scrubFields scrubbing fields -/// @param safeListFields safe list of fields (to never scrub) -- (instancetype)initWithEnabled:(BOOL)enabled - scrubFields:(NSArray *)scrubFields - safeListFields:(NSArray *)safeListFields; - -/// Initializer -/// @param scrubFields scrubbing fields -/// @param safeListFields safe list of fields (to never scrub) -- (instancetype)initWithScrubFields:(NSArray *)scrubFields - safeListFields:(NSArray *)safeListFields; - -/// Initializer -/// @param scrubFields scrubbing fields -- (instancetype)initWithScrubFields:(NSArray *)scrubFields; - @end NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarServerConfig.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarServerConfig.h index d981ef5b..5f512fab 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarServerConfig.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarServerConfig.h @@ -8,20 +8,6 @@ NS_ASSUME_NONNULL_BEGIN /// Server config setting of a configuration @interface RollbarServerConfig : RollbarDTO -#pragma mark - properties - -/// Servef host -@property (nonatomic, copy, nullable) NSString *host; - -/// Server code root -@property (nonatomic, copy, nullable) NSString *root; - -/// Server code branch -@property (nonatomic, copy, nullable) NSString *branch; - -/// Server code version -@property (nonatomic, copy, nullable) NSString *codeVersion; - #pragma mark - initializers /// Initializer @@ -34,6 +20,40 @@ NS_ASSUME_NONNULL_BEGIN branch:(nullable NSString *)branch codeVersion:(nullable NSString *)codeVersion; +#pragma mark - properties + +/// Servef host +@property (nonatomic, copy, nullable, readonly) NSString *host; + +/// Server code root +@property (nonatomic, copy, nullable, readonly) NSString *root; + +/// Server code branch, readonly +@property (nonatomic, copy, nullable, readonly) NSString *branch; + +/// Server code version, readonly +@property (nonatomic, copy, nullable, readonly) NSString *codeVersion; + +@end + + +/// Mutable Server config setting of a configuration +@interface RollbarMutableServerConfig : RollbarServerConfig + +#pragma mark - properties + +/// Servef host +@property (nonatomic, copy, nullable, readwrite) NSString *host; + +/// Server code root +@property (nonatomic, copy, nullable, readwrite) NSString *root; + +/// Server code branch +@property (nonatomic, copy, nullable, readwrite) NSString *branch; + +/// Server code version +@property (nonatomic, copy, nullable, readwrite) NSString *codeVersion; + @end NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h index 2c51f4e8..c89320b2 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h @@ -52,6 +52,8 @@ /// @param telemetryOptions desired Telemetry options - (nonnull instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions; +@property (readonly, nonnull) RollbarTelemetryOptions *telemetryOptions; + /// Telemetry collection enable/disable switch @property (readwrite, atomic) BOOL enabled; diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h index 63b8d7d8..edc866b8 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h @@ -1,35 +1,16 @@ #ifndef RollbarTelemetryOptions_h #define RollbarTelemetryOptions_h +#import "RollbarScrubbingOptions.h" + @import RollbarCommon; -@class RollbarScrubbingOptions; NS_ASSUME_NONNULL_BEGIN /// Telemetry related settings of a configuration @interface RollbarTelemetryOptions : RollbarDTO -#pragma mark - properties -/// Telemetry enabled flag -@property (nonatomic) BOOL enabled; - -/// Capture OS log as Telemetry events flag -@property (nonatomic) BOOL captureLog; - -/// Capture connectivity flag -@property (nonatomic) BOOL captureConnectivity; - -/// Telemtry events buffer limit -@property (nonatomic) NSUInteger maximumTelemetryData; - -/// Telemetry scrubbing options -@property (nonatomic, strong) RollbarScrubbingOptions *viewInputsScrubber; - -/// Time interval for auto-collecting memtory stats -/// @note 0.0 means no collection! -@property (atomic) NSTimeInterval memoryStatsAutocollectionInterval; //[sec] - #pragma mark - initializers /// Initializer @@ -54,6 +35,57 @@ NS_ASSUME_NONNULL_BEGIN /// @param enabled telemetry enabled - (instancetype)initWithEnabled:(BOOL)enabled; +#pragma mark - properties +/// Telemetry enabled flag +@property (nonatomic, readonly) BOOL enabled; + +/// Capture OS log as Telemetry events flag +@property (nonatomic, readonly) BOOL captureLog; + +/// Capture connectivity flag +@property (nonatomic, readonly) BOOL captureConnectivity; + +/// Telemtry events buffer limit +@property (nonatomic, readonly) NSUInteger maximumTelemetryData; + +/// Telemetry scrubbing options +@property (nonatomic, strong, readonly) RollbarScrubbingOptions *viewInputsScrubber; + +/// Time interval for auto-collecting memtory stats +/// @note 0.0 means no collection! +@property (nonatomic, readonly) NSTimeInterval memoryStatsAutocollectionInterval; //[sec] + +@end + + +/// Mutable Telemetry related settings of a configuration +@interface RollbarMutableTelemetryOptions : RollbarTelemetryOptions + +#pragma mark - initializers + +- (instancetype)init +NS_DESIGNATED_INITIALIZER; + +#pragma mark - properties +/// Telemetry enabled flag +@property (nonatomic, readwrite) BOOL enabled; + +/// Capture OS log as Telemetry events flag +@property (nonatomic, readwrite) BOOL captureLog; + +/// Capture connectivity flag +@property (nonatomic, readwrite) BOOL captureConnectivity; + +/// Telemtry events buffer limit +@property (nonatomic, readwrite) NSUInteger maximumTelemetryData; + +/// Telemetry scrubbing options +@property (nonatomic, strong, readwrite) RollbarMutableScrubbingOptions *viewInputsScrubber; + +/// Time interval for auto-collecting memtory stats +/// @note 0.0 means no collection! +@property (nonatomic, readwrite) NSTimeInterval memoryStatsAutocollectionInterval; //[sec] + @end NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/DTOsTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/DTOsTests.m index 591d79e3..619c3d5d 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/DTOsTests.m +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/DTOsTests.m @@ -149,7 +149,7 @@ -(void)testRollbarProxyDTO { } - (void)testRollbarScrubbingOptionsDTO { - RollbarScrubbingOptions *dto = [[RollbarScrubbingOptions alloc] initWithScrubFields:@[@"field1", @"field2"]]; + RollbarMutableScrubbingOptions *dto = [[RollbarMutableScrubbingOptions alloc] initWithScrubFields:@[@"field1", @"field2"]]; XCTAssertTrue(dto.enabled, @"Enabled by default" ); @@ -160,7 +160,7 @@ - (void)testRollbarScrubbingOptionsDTO { @"Has NO whitelist fields" ); - dto.safeListFields = @[@"tf1", @"tf2", @"tf3"]; + dto.safeListFields = [@[@"tf1", @"tf2", @"tf3"] mutableCopy]; XCTAssertTrue(dto.safeListFields.count == 3, @"Has some whitelist fields" ); @@ -172,11 +172,11 @@ - (void)testRollbarScrubbingOptionsDTO { } - (void)testRollbarServerConfigDTO { - RollbarServerConfig *dto = [[RollbarServerConfig alloc] initWithHost:@"HOST" - root:@"ROOT" - branch:@"BRANCH" - codeVersion:@"1.2.3" - ]; + RollbarMutableServerConfig *dto = [[RollbarMutableServerConfig alloc] initWithHost:@"HOST" + root:@"ROOT" + branch:@"BRANCH" + codeVersion:@"1.2.3" + ]; XCTAssertTrue(NSOrderedSame == [dto.host compare:@"HOST"], @"Proper host" ); @@ -208,7 +208,7 @@ - (void)testRollbarServerConfigDTO { ); - RollbarConfig *rc = [RollbarConfig new]; + RollbarMutableConfig *rc = [RollbarMutableConfig new]; rc.destination.accessToken = @"ACCESSTOKEN"; rc.destination.environment = @"ENVIRONMENT"; rc.destination.endpoint = @"ENDPOINT"; @@ -225,9 +225,9 @@ - (void)testRollbarServerConfigDTO { } - (void)testRollbarPersonDTO { - RollbarPerson *dto = [[RollbarPerson alloc] initWithID:@"ID" - username:@"USERNAME" - email:@"EMAIL" + RollbarMutablePerson *dto = [[RollbarMutablePerson alloc] initWithID:@"ID" + username:@"USERNAME" + email:@"EMAIL" ]; XCTAssertTrue(NSOrderedSame == [dto.ID compare:@"ID"], @"Proper ID" @@ -252,7 +252,7 @@ - (void)testRollbarPersonDTO { @"Proper email" ); - dto = [[RollbarPerson alloc] initWithID:@"ID007"]; + dto = [[RollbarMutablePerson alloc] initWithID:@"ID007"]; XCTAssertTrue(NSOrderedSame == [dto.ID compare:@"ID007"], @"Proper ID" ); @@ -265,9 +265,9 @@ - (void)testRollbarPersonDTO { } - (void)testRollbarModuleDTO { - RollbarModule *dto = [[RollbarModule alloc] initWithName:@"ModuleName" - version:@"v1.2.3" - ]; + RollbarMutableModule *dto = [[RollbarMutableModule alloc] initWithName:@"ModuleName" + version:@"v1.2.3" + ]; XCTAssertTrue([dto.name isEqualToString:@"ModuleName"], @"Proper name" ); @@ -284,7 +284,7 @@ - (void)testRollbarModuleDTO { @"Proper version" ); - dto = [[RollbarModule alloc] initWithName:@"Module"]; + dto = [[RollbarMutableModule alloc] initWithName:@"Module"]; XCTAssertTrue([dto.name isEqualToString:@"Module"], @"Proper name" ); @@ -298,7 +298,7 @@ - (void)testRollbarTelemetryOptionsDTO { [[RollbarScrubbingOptions alloc] initWithEnabled:YES scrubFields:@[@"one", @"two"] safeListFields:@[@"two", @"three", @"four"] - ]; + ]; RollbarTelemetryOptions *dto = [[RollbarTelemetryOptions alloc] initWithEnabled:YES captureLog:YES captureConnectivity:YES @@ -323,7 +323,7 @@ - (void)testRollbarTelemetryOptionsDTO { @"Proper view inputs scrubber white list fields count" ); - dto = [[RollbarTelemetryOptions alloc] init]; + dto = [[RollbarMutableTelemetryOptions alloc] init]; XCTAssertTrue(!dto.enabled, @"Proper enabled" ); @@ -346,9 +346,9 @@ - (void)testRollbarTelemetryOptionsDTO { } - (void)testRollbarLoggingOptionsDTO { - RollbarLoggingOptions *dto = [[RollbarLoggingOptions alloc] initWithLogLevel:RollbarLevel_Error - crashLevel:RollbarLevel_Info - maximumReportsPerMinute:45]; + RollbarMutableLoggingOptions *dto = [[RollbarMutableLoggingOptions alloc] initWithLogLevel:RollbarLevel_Error + crashLevel:RollbarLevel_Info + maximumReportsPerMinute:45]; dto.captureIp = RollbarCaptureIpType_Anonymize; dto.codeVersion = @"CODEVERSION"; dto.framework = @"FRAMEWORK"; @@ -376,7 +376,7 @@ - (void)testRollbarLoggingOptionsDTO { @"Proper request ID" ); - dto = [[RollbarLoggingOptions alloc] init]; + dto = [[RollbarMutableLoggingOptions alloc] init]; XCTAssertTrue(dto.logLevel == RollbarLevel_Info, @"Proper default log level" ); @@ -402,7 +402,7 @@ - (void)testRollbarLoggingOptionsDTO { - (void)testRollbarConfigDTO { - RollbarConfig *rc = [RollbarConfig new]; + RollbarMutableConfig *rc = [RollbarMutableConfig new]; //id destination = rc.destination; rc.destination.accessToken = @"ACCESSTOKEN"; rc.destination.environment = @"ENVIRONMENT"; @@ -413,7 +413,7 @@ - (void)testRollbarConfigDTO { [rc setServerHost:@"SERVERHOST" root:@"SERVERROOT" branch:@"SERVERBRANCH" codeVersion:@"SERVERCODEVERSION"]; [rc setNotifierName:@"NOTIFIERNAME" version:@"NOTIFIERVERSION"]; - RollbarConfig *rcClone = [[RollbarConfig alloc] initWithJSONString:[rc serializeToJSONString]]; + RollbarMutableConfig *rcClone = [[RollbarMutableConfig alloc] initWithJSONString:[rc serializeToJSONString]]; // id scrubList = rc.scrubFields; // id scrubListClone = rcClone.scrubFields; @@ -437,7 +437,7 @@ - (void)testRollbarConfigDTO { // [rcClone serializeToJSONString] // ); - rcClone = [[RollbarConfig alloc] initWithJSONString:[rc serializeToJSONString]]; + rcClone = [[RollbarMutableConfig alloc] initWithJSONString:[rc serializeToJSONString]]; rcClone.httpProxy.proxyUrl = @"SOME_OTHER_ONE"; XCTAssertTrue(![rc isEqual:rcClone], @"Two DTOs are NOT expected to be equal" diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/PayloadTruncationTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/PayloadTruncationTests.m index 735c9abc..4c783eac 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/PayloadTruncationTests.m +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/PayloadTruncationTests.m @@ -15,18 +15,10 @@ @implementation PayloadTruncationTests - (void)setUp { [super setUp]; - - [RollbarLogger clearSdkDataStore]; - - if (!Rollbar.currentConfiguration) { - [Rollbar initWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken]]; - Rollbar.currentConfiguration.destination.environment = [RollbarTestHelper getRollbarEnvironment]; - } } - (void)tearDown { - [Rollbar updateConfiguration:[RollbarConfig new]]; [super tearDown]; } @@ -181,6 +173,23 @@ - (void)testVisuallyTruncateStringToTotalBytesUnicode { - (void)testPayloadTruncation { + RollbarMutableConfig *config = + [RollbarMutableConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment]]; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.developerOptions.transmit = NO; + + [Rollbar initWithConfiguration:config]; + + [RollbarTestUtil waitWithWaitTimeInSeconds:2]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 0); + @try { NSArray *crew = [NSArray arrayWithObjects: @"Dave", @@ -193,9 +202,11 @@ - (void)testPayloadTruncation { [Rollbar errorException:exception]; } - [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:3]; - NSArray *items = [RollbarLogger readLogItemsFromStore]; + items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertNotNil(items); + XCTAssertEqual(items.count, 1); for (id payload in items) { NSMutableArray *frames = [payload mutableArrayValueForKeyPath:@"body.trace.frames"]; @@ -216,6 +227,23 @@ - (void)testPayloadTruncation { - (void)testErrorReportingWithTruncation { + RollbarMutableConfig *config = + [RollbarMutableConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment]]; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.developerOptions.transmit = YES; + + [Rollbar initWithConfiguration:config]; + + [RollbarTestUtil waitWithWaitTimeInSeconds:5]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 0); + NSMutableString *simulatedLongString = [[NSMutableString alloc] initWithCapacity:(512 + 1)*1024]; while (simulatedLongString.length < (512 * 1024)) { @@ -226,6 +254,14 @@ - (void)testErrorReportingWithTruncation { data:@{@"extra_truncatable_data": simulatedLongString} ]; + [RollbarTestUtil waitWithWaitTimeInSeconds:2]; + items = [RollbarTestUtil readIncomingPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 1); + XCTAssertTrue([@"Message with long extra data" isEqualToString:[items[0] valueForKeyPath:@"body.message.body"]]); + [RollbarTestUtil waitWithWaitTimeInSeconds:3]; + items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 1); + @try { NSArray *crew = [NSArray arrayWithObjects: @"Dave", @@ -240,13 +276,12 @@ - (void)testErrorReportingWithTruncation { data:@{@"extra_truncatable_data": simulatedLongString} ]; - [NSThread sleepForTimeInterval:1.0f]; - - // What is this doing? -// [Rollbar.currentNotifier updateReportingRate:10]; -// [Rollbar.currentNotifier updateReportingRate:60]; -// [Rollbar.currentNotifier updateReportingRate:20]; -// [Rollbar.currentNotifier updateReportingRate:60]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + items = [RollbarTestUtil readIncomingPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 2); + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 2); } } diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigTests.m new file mode 100644 index 00000000..d7e3199f --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigTests.m @@ -0,0 +1,640 @@ +// +// RollbarConfigTests.m +// +// +// Created by Andrey Kornich on 2022-07-14. +// + +#import + +@import RollbarCommon; +@import RollbarNotifier; + +@interface RollbarConfigTests : XCTestCase + +@end + +@implementation RollbarConfigTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testRollbarDestination { + + NSString *defaultMutable = [[RollbarMutableDestination new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarDestination new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableDestination *mutable = [RollbarMutableDestination new]; + mutable.endpoint = @"test_EP"; + mutable.accessToken = @"test_AC"; + mutable.environment = @"test_ENV"; + XCTAssertTrue([mutable.endpoint isEqualToString:@"test_EP"]); + XCTAssertTrue([mutable.accessToken isEqualToString:@"test_AC"]); + XCTAssertTrue([mutable.environment isEqualToString:@"test_ENV"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_EP"]); + XCTAssertTrue([content containsString:@"test_AC"]); + XCTAssertTrue([content containsString:@"test_ENV"]); + + RollbarDestination *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue([immutable.endpoint isEqualToString:@"test_EP"]); + XCTAssertTrue([immutable.accessToken isEqualToString:@"test_AC"]); + XCTAssertTrue([immutable.environment isEqualToString:@"test_ENV"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_EP"]); + XCTAssertTrue([content containsString:@"test_AC"]); + XCTAssertTrue([content containsString:@"test_ENV"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarDeveloperOptions { + + NSString *defaultMutable = [[RollbarMutableDeveloperOptions new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarDeveloperOptions new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableDeveloperOptions *mutable = [RollbarMutableDeveloperOptions new]; + mutable.enabled = NO; + mutable.transmit = NO; + mutable.suppressSdkInfoLogging = NO; + mutable.logTransmittedPayloads = NO; + mutable.transmittedPayloadsLogFile = @"test_PAYLOADS.LOG_old"; + XCTAssertFalse(mutable.enabled); + XCTAssertFalse(mutable.transmit); + XCTAssertFalse(mutable.suppressSdkInfoLogging); + XCTAssertFalse(mutable.logTransmittedPayloads); + XCTAssertTrue([mutable.transmittedPayloadsLogFile isEqualToString:@"test_PAYLOADS.LOG_old"]); + + mutable.enabled = YES; + mutable.transmit = YES; + mutable.suppressSdkInfoLogging = YES; + mutable.logTransmittedPayloads = YES; + mutable.transmittedPayloadsLogFile = @"test_PAYLOADS.LOG"; + XCTAssertTrue(mutable.enabled); + XCTAssertTrue(mutable.transmit); + XCTAssertTrue(mutable.suppressSdkInfoLogging); + XCTAssertTrue(mutable.logTransmittedPayloads); + XCTAssertTrue([mutable.transmittedPayloadsLogFile isEqualToString:@"test_PAYLOADS.LOG"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_PAYLOADS.LOG"]); + + RollbarDeveloperOptions *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue(immutable.enabled); + XCTAssertTrue(immutable.transmit); + XCTAssertTrue(immutable.suppressSdkInfoLogging); + XCTAssertTrue(immutable.logTransmittedPayloads); + XCTAssertTrue([immutable.transmittedPayloadsLogFile isEqualToString:@"test_PAYLOADS.LOG"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_PAYLOADS.LOG"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarLoggingOptions { + + NSString *defaultMutable = [[RollbarMutableLoggingOptions new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarLoggingOptions new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableLoggingOptions *mutable = [RollbarMutableLoggingOptions new]; + mutable.logLevel = RollbarLevel_Info; + mutable.crashLevel = RollbarLevel_Error; + mutable.maximumReportsPerMinute = 10; + mutable.captureIp = RollbarCaptureIpType_Anonymize; + mutable.codeVersion = @"test_CV_old"; + mutable.framework = @"test_FW_old"; + mutable.requestId = @"test_RID_old"; + mutable.enableOomDetection = NO; + XCTAssertEqual(mutable.logLevel, RollbarLevel_Info); + XCTAssertEqual(mutable.crashLevel, RollbarLevel_Error); + XCTAssertEqual(mutable.maximumReportsPerMinute, 10); + XCTAssertEqual(mutable.captureIp, RollbarCaptureIpType_Anonymize); + XCTAssertTrue([mutable.codeVersion isEqualToString:@"test_CV_old"]); + XCTAssertTrue([mutable.framework isEqualToString:@"test_FW_old"]); + XCTAssertTrue([mutable.requestId isEqualToString:@"test_RID_old"]); + XCTAssertFalse(mutable.enableOomDetection); + + mutable.logLevel = RollbarLevel_Debug; + mutable.crashLevel = RollbarLevel_Critical; + mutable.maximumReportsPerMinute = 25; + mutable.captureIp = RollbarCaptureIpType_Full; + mutable.codeVersion = @"test_CV"; + mutable.framework = @"test_FW"; + mutable.requestId = @"test_RID"; + mutable.enableOomDetection = YES; + XCTAssertEqual(mutable.logLevel, RollbarLevel_Debug); + XCTAssertEqual(mutable.crashLevel, RollbarLevel_Critical); + XCTAssertEqual(mutable.maximumReportsPerMinute, 25); + XCTAssertEqual(mutable.captureIp, RollbarCaptureIpType_Full); + XCTAssertTrue([mutable.codeVersion isEqualToString:@"test_CV"]); + XCTAssertTrue([mutable.framework isEqualToString:@"test_FW"]); + XCTAssertTrue([mutable.requestId isEqualToString:@"test_RID"]); + XCTAssertTrue(mutable.enableOomDetection); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"debug"]); + XCTAssertTrue([content containsString:@"critical"]); + XCTAssertTrue([content containsString:@"25"]); + XCTAssertTrue([content containsString:@"Full"]); + XCTAssertTrue([content containsString:@"test_CV"]); + XCTAssertTrue([content containsString:@"test_FW"]); + XCTAssertTrue([content containsString:@"test_RID"]); + + RollbarLoggingOptions *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertEqual(immutable.logLevel, RollbarLevel_Debug); + XCTAssertEqual(immutable.crashLevel, RollbarLevel_Critical); + XCTAssertEqual(immutable.maximumReportsPerMinute, 25); + XCTAssertEqual(immutable.captureIp, RollbarCaptureIpType_Full); + XCTAssertTrue([immutable.codeVersion isEqualToString:@"test_CV"]); + XCTAssertTrue([immutable.framework isEqualToString:@"test_FW"]); + XCTAssertTrue([immutable.requestId isEqualToString:@"test_RID"]); + XCTAssertTrue(immutable.enableOomDetection); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"debug"]); + XCTAssertTrue([content containsString:@"critical"]); + XCTAssertTrue([content containsString:@"25"]); + XCTAssertTrue([content containsString:@"Full"]); + XCTAssertTrue([content containsString:@"test_CV"]); + XCTAssertTrue([content containsString:@"test_FW"]); + XCTAssertTrue([content containsString:@"test_RID"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarModule { + + NSString *defaultMutable = [[RollbarMutableModule new] serializeToJSONString]; + NSString *defaultImmutable = [[[RollbarModule alloc] initWithName:nil] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableModule *mutable = [RollbarMutableModule new]; + mutable.name = @"test_N_old"; + mutable.version = @"test_V_old"; + XCTAssertTrue([mutable.name isEqualToString:@"test_N_old"]); + XCTAssertTrue([mutable.version isEqualToString:@"test_V_old"]); + + mutable.name = @"test_N"; + mutable.version = @"test_V"; + XCTAssertTrue([mutable.name isEqualToString:@"test_N"]); + XCTAssertTrue([mutable.version isEqualToString:@"test_V"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_N"]); + XCTAssertTrue([content containsString:@"test_V"]); + + RollbarModule *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue([immutable.name isEqualToString:@"test_N"]); + XCTAssertTrue([immutable.version isEqualToString:@"test_V"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_N"]); + XCTAssertTrue([content containsString:@"test_V"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarPerson { + + NSString *defaultMutable = [[[RollbarMutablePerson alloc] initWithID:@"ID"] serializeToJSONString]; + NSString *defaultImmutable = [[[RollbarPerson alloc] initWithID:@"ID"] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutablePerson *mutable = [RollbarMutablePerson new]; + mutable.ID = @"test_ID_old"; + mutable.username = @"test_UN_old"; + mutable.email = @"test_EM_old"; + XCTAssertTrue([mutable.ID isEqualToString:@"test_ID_old"]); + XCTAssertTrue([mutable.username isEqualToString:@"test_UN_old"]); + XCTAssertTrue([mutable.email isEqualToString:@"test_EM_old"]); + + mutable.ID = @"test_ID"; + mutable.username = @"test_UN"; + mutable.email = @"test_EM"; + XCTAssertTrue([mutable.ID isEqualToString:@"test_ID"]); + XCTAssertTrue([mutable.username isEqualToString:@"test_UN"]); + XCTAssertTrue([mutable.email isEqualToString:@"test_EM"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_ID"]); + XCTAssertTrue([content containsString:@"test_UN"]); + XCTAssertTrue([content containsString:@"test_EM"]); + + RollbarPerson *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue([immutable.ID isEqualToString:@"test_ID"]); + XCTAssertTrue([immutable.username isEqualToString:@"test_UN"]); + XCTAssertTrue([immutable.email isEqualToString:@"test_EM"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_ID"]); + XCTAssertTrue([content containsString:@"test_UN"]); + XCTAssertTrue([content containsString:@"test_EM"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarProxy { + + NSString *defaultMutable = [[RollbarMutableProxy new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarProxy new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableProxy *mutable = [RollbarMutableProxy new]; + mutable.enabled = NO; + mutable.proxyUrl = @"test_URL_old"; + mutable.proxyPort = 100; + XCTAssertTrue(!mutable.enabled); + XCTAssertTrue([mutable.proxyUrl isEqualToString:@"test_URL_old"]); + XCTAssertEqual(mutable.proxyPort, 100); + + mutable.enabled = YES; + mutable.proxyUrl = @"test_URL"; + mutable.proxyPort = 199; + XCTAssertTrue(mutable.enabled); + XCTAssertTrue([mutable.proxyUrl isEqualToString:@"test_URL"]); + XCTAssertEqual(mutable.proxyPort, 199); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_URL"]); + XCTAssertTrue([content containsString:@"199"]); + + RollbarProxy *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue(immutable.enabled); + XCTAssertTrue([immutable.proxyUrl isEqualToString:@"test_URL"]); + XCTAssertEqual(immutable.proxyPort, 199); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_URL"]); + XCTAssertTrue([content containsString:@"199"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarServerConfig { + + NSString *defaultMutable = [[RollbarMutableServerConfig new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarServerConfig new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableServerConfig *mutable = [RollbarMutableServerConfig new]; + mutable.host = @"test_H_old"; + mutable.root = @"test_R_old"; + mutable.branch = @"test_B_old"; + mutable.codeVersion = @"test_CV_old"; + XCTAssertTrue([mutable.host isEqualToString:@"test_H_old"]); + XCTAssertTrue([mutable.root isEqualToString:@"test_R_old"]); + XCTAssertTrue([mutable.branch isEqualToString:@"test_B_old"]); + XCTAssertTrue([mutable.codeVersion isEqualToString:@"test_CV_old"]); + + mutable.host = @"test_H"; + mutable.root = @"test_R"; + mutable.branch = @"test_B"; + mutable.codeVersion = @"test_CV"; + XCTAssertTrue([mutable.host isEqualToString:@"test_H"]); + XCTAssertTrue([mutable.root isEqualToString:@"test_R"]); + XCTAssertTrue([mutable.branch isEqualToString:@"test_B"]); + XCTAssertTrue([mutable.codeVersion isEqualToString:@"test_CV"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_H"]); + XCTAssertTrue([content containsString:@"test_R"]); + XCTAssertTrue([content containsString:@"test_B"]); + XCTAssertTrue([content containsString:@"test_CV"]); + + RollbarServerConfig *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue([immutable.host isEqualToString:@"test_H"]); + XCTAssertTrue([immutable.root isEqualToString:@"test_R"]); + XCTAssertTrue([immutable.branch isEqualToString:@"test_B"]); + XCTAssertTrue([immutable.codeVersion isEqualToString:@"test_CV"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_H"]); + XCTAssertTrue([content containsString:@"test_R"]); + XCTAssertTrue([content containsString:@"test_B"]); + XCTAssertTrue([content containsString:@"test_CV"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarScrubbingOptions { + + NSString *defaultMutable = [[RollbarMutableScrubbingOptions new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarScrubbingOptions new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableScrubbingOptions *mutable = [RollbarMutableScrubbingOptions new]; + mutable.enabled = NO; + mutable.scrubFields = [@[@"F1"] mutableCopy]; + mutable.safeListFields = [@[@"S1"] mutableCopy]; + XCTAssertTrue(!mutable.enabled); + XCTAssertTrue([mutable.scrubFields containsObject:@"F1"]); + XCTAssertTrue(![mutable.scrubFields containsObject:@"F2"]); + XCTAssertTrue([mutable.safeListFields containsObject:@"S1"]); + XCTAssertTrue(![mutable.safeListFields containsObject:@"S2"]); + + mutable.enabled = YES; + [mutable addScrubField:@"F2"]; + [mutable addScrubSafeListField:@"S2"]; + XCTAssertTrue(mutable.enabled); + XCTAssertTrue([mutable.scrubFields containsObject:@"F1"]); + XCTAssertTrue([mutable.scrubFields containsObject:@"F2"]); + XCTAssertTrue([mutable.safeListFields containsObject:@"S1"]); + XCTAssertTrue([mutable.safeListFields containsObject:@"S2"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"F1"]); + XCTAssertTrue([content containsString:@"F2"]); + XCTAssertTrue([content containsString:@"S1"]); + XCTAssertTrue([content containsString:@"S2"]); + + RollbarScrubbingOptions *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue(immutable.enabled); + XCTAssertTrue([immutable.scrubFields containsObject:@"F1"]); + XCTAssertTrue([immutable.scrubFields containsObject:@"F2"]); + XCTAssertTrue([immutable.safeListFields containsObject:@"S1"]); + XCTAssertTrue([immutable.safeListFields containsObject:@"S2"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"F1"]); + XCTAssertTrue([content containsString:@"F2"]); + XCTAssertTrue([content containsString:@"S1"]); + XCTAssertTrue([content containsString:@"S2"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarTelemetryOptions { + + NSString *defaultMutable = [[RollbarMutableTelemetryOptions new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarTelemetryOptions new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableTelemetryOptions *mutable = [RollbarMutableTelemetryOptions new]; + mutable.enabled = NO; + mutable.captureLog = NO; + mutable.captureConnectivity = NO; + mutable.maximumTelemetryData = 10; + mutable.memoryStatsAutocollectionInterval = 11; + [mutable.viewInputsScrubber addScrubField:@"F1"]; + [mutable.viewInputsScrubber addScrubSafeListField:@"S1"]; + XCTAssertTrue(!mutable.enabled); + XCTAssertTrue(!mutable.captureLog); + XCTAssertTrue(!mutable.captureConnectivity); + XCTAssertEqual(mutable.maximumTelemetryData, 10); + XCTAssertEqual(mutable.memoryStatsAutocollectionInterval, 11); + XCTAssertNotNil(mutable.viewInputsScrubber); + XCTAssertNotNil(mutable.viewInputsScrubber.scrubFields); + XCTAssertNotNil(mutable.viewInputsScrubber.safeListFields); + XCTAssertEqual(mutable.viewInputsScrubber.scrubFields.count, 9); + XCTAssertEqual(mutable.viewInputsScrubber.safeListFields.count, 1); + XCTAssertEqual(mutable.viewInputsScrubber.scrubFields[8], @"F1"); + XCTAssertEqual(mutable.viewInputsScrubber.safeListFields[0], @"S1"); + + mutable.enabled = YES; + mutable.captureLog = YES; + mutable.captureConnectivity = YES; + mutable.maximumTelemetryData = 19; + mutable.memoryStatsAutocollectionInterval = 21; + [mutable.viewInputsScrubber addScrubField:@"F2"]; + [mutable.viewInputsScrubber addScrubSafeListField:@"S2"]; + XCTAssertTrue(mutable.enabled); + XCTAssertTrue(mutable.captureLog); + XCTAssertTrue(mutable.captureConnectivity); + XCTAssertEqual(mutable.maximumTelemetryData, 19); + XCTAssertEqual(mutable.memoryStatsAutocollectionInterval, 21); + XCTAssertNotNil(mutable.viewInputsScrubber); + XCTAssertNotNil(mutable.viewInputsScrubber.scrubFields); + XCTAssertNotNil(mutable.viewInputsScrubber.safeListFields); + XCTAssertEqual(mutable.viewInputsScrubber.scrubFields.count, 10); + XCTAssertEqual(mutable.viewInputsScrubber.safeListFields.count, 2); + XCTAssertTrue([mutable.viewInputsScrubber.scrubFields[8] isEqualToString:@"F1"]); + XCTAssertTrue([mutable.viewInputsScrubber.safeListFields[0] isEqualToString:@"S1"]); + XCTAssertTrue([mutable.viewInputsScrubber.scrubFields[9] isEqualToString:@"F2"]); + XCTAssertTrue([mutable.viewInputsScrubber.safeListFields[1] isEqualToString:@"S2"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"19"]); + XCTAssertTrue([content containsString:@"21"]); + XCTAssertTrue([content containsString:@"F1"]); + XCTAssertTrue([content containsString:@"F2"]); + XCTAssertTrue([content containsString:@"S1"]); + XCTAssertTrue([content containsString:@"S2"]); + + RollbarTelemetryOptions *immutable = [mutable copy]; + XCTAssertNotNil(immutable); + XCTAssertTrue(immutable.enabled); + XCTAssertTrue(immutable.captureLog); + XCTAssertTrue(immutable.captureConnectivity); + XCTAssertEqual(immutable.maximumTelemetryData, 19); + XCTAssertEqual(immutable.memoryStatsAutocollectionInterval, 21); + XCTAssertNotNil(immutable.viewInputsScrubber); + XCTAssertNotNil(immutable.viewInputsScrubber.scrubFields); + XCTAssertNotNil(immutable.viewInputsScrubber.safeListFields); + XCTAssertEqual(immutable.viewInputsScrubber.scrubFields.count, 10); + XCTAssertEqual(immutable.viewInputsScrubber.safeListFields.count, 2); + XCTAssertTrue([immutable.viewInputsScrubber.scrubFields[8] isEqualToString:@"F1"]); + XCTAssertTrue([immutable.viewInputsScrubber.safeListFields[0] isEqualToString:@"S1"]); + XCTAssertTrue([immutable.viewInputsScrubber.scrubFields[9] isEqualToString:@"F2"]); + XCTAssertTrue([immutable.viewInputsScrubber.safeListFields[1] isEqualToString:@"S2"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"19"]); + XCTAssertTrue([content containsString:@"21"]); + XCTAssertTrue([content containsString:@"F1"]); + XCTAssertTrue([content containsString:@"F2"]); + XCTAssertTrue([content containsString:@"S1"]); + XCTAssertTrue([content containsString:@"S2"]); + + XCTAssertEqualObjects(immutable, mutable); + XCTAssertNotIdentical(immutable, mutable); + + XCTAssertEqualObjects(immutable, [immutable copy]); + XCTAssertIdentical (immutable, [immutable copy]); + XCTAssertEqualObjects(immutable, [immutable mutableCopy]); + XCTAssertNotIdentical(immutable, [immutable mutableCopy]); + + XCTAssertEqualObjects(mutable, [mutable copy]); + XCTAssertNotIdentical(mutable, [mutable copy]); + XCTAssertEqualObjects(mutable, [mutable mutableCopy]); + XCTAssertNotIdentical(mutable, [mutable mutableCopy]); +} + +- (void)testRollbarConfig { + + NSString *defaultMutable = [[RollbarMutableConfig new] serializeToJSONString]; + NSString *defaultImmutable = [[RollbarConfig new] serializeToJSONString]; + XCTAssertTrue([defaultMutable isEqualToString:defaultImmutable]); + + RollbarMutableConfig *mutable = [RollbarMutableConfig new]; + mutable.destination.accessToken = @"test_AT_old"; + RollbarMutableDeveloperOptions *devOptions = [RollbarMutableDeveloperOptions new]; + devOptions.transmittedPayloadsLogFile = @"test_PL_old"; + mutable.developerOptions = devOptions; + + XCTAssertTrue([mutable.destination.accessToken isEqualToString:@"test_AT_old"]); + XCTAssertTrue([mutable.developerOptions.transmittedPayloadsLogFile isEqualToString:@"test_PL_old"]); + + NSString *content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_AT_old"]); + XCTAssertTrue([content containsString:@"test_PL_old"]); + + mutable.destination.accessToken = @"test_AT"; + mutable.developerOptions.transmittedPayloadsLogFile = @"test_PL"; + + XCTAssertTrue([mutable.destination.accessToken isEqualToString:@"test_AT"]); + XCTAssertTrue([mutable.developerOptions.transmittedPayloadsLogFile isEqualToString:@"test_PL"]); + + content = [mutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_AT"]); + XCTAssertTrue([content containsString:@"test_PL"]); + XCTAssertTrue(![content containsString:@"test_AT_old"]); + XCTAssertTrue(![content containsString:@"test_PL_old"]); + + RollbarConfig *immutable = [mutable copy]; + + XCTAssertTrue([immutable.destination.accessToken isEqualToString:@"test_AT"]); + XCTAssertTrue([immutable.developerOptions.transmittedPayloadsLogFile isEqualToString:@"test_PL"]); + + content = [immutable serializeToJSONString]; + XCTAssertNotNil(content); + XCTAssertTrue([content containsString:@"test_AT"]); + XCTAssertTrue([content containsString:@"test_PL"]); + XCTAssertTrue(![content containsString:@"test_AT_old"]); + XCTAssertTrue(![content containsString:@"test_PL_old"]); +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigurationTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigurationTests.m index 31de609a..3313450e 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigurationTests.m +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarConfigurationTests.m @@ -4,6 +4,7 @@ #if !TARGET_OS_WATCH #import +@import RollbarCommon; @import RollbarNotifier; @interface RollbarConfigurationTests : XCTestCase @@ -16,19 +17,60 @@ - (void)setUp { [super setUp]; - [RollbarLogger clearSdkDataStore]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil clearTelemetryFile]; - if (!Rollbar.currentConfiguration) { - [Rollbar initWithAccessToken:@""]; - } + RollbarMutableConfig *config = [RollbarMutableConfig new]; + config.developerOptions.transmit = NO; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.loggingOptions.enableOomDetection = NO; + + [Rollbar initWithConfiguration:config]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil clearTelemetryFile]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; } - (void)tearDown { - [Rollbar updateConfiguration:[RollbarConfig new]]; [super tearDown]; } +- (BOOL)rollbarTransmittedPayloadsLogContains:(nonnull NSString *)string { + + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *logItems = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + for (NSString *item in logItems) { + if ([item containsString:string]) { + return YES; + } + } + return NO; +} + +- (void)testConfigCloning { + + RollbarMutableConfig *rc = [RollbarMutableConfig new]; + NSString *customEnv = @"CUSTOM_ENV"; + + XCTAssertNotEqual(rc.destination.environment, customEnv); + XCTAssertFalse(NSOrderedSame == [rc.destination.environment compare: customEnv]); + rc.destination.environment = customEnv; + XCTAssertTrue(NSOrderedSame == [rc.destination.environment compare: customEnv]); + + RollbarConfig *rcClone = (RollbarConfig *)[rc copy]; + XCTAssertNotEqual(rc.destination, rcClone.destination); + XCTAssertTrue(NSOrderedSame == [rcClone.destination.environment compare: customEnv]); +} + - (void)testDefaultRollbarConfiguration { RollbarConfig *rc = [RollbarConfig new]; @@ -40,16 +82,19 @@ - (void)testScrubSafeListFields { NSString *scrubedContent = @"*****"; NSArray *keys = @[@"client.ios.app_name", @"client.ios.os_version", @"body.message.body"]; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; // define scrub fields: for (NSString *key in keys) { - [Rollbar.currentConfiguration.dataScrubber addScrubField:key]; + [config.dataScrubber addScrubField:key]; } + [Rollbar updateWithConfiguration:config]; [Rollbar debugMessage:@"test"]; - + [RollbarLogger flushRollbarThread]; - + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + // verify the fields were scrubbed: - NSArray *logItems = [RollbarLogger readLogItemsFromStore]; + NSArray *logItems = [RollbarTestUtil readIncomingPayloadsAsDictionaries]; for (NSString *key in keys) { NSString *content = [logItems[0] valueForKeyPath:key]; XCTAssertTrue([content isEqualToString:scrubedContent], @@ -60,18 +105,22 @@ - (void)testScrubSafeListFields { ); } - [RollbarLogger clearSdkDataStore]; - + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + // define scrub whitelist fields (the same as the scrub fields - to counterbalance them): for (NSString *key in keys) { - [Rollbar.currentConfiguration.dataScrubber addScrubSafeListField:key]; + [config.dataScrubber addScrubSafeListField:key]; } + [Rollbar updateWithConfiguration:config]; [Rollbar debugMessage:@"test"]; [RollbarLogger flushRollbarThread]; - + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + // verify the fields were not scrubbed: - logItems = [RollbarLogger readLogItemsFromStore]; + logItems = [RollbarTestUtil readIncomingPayloadsAsDictionaries]; for (NSString *key in keys) { NSString *content = [logItems[0] valueForKeyPath:key]; XCTAssertTrue(![content isEqualToString:scrubedContent], @@ -85,11 +134,10 @@ - (void)testScrubSafeListFields { - (void)testTelemetryEnabled { - [RollbarLogger clearSdkDataStore]; - BOOL expectedFlag = NO; - Rollbar.currentConfiguration.telemetry.enabled = expectedFlag; - [Rollbar reapplyConfiguration]; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + config.telemetry.enabled = expectedFlag; + [Rollbar updateWithConfiguration:config]; XCTAssertTrue(RollbarTelemetry.sharedInstance.enabled == expectedFlag, @"RollbarTelemetry.sharedInstance.enabled is expected to be NO." @@ -100,7 +148,7 @@ - (void)testTelemetryEnabled { [Rollbar recordErrorEventForLevel:RollbarLevel_Debug message:@"test"]; } - Rollbar.currentConfiguration.loggingOptions.maximumReportsPerMinute = max; + config.loggingOptions.maximumReportsPerMinute = max; NSArray *telemetryCollection = [[RollbarTelemetry sharedInstance] getAllData]; XCTAssertTrue(telemetryCollection.count == 0, @"Telemetry count is expected to be %i. Actual is %lu", @@ -109,8 +157,8 @@ - (void)testTelemetryEnabled { ); expectedFlag = YES; - Rollbar.currentConfiguration.telemetry.enabled = expectedFlag; - [Rollbar reapplyConfiguration]; + config.telemetry.enabled = expectedFlag; + [Rollbar updateWithConfiguration:config]; XCTAssertTrue(RollbarTelemetry.sharedInstance.enabled == expectedFlag, @"RollbarTelemetry.sharedInstance.enabled is expected to be YES." @@ -118,7 +166,7 @@ - (void)testTelemetryEnabled { for (int i=0; i + +@import RollbarNotifier; +@import UnitTesting; + +@interface RollbarInfrastructureTests : XCTestCase + +@end + +@implementation RollbarInfrastructureTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)test1_RollbarInfrastructureNotConfiguredException { + +// XCTAssertThrowsSpecificNamed([RollbarInfrastructure sharedInstance].configuration, +// NSException, +// @"RollbarInfrastructureNotConfiguredException", +// @"An RollbarInfrastructureNotConfiguredException is expected!"); +// XCTAssertThrowsSpecificNamed([RollbarInfrastructure sharedInstance].logger, +// NSException, +// @"RollbarInfrastructureNotConfiguredException", +// @"An RollbarInfrastructureNotConfiguredException is expected!"); + +// XCTAssertThrows([RollbarInfrastructure sharedInstance].configuration, +// @"An RollbarInfrastructureNotConfiguredException is expected!" +// ); +// XCTAssertThrows([RollbarInfrastructure sharedInstance].logger, +// @"An RollbarInfrastructureNotConfiguredException is expected!" +// ); + + + [[RollbarInfrastructure sharedInstance] configureWith:[RollbarConfig new]]; + + XCTAssertNoThrow([RollbarInfrastructure sharedInstance].configuration, + @"An RollbarInfrastructureNotConfiguredException is NOT expected!" + ); + XCTAssertNoThrow([RollbarInfrastructure sharedInstance].logger, + @"An RollbarInfrastructureNotConfiguredException is NOT expected!" + ); +} + +- (void)test2_Basics { + + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil clearTelemetryFile]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *items = [RollbarTestUtil readIncomingPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + + RollbarMutableConfig *config = + [RollbarMutableConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment] + ]; + config.developerOptions.transmit = NO; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.loggingOptions.maximumReportsPerMinute = 180; + [[RollbarInfrastructure sharedInstance] configureWith:config]; + + //[NSThread sleepForTimeInterval:1.0f]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + + [[RollbarInfrastructure sharedInstance].logger log:RollbarLevel_Critical + message:@"RollbarInfrastructure basics test 2!" + data:nil + context:nil + ]; + + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + BOOL wasLogged = NO; + for (NSString *item in items) { + if (YES == [item containsString:@"RollbarInfrastructure basics test 2!"]) { + wasLogged = YES; + break; + } + } + XCTAssertTrue(YES == wasLogged); + + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil deleteLogFiles]; + + items = [RollbarTestUtil readIncomingPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); +} + +- (void)test3_Live { + + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil clearTelemetryFile]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *items = [RollbarTestUtil readIncomingPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + + RollbarMutableConfig *config = + [RollbarMutableConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment]]; + config.developerOptions.transmit = YES; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + [[RollbarInfrastructure sharedInstance] configureWith:config]; + + + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + items = [RollbarTestUtil readIncomingPayloadsAsStrings]; + XCTAssertNotNil(items); + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + + [[RollbarInfrastructure sharedInstance].logger log:RollbarLevel_Critical + message:@"RollbarInfrastructure basics test 3!" + data:nil + context:nil + ]; + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + BOOL wasLogged = NO; + for (NSString *item in items) { + if (YES == [item containsString:@"RollbarInfrastructure basics test 3!"]) { + wasLogged = YES; + break; + } + } + XCTAssertTrue(YES == wasLogged); + + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil deleteLogFiles]; + + items = [RollbarTestUtil readIncomingPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); + items = [RollbarTestUtil readTransmittedPayloadsAsStrings]; + XCTAssertNotNil(items); + XCTAssertEqual(0, items.count); +} + +//- (void)testPerformanceExample { +// // This is an example of a performance test case. +// [self measureBlock:^{ +// // Put the code you want to measure the time of here. +// }]; +//} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadFactoryTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadFactoryTests.m new file mode 100644 index 00000000..441450f9 --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadFactoryTests.m @@ -0,0 +1,174 @@ +// +// RollbarPayloadFactoryTests.m +// +// +// Created by Andrey Kornich on 2022-06-15. +// + +#import +#import "XCTestCase+RollbarNotifierTest.h" + +#import "../../Sources/RollbarNotifier/RollbarPayloadFactory.h" + +@import UnitTesting; +@import RollbarNotifier; + +@interface RollbarPayloadFactoryTests : XCTestCase + +@end + +@implementation RollbarPayloadFactoryTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testCrashReportPayload { + + NSString *crashReport = @"Fake crash report!"; + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + crashReport:crashReport]; + + [self assertCommonComponentsOfPayload:payload]; + + XCTAssertNotNil(payload.data.body.crashReport); + XCTAssertNil(payload.data.body.trace); + XCTAssertNil(payload.data.body.traceChain); + XCTAssertNil(payload.data.body.message); + + XCTAssertTrue([payload.data.body.crashReport.rawCrashReport containsString:crashReport]); + XCTAssertTrue([[payload serializeToJSONString] containsString:crashReport]); +} + +- (void)testMessagePayload { + + NSString *message = @"MessageMock"; + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + message:message + data:nil + context:@"MockContext"]; + + [self assertCommonComponentsOfPayload:payload]; + + XCTAssertNil(payload.data.body.crashReport); + XCTAssertNil(payload.data.body.trace); + XCTAssertNil(payload.data.body.traceChain); + XCTAssertNotNil(payload.data.body.message); + + XCTAssertTrue([payload.data.body.message.description containsString:message]); + XCTAssertTrue([[payload serializeToJSONString] containsString:message]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"MockContext"]); +} + + +- (void)testErrorPayload { + + NSError *error = [NSError errorWithDomain:@"NSErrorDomain" code:1001 userInfo:nil]; + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + error:error + data:nil + context:@"MockContext"]; + + [self assertCommonComponentsOfPayload:payload]; + + XCTAssertNil(payload.data.body.crashReport); + XCTAssertNil(payload.data.body.trace); + XCTAssertNil(payload.data.body.traceChain); + XCTAssertNotNil(payload.data.body.message); + + XCTAssertTrue([payload.data.body.message.description containsString:@"NSErrorDomain"]); + XCTAssertTrue([payload.data.body.message.description containsString:@"1001"]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"NSErrorDomain"]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"1001"]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"MockContext"]); +} + +- (void)testExceptionPayload { + + NSException *exception = [NSException exceptionWithName:@"Oy vey!" reason:@"Don't ask!" userInfo:nil]; + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + exception:exception + data:nil + context:@"MockContext"]; + + [self assertCommonComponentsOfPayload:payload]; + + XCTAssertNil(payload.data.body.crashReport); + XCTAssertNotNil(payload.data.body.trace); + XCTAssertNil(payload.data.body.traceChain); + XCTAssertNil(payload.data.body.message); + + XCTAssertTrue([[payload serializeToJSONString] containsString:@"NSException"]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"Don't ask!"]); + XCTAssertTrue([[payload serializeToJSONString] containsString:@"MockContext"]); +} + +- (void)testPerformanceCrashReportPayload { + + NSString *crashReport = @"Fake crash report!"; + + [self measureBlock:^{ + + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + crashReport:crashReport]; + [payload serializeToJSONString]; + }]; +} + +- (void)testPerformanceMessagePayload { + + NSString *message = @"MessageMock"; + + [self measureBlock:^{ + + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + message:message + data:nil + context:@"MockContext"]; + [payload serializeToJSONString]; + }]; +} + +- (void)testPerformanceErrorPayload { + + NSError *error = [NSError errorWithDomain:@"NSErrorDomain" code:1001 userInfo:nil]; + + [self measureBlock:^{ + + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + error:error + data:nil + context:@"MockContext"]; + [payload serializeToJSONString]; + }]; +} + +- (void)testPerformanceExceptionPayload { + + NSException *exception = [NSException exceptionWithName:@"Oy vey!" reason:@"Don't ask!" userInfo:nil]; + + [self measureBlock:^{ + + RollbarPayload *payload = [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + exception:exception + data:nil + context:@"MockContext"]; + [payload serializeToJSONString]; + }]; +} + +- (void)assertCommonComponentsOfPayload:(nullable RollbarPayload *)payload { + + XCTAssertNotNil(payload); + XCTAssertNotNil(payload.data); + XCTAssertNotNil(payload.data.body); + XCTAssertNotNil(payload.data.notifier); + //TODO: continue asserting presence and values of other expected common components of every payload... +} + + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadPostReplyTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadPostReplyTests.m new file mode 100644 index 00000000..c8eb05ca --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadPostReplyTests.m @@ -0,0 +1,71 @@ +// +// RollbarPayloadPostReplyTests.m +// +// +// Created by Andrey Kornich on 2022-06-14. +// + +#import +#import "../../Sources/RollbarNotifier/RollbarPayloadPostReply.h" + +@interface RollbarPayloadPostReplyTests : XCTestCase + +@end + +@implementation RollbarPayloadPostReplyTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testCanPostNowReply { + + RollbarPayloadPostReply *reply = [[RollbarPayloadPostReply alloc] initWithStatusCode:200 + rateLimit:60 + remainingCount:10 + remainingSeconds:20]; + NSDate *now = [NSDate date]; + XCTAssertEqual([now earlierDate:reply.nextPostTime], reply.nextPostTime); +} + +- (void)testWillPostLaterReply { + + RollbarPayloadPostReply *reply = [[RollbarPayloadPostReply alloc] initWithStatusCode:200 + rateLimit:60 + remainingCount:0 + remainingSeconds:20]; + NSDate *now = [NSDate date]; + XCTAssertEqual([now earlierDate:reply.nextPostTime], now); + XCTAssertTrue([reply.nextPostTime timeIntervalSinceDate:now] <= 20); +} + +- (void)testGreenReply { + + RollbarPayloadPostReply *reply = [RollbarPayloadPostReply greenReply]; + NSDate *now = [NSDate date]; + XCTAssertEqual([now earlierDate:reply.nextPostTime], reply.nextPostTime); +} + +- (void)testRedReply { + + RollbarPayloadPostReply *reply = [RollbarPayloadPostReply redReply]; + NSDate *now = [NSDate date]; + XCTAssertEqual([now earlierDate:reply.nextPostTime], now); + XCTAssertTrue([reply.nextPostTime timeIntervalSinceDate:now] <= reply.remainingSeconds); +} + +- (void)testPerformanceExample { + + [self measureBlock:^{ + RollbarPayloadPostReply *reply = [[RollbarPayloadPostReply alloc] initWithStatusCode:200 + rateLimit:60 + remainingCount:10 + remainingSeconds:20]; + }]; +} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadRepositoryTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadRepositoryTests.m new file mode 100644 index 00000000..b71e57f5 --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarPayloadRepositoryTests.m @@ -0,0 +1,561 @@ +// +// RollbarPayloadRepositoryTests.m +// +// +// Created by Andrey Kornich on 2022-07-28. +// + +#import +#import "../../Sources/RollbarNotifier/RollbarPayloadRepository.h" + +@import UnitTesting; + +@interface RollbarPayloadRepositoryTests : XCTestCase + +@end + +@implementation RollbarPayloadRepositoryTests + +- (void)setUp { + + [super setUp]; + + [RollbarTestUtil deletePayloadsStoreFile]; + XCTAssertFalse([RollbarTestUtil checkPayloadsStoreFileExists]); +} + +- (void)tearDown { + + //[RollbarTestUtil deletePayloadsStoreFile]; + + [super tearDown]; +} + +#pragma mark - Destinations unit tests + +- (void)testInMemoryRepoInitialization { + + XCTAssertFalse([RollbarTestUtil checkPayloadsStoreFileExists]); + RollbarPayloadRepository *repo = [RollbarPayloadRepository inMemoryRepository]; + XCTAssertFalse([RollbarTestUtil checkPayloadsStoreFileExists]); + + XCTAssertFalse([repo checkIfTableExists_Unknown]); + XCTAssertTrue ([repo checkIfTableExists_Destinations]); + XCTAssertTrue ([repo checkIfTableExists_Payloads]); +} + +- (void)testAddDestination { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + NSDictionary *destination = + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + + XCTAssertTrue([destination.allKeys containsObject:@"id"]); + XCTAssertTrue([destination.allKeys containsObject:@"endpoint"]); + XCTAssertTrue([destination.allKeys containsObject:@"access_token"]); + + XCTAssertTrue([destination.allValues containsObject:@"EP_001"]); + XCTAssertTrue([destination.allValues containsObject:@"AT_001"]); + + XCTAssertNotNil(destination[@"id"]); + XCTAssertTrue([destination[@"endpoint"] isEqualToString:@"EP_001"]); + XCTAssertTrue([destination[@"access_token"] isEqualToString:@"AT_001"]); +} + +- (void)testGetDestination { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + [self insertDestinationMocks:repo]; + + NSDictionary *destination = + [repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + + XCTAssertNotNil(destination); + + XCTAssertTrue([destination.allKeys containsObject:@"id"]); + XCTAssertTrue([destination.allKeys containsObject:@"endpoint"]); + XCTAssertTrue([destination.allKeys containsObject:@"access_token"]); + + XCTAssertTrue([destination.allValues containsObject:@"EP_001"]); + XCTAssertTrue([destination.allValues containsObject:@"AT_001"]); + + XCTAssertTrue([destination[@"endpoint"] isEqualToString:@"EP_001"]); + XCTAssertTrue([destination[@"access_token"] isEqualToString:@"AT_001"]); +} + +- (void)testGetDestinationByID { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + [self insertDestinationMocks:repo]; + + NSDictionary *destination = + [repo getDestinationByID:@"0000000000001"]; + + XCTAssertNotNil(destination); + + XCTAssertTrue([destination.allKeys containsObject:@"id"]); + XCTAssertTrue([destination.allKeys containsObject:@"endpoint"]); + XCTAssertTrue([destination.allKeys containsObject:@"access_token"]); + + XCTAssertTrue([destination[@"id"] isEqualToString:@"1"]); + XCTAssertTrue([destination[@"endpoint"] isEqualToString:@"EP_001"]); + XCTAssertTrue([destination[@"access_token"] isEqualToString:@"AT_001"]); +} + +- (void)testGetAllDestinations { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_002"]; + + NSArray *> *destinations = + [repo getAllDestinations]; + + XCTAssertNotNil(destinations); + + XCTAssertEqual(destinations.count, 2); + + for(NSDictionary *destination in destinations) { + + XCTAssertTrue([destination.allKeys containsObject:@"id"]); + XCTAssertTrue([destination.allKeys containsObject:@"endpoint"]); + XCTAssertTrue([destination.allKeys containsObject:@"access_token"]); + + XCTAssertTrue([destination.allValues containsObject:@"EP_001"]); + + XCTAssertTrue([destination[@"endpoint"] isEqualToString:@"EP_001"]); + XCTAssertTrue([destination[@"access_token"] containsString:@"AT_00"]); + } +} + +- (void)testDestinationRemovals { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + [self insertDestinationMocks:repo]; + + int removedCount = 0; + int initialCount = [repo getAllDestinations].count; + + XCTAssertEqual(YES, [repo removeDestinationByID:@"0001"]); + removedCount++; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); + + XCTAssertEqual(YES, [repo removeDestinationByID:@"0001"]); + //removedCount++; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); + + XCTAssertEqual(YES, [repo removeDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_007"]); + removedCount++; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); + + XCTAssertEqual(YES, [repo removeDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_007"]); + //removedCount++; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); + + XCTAssertEqual(YES, [repo removeUnusedDestinations]); + removedCount = initialCount; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); + + XCTAssertEqual(YES, [repo removeAllDestinations]); + removedCount = initialCount; + XCTAssertEqual(initialCount - removedCount, [repo getAllDestinations].count); +} + +#pragma mark - Destinations performance tests + +- (void)testRepoInitializationPerformance { + + [self measureBlock:^{ + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + }]; +} + +- (void)testAddRemoveDestinationPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + + [self measureBlock:^{ + + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + + [repo removeDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + }]; +} + +- (void)testAddRemoveDestinationByIDPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + + [self measureBlock:^{ + + NSDictionary *destinationRow = [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + + [repo removeDestinationByID:destinationRow[@"id"]]; + }]; +} + +- (void)testGetDestinationPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + [self insertDestinationMocks:repo]; + XCTAssertNotNil([repo getAllDestinations]); + XCTAssertTrue([repo getAllDestinations].count > 0); + + [self measureBlock:^{ + + id result = [repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_009"]; + }]; +} + +- (void)testGetAllDestinationsPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + [self insertDestinationMocks:repo]; + + NSArray __block *result = nil; + [self measureBlock:^{ + + result = [repo getAllDestinations]; + }]; + + XCTAssertNotNil(result); + XCTAssertTrue(result.count > 0); +} + + +#pragma mark - Payloads unit tests + +- (void)testTimestampCalculations { + + NSDate *cutoffTime = [NSDate date]; + NSNumber *threshold = [NSNumber numberWithInteger:cutoffTime.timeIntervalSince1970]; + + RollbarSdkLog(@"*** Removing payloads older than: %@ (%@)", + cutoffTime, + [NSDate dateWithTimeIntervalSince1970:[threshold integerValue]] + ); + XCTAssertTrue((cutoffTime.timeIntervalSince1970 - threshold.doubleValue) < 1.0); +} + +- (void)testAddGetRemovePayload { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + [self insertDestinationMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + + XCTAssertEqual(0, [repo getAllPayloads].count); + + XCTAssertNil([repo getPayloadByID:@"0001"]); + NSDictionary *dataFields = + [repo addPayload:@"PL_001" + withConfig:@"C_001" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"] + ]; + XCTAssertEqual(1, [repo getAllPayloads].count); + NSDictionary *dataFields1 = [repo getPayloadByID:dataFields[@"id"]]; + XCTAssertNotNil(dataFields1); + XCTAssertEqual(5, dataFields1.count); + XCTAssertTrue([dataFields[@"id"] isEqualToString:dataFields1[@"id"]]); + XCTAssertTrue([dataFields[@"payload_json"] isEqualToString:dataFields1[@"payload_json"]]); + XCTAssertTrue([dataFields[@"config_json"] isEqualToString:dataFields1[@"config_json"]]); + + [repo removePayloadByID:dataFields[@"id"]]; + XCTAssertNil([repo getPayloadByID:dataFields[@"id"]]); + XCTAssertEqual(0, [repo getAllPayloads].count); +} + +- (void)testAddPayloadWithOddContent { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + [self insertDestinationMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + + XCTAssertEqual(0, [repo getAllPayloads].count); + + XCTAssertNil([repo getPayloadByID:@"0001"]); + + NSDictionary *dataFields = + [repo addPayload:@"PL_001" + withConfig:@"C_001" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"] + ]; + XCTAssertEqual(1, [repo getAllPayloads].count); + NSDictionary *dataFields1 = [repo getPayloadByID:dataFields[@"id"]]; + XCTAssertNotNil(dataFields1); + XCTAssertEqual(5, dataFields1.count); + + [repo removePayloadByID:dataFields[@"id"]]; + XCTAssertNil([repo getPayloadByID:dataFields[@"id"]]); + XCTAssertEqual(0, [repo getAllPayloads].count); + + + dataFields = + [repo addPayload:@"Don't" + withConfig:@"C_001" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"] + ]; + XCTAssertEqual(1, [repo getAllPayloads].count); + id payloads = [repo getAllPayloads]; +} + +- (void)testGetAllPayloads { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 < [repo getAllPayloads].count); +} + +- (void)testRemoveAllPayloads { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 < [repo getAllPayloads].count); + [repo removeAllPayloads]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); +} + +- (void)testRemoveOldPayloads { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 < [repo getAllPayloads].count); + [repo removePayloadsOlderThan:[NSDate distantPast]]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertEqual(4, [repo getAllPayloads].count); + [repo removePayloadsOlderThan:[NSDate distantFuture]]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertEqual(0, [repo getAllPayloads].count); +} + +- (void)testGetPayloadsWithDestinationID { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + NSString *destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"]; + XCTAssertEqual(3, [repo getAllPayloadsWithDestinationID:destinationID].count); + destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_006"][@"id"]; + XCTAssertEqual(1, [repo getAllPayloadsWithDestinationID:destinationID].count); + destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_007"][@"id"]; + XCTAssertEqual(0, [repo getAllPayloadsWithDestinationID:destinationID].count); +} + + +- (void)testGetPayloadsWithDestinationIDAndOffsetAndLimit { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertEqual(4, [repo getAllPayloads].count); + + NSString *destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"]; + XCTAssertEqual(3, [repo getAllPayloadsWithDestinationID:destinationID].count); + + + XCTAssertEqual(2, [repo getPayloadsWithDestinationID:destinationID andLimit:2].count); + XCTAssertEqual(3, [repo getPayloadsWithDestinationID:destinationID andOffset:0 andLimit:3].count); + XCTAssertEqual(3, [repo getPayloadsWithDestinationID:destinationID andOffset:0 andLimit:6].count); + XCTAssertEqual(2, [repo getPayloadsWithDestinationID:destinationID andOffset:1 andLimit:2].count); + XCTAssertEqual(2, [repo getPayloadsWithDestinationID:destinationID andOffset:1 andLimit:4].count); + XCTAssertEqual(1, [repo getPayloadsWithDestinationID:destinationID andOffset:2 andLimit:2].count); + XCTAssertEqual(1, [repo getPayloadsWithDestinationID:destinationID andOffset:2 andLimit:4].count); + XCTAssertEqual(0, [repo getPayloadsWithDestinationID:destinationID andOffset:5 andLimit:2].count); +} + +- (void)testGetPayloadsWithOffsetAndLimit { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertEqual(4, [repo getAllPayloads].count); + XCTAssertEqual(2, [repo getPayloadsWithLimit:2].count); + XCTAssertEqual(2, [repo getPayloadsWithOffset:2 andLimit:4].count); + XCTAssertEqual(1, [repo getPayloadsWithOffset:3 andLimit:4].count); + XCTAssertEqual(0, [repo getPayloadsWithOffset:4 andLimit:4].count); + XCTAssertEqual(0, [repo getPayloadsWithOffset:5 andLimit:4].count); + XCTAssertEqual(0, [repo getPayloadsWithOffset:2 andLimit:0].count); +} + +#pragma mark - Payloads performance tests + +- (void)testAddGetRemovePayloadPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + [self insertDestinationMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + + XCTAssertEqual(0, [repo getAllPayloads].count); + + XCTAssertNil([repo getPayloadByID:@"0001"]); + + [self measureBlock:^{ + NSDictionary *dataFields = + [repo addPayload:@"PL_001" + withConfig:@"C_001" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"] + ]; + [repo removePayloadByID:dataFields[@"id"]]; + }]; +} + +- (void)testGetAllPayloadsPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + [self measureBlock:^{ + id result = [repo getAllPayloads]; + }]; +} + +- (void)testRemoveAllPayloadsPerformance { + + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 < [repo getAllPayloads].count); + [self measureBlock:^{ + [repo removeAllPayloads]; + }]; +} + +- (void)testRemoveOldPayloadsPerformance { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertTrue(0 < [repo getAllDestinations].count); + XCTAssertTrue(0 < [repo getAllPayloads].count); + [self measureBlock:^{ + [repo removePayloadsOlderThan:[NSDate distantPast]]; + }]; +} + +- (void)testGetPayloadsWithDestinationIDPerformance { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + NSString *destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"]; + [self measureBlock:^{ + id result = [repo getAllPayloadsWithDestinationID:destinationID]; + }]; +} + + +- (void)testGetPayloadsWithDestinationIDAndOffsetAndLimitPerformance { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + XCTAssertEqual(4, [repo getAllPayloads].count); + + NSString *destinationID = [repo getDestinationWithEndpoint:@"EP_001" + andAccesToken:@"AT_005"][@"id"]; + [self measureBlock:^{ + id result = [repo getPayloadsWithDestinationID:destinationID andOffset:1 andLimit:4]; + }]; +} + +- (void)testGetPayloadsWithOffsetAndLimitPerformance { + + NSDate *cutoffTime = [NSDate date]; + RollbarPayloadRepository *repo = [RollbarPayloadRepository persistentRepository]; + XCTAssertTrue(0 == [repo getAllDestinations].count); + XCTAssertTrue(0 == [repo getAllPayloads].count); + [self insertPayloadMocks:repo]; + [self measureBlock:^{ + id result = [repo getPayloadsWithOffset:2 andLimit:4]; + }]; +} + +#pragma mark - mocking helpers + +- (void)insertDestinationMocks:(RollbarPayloadRepository *)repo { + + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_001"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_002"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_003"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_004"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_005"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_006"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_007"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_008"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_009"]; + [repo addDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_010"]; + + [repo addDestinationWithEndpoint:@"EP_002" andAccesToken:@"AT_001"]; + [repo addDestinationWithEndpoint:@"EP_003" andAccesToken:@"AT_001"]; + [repo addDestinationWithEndpoint:@"EP_004" andAccesToken:@"AT_001"]; + [repo addDestinationWithEndpoint:@"EP_005" andAccesToken:@"AT_001"]; +} + +- (void)insertPayloadMocks:(RollbarPayloadRepository *)repo { + + [self insertDestinationMocks:repo]; + + //paylooads for EP_001/AT_005 destination + [repo addPayload:@"PL_001" + withConfig:@"C_001" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_005"][@"id"] + ]; + [repo addPayload:@"PL_002" + withConfig:@"C_002" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_005"][@"id"] + ]; + [repo addPayload:@"PL_003" + withConfig:@"C_003" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_005"][@"id"] + ]; + + //paylooads for EP_001/AT_006 destination + [repo addPayload:@"PL_004" + withConfig:@"C_004" + andDestinationID:[repo getDestinationWithEndpoint:@"EP_001" andAccesToken:@"AT_006"][@"id"] + ]; +} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarRegistryTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarRegistryTests.m new file mode 100644 index 00000000..b1da9799 --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarRegistryTests.m @@ -0,0 +1,139 @@ +// +// RollbarLoggerRegistryTests.m +// +// +// Created by Andrey Kornich on 2022-06-30. +// + +#import + +@import Foundation; +@import RollbarNotifier; + +#import "../../Sources/RollbarNotifier/RollbarRegistry.h" +#import "../../Sources/RollbarNotifier/RollbarDestinationRecord.h" +#import "../../Sources/RollbarNotifier/RollbarPayloadPostReply.h" + +@interface RollbarLoggerRegistryTests : XCTestCase + +@end + +@implementation RollbarLoggerRegistryTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +#pragma mark - registry destination record tests + +- (void)testDestinationRecord { + + RollbarRegistry *registry = [RollbarRegistry new]; + XCTAssertEqual(0, registry.totalDestinationRecords); + + RollbarConfig *config = [RollbarConfig mutableConfigWithAccessToken:@"AT1" + environment:@"Env1"]; + + RollbarDestinationRecord *record = [registry getRecordForConfig:config]; + XCTAssertEqual(1, registry.totalDestinationRecords); + XCTAssertEqual(YES, [record canPost]); + XCTAssertNotNil(record.nextEarliestPost); + XCTAssertNil(record.nextLocalWindowStart); + XCTAssertNil(record.nextServerWindowStart); + + RollbarPayloadPostReply *reply = [RollbarPayloadPostReply greenReply]; + [record recordPostReply:reply]; + XCTAssertTrue([record canPost]); + + reply = [RollbarPayloadPostReply yellowReply]; + [record recordPostReply:reply]; + XCTAssertFalse([record canPost]); + + reply = [RollbarPayloadPostReply redReply]; + [record recordPostReply:reply]; + XCTAssertFalse([record canPost]); +} + +#pragma mark - registry records tests + +- (void)testRegistryRecords { + + RollbarRegistry *registry = [RollbarRegistry new]; + XCTAssertEqual(0, registry.totalDestinationRecords); + + RollbarConfig *config = [RollbarConfig configWithAccessToken:@"AT1" + environment:@"Env1"]; + + RollbarDestinationRecord *record = [registry getRecordForConfig:config]; + XCTAssertEqual(1, registry.totalDestinationRecords); + XCTAssertTrue([record.destinationID containsString:@"https://api.rollbar.com/api/1/item/"]); + XCTAssertTrue([record.destinationID containsString:@"AT1"]); + XCTAssertFalse([record.destinationID containsString:@"Env1"]); + + RollbarConfig *config1 = [config copy]; + RollbarDestinationRecord *record1 = [registry getRecordForConfig:config1]; + XCTAssertEqual(1, registry.totalDestinationRecords); + XCTAssertTrue([record1.destinationID containsString:@"https://api.rollbar.com/api/1/item/"]); + XCTAssertTrue([record1.destinationID containsString:@"AT1"]); + XCTAssertFalse([record1.destinationID containsString:@"Env1"]); + XCTAssertEqualObjects(record, record1); + XCTAssertEqual(record, record1); + XCTAssertIdentical(record, record1); + XCTAssertTrue(record == record1); + + RollbarConfig *config2 = [RollbarConfig configWithAccessToken:@"AT2" + environment:@"Env2"]; + RollbarDestinationRecord *record2 = [registry getRecordForConfig:config2]; + XCTAssertEqual(2, registry.totalDestinationRecords); + XCTAssertTrue([record2.destinationID containsString:@"https://api.rollbar.com/api/1/item/"]); + XCTAssertTrue([record2.destinationID containsString:@"AT2"]); + XCTAssertFalse([record2.destinationID containsString:@"Env2"]); + XCTAssertNotEqualObjects(record2, record1); + XCTAssertNotEqual(record2, record1); + XCTAssertNotIdentical(record2, record1); + XCTAssertFalse(record2 == record1); +} + +- (void)testPerformanceOfNewDestinationRecord { + + RollbarRegistry *registry = [RollbarRegistry new]; + XCTAssertEqual(0, registry.totalDestinationRecords); + + RollbarConfig *config = [RollbarConfig configWithAccessToken:@"AT1" + environment:@"Env1"]; + [self measureBlock:^{ + RollbarDestinationRecord *record = [registry getRecordForConfig:config]; + }]; +} + +#pragma mark - destination ID tests + +- (void)testDestinationID { + + RollbarDestination *destination = [[RollbarDestination alloc] initWithEndpoint:@"End_Point" + accessToken:@"Access_Token" + environment:@"ENV" + ]; + + NSString *destinationID = [RollbarRegistry destinationID:destination]; + XCTAssertTrue([destinationID containsString:@"End_Point"]); + XCTAssertTrue([destinationID containsString:@"Access_Token"]); + XCTAssertFalse([destinationID containsString:@"ENV"]); +} + +- (void)testPerformanceDestinationID { + + RollbarDestination *destination = [[RollbarDestination alloc] initWithEndpoint:@"End_Point" + accessToken:@"Access_Token" + environment:@"ENV" + ]; + [self measureBlock:^{ + NSString *destinationID = [RollbarRegistry destinationID:destination]; + }]; +} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarSenderTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarSenderTests.m new file mode 100644 index 00000000..8a13527a --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarSenderTests.m @@ -0,0 +1,134 @@ +// +// RollbarSenderTests.m +// +// +// Created by Andrey Kornich on 2022-06-14. +// + +#import +#import "XCTestCase+RollbarNotifierTest.h" + +#import "../../Sources/RollbarNotifier/RollbarSender.h" +#import "../../Sources/RollbarNotifier/RollbarPayloadFactory.h" + +@import RollbarNotifier; + +@interface RollbarSenderTests : XCTestCase + +@end + +@implementation RollbarSenderTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +#pragma mark - tests of live-sending of different payload types + +- (void)testSendingCrashReport { + + RollbarPayload *payload = [self getPayload_CrashReport]; + [self sendAndAssertPayload:payload]; +} + +- (void)testSendingMessage { + + RollbarPayload *payload = [self getPayload_Message]; + [self sendAndAssertPayload:payload]; +} + +- (void)testSendingError { + + RollbarPayload *payload = [self getPayload_Error]; + [self sendAndAssertPayload:payload]; +} + +- (void)testSendingException { + + RollbarPayload *payload = [self getPayload_Exception]; + [self sendAndAssertPayload:payload]; +} + +- (void)sendAndAssertPayload:(RollbarPayload *)payload { + + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + + XCTAssertNotNil(reply); +} + +#pragma mark - performance tests + +- (void)testPerformanceRollbarSenderInstantiation{ + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarSender *sender = [RollbarSender new]; + }]; +} + +- (void)testPerformanceSendingCrashReport { + + RollbarPayload *payload = [self getPayload_CrashReport]; + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + }]; +} + +- (void)testPerformanceSendingMessage { + + RollbarPayload *payload = [self getPayload_Message]; + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + }]; +} + +- (void)testPerformanceSendingError { + + RollbarPayload *payload = [self getPayload_Error]; + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + }]; +} + +- (void)testPerformanceSendingException { + + RollbarPayload *payload = [self getPayload_Exception]; + //[self measureSendingPerformanceOfPayload:payload]; + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + }]; +} + +- (void)measureSendingPerformanceOfPayload:(RollbarPayload *)payload { + + NSData *payloadData = [payload serializeToJSONData]; + RollbarSender *sender = [RollbarSender new]; + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + RollbarPayloadPostReply *reply = [sender sendPayload:payloadData usingConfig:[self getConfig_Live_Default]]; + }]; +} +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTelemetryTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTelemetryTests.m index f9ca5a9d..a7222967 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTelemetryTests.m +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTelemetryTests.m @@ -16,24 +16,35 @@ - (void)setUp { [super setUp]; - [RollbarLogger clearSdkDataStore]; - - if (!Rollbar.currentConfiguration) { - [Rollbar initWithAccessToken: [RollbarTestHelper getRollbarPayloadsAccessToken]]; - Rollbar.currentConfiguration.destination.environment = [RollbarTestHelper getRollbarEnvironment]; - } - + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil clearTelemetryFile]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + RollbarMutableConfig *config = + [RollbarMutableConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment]]; + config.developerOptions.transmit = NO; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.loggingOptions.maximumReportsPerMinute = 180; + [Rollbar updateWithConfiguration:config]; + + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + [RollbarTestUtil deleteLogFiles]; } - (void)tearDown { - [Rollbar updateConfiguration:[RollbarConfig new]]; + [Rollbar updateWithConfiguration:[RollbarConfig new]]; [super tearDown]; } - (void)testTelemetryCapture { - Rollbar.currentConfiguration.telemetry.enabled = YES; - [Rollbar reapplyConfiguration]; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + config.telemetry.enabled = YES; + [Rollbar updateWithConfiguration:config]; [Rollbar recordNavigationEventForLevel:RollbarLevel_Info from:@"from" to:@"to"]; [Rollbar recordConnectivityEventForLevel:RollbarLevel_Info status:@"status"]; @@ -44,9 +55,10 @@ - (void)testTelemetryCapture { [Rollbar debugMessage:@"Test"]; [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; - NSArray *logItems = [RollbarLogger readLogItemsFromStore]; - NSDictionary *item = logItems[0]; + NSArray *logItems = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + NSDictionary *item = logItems[logItems.count - 1]; NSArray *telemetryData = [item valueForKeyPath:@"body.telemetry"]; XCTAssertTrue(telemetryData.count > 0); @@ -78,7 +90,9 @@ - (void)testTelemetryCapture { - (void)testErrorReportingWithTelemetry { - Rollbar.currentConfiguration.telemetry.enabled = YES; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + config.telemetry.enabled = YES; + [Rollbar updateWithConfiguration:config]; [Rollbar recordNavigationEventForLevel:RollbarLevel_Info from:@"SomeNavigationSource" to:@"SomeNavigationDestination"]; [Rollbar recordConnectivityEventForLevel:RollbarLevel_Info status:@"SomeConnectivityStatus"]; @@ -94,9 +108,10 @@ - (void)testErrorReportingWithTelemetry { [Rollbar debugMessage:@"Demonstrate Telemetry capture once more..."]; [Rollbar debugMessage:@"DO Demonstrate Telemetry capture once more..."]; - //[NSThread sleepForTimeInterval:8.0f]; - - NSArray *logItems = [RollbarLogger readLogItemsFromStore]; + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *logItems = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; for (NSDictionary *item in logItems) { NSArray *telemetryData = [item valueForKeyPath:@"body.telemetry"]; @@ -130,11 +145,12 @@ - (void)testErrorReportingWithTelemetry { - (void)testTelemetryViewEventScrubbing { - Rollbar.currentConfiguration.telemetry.enabled = YES; - Rollbar.currentConfiguration.telemetry.viewInputsScrubber.enabled = YES; - [Rollbar.currentConfiguration.telemetry.viewInputsScrubber addScrubField:@"password"]; - [Rollbar.currentConfiguration.telemetry.viewInputsScrubber addScrubField:@"pin"]; - [Rollbar reapplyConfiguration]; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + config.telemetry.enabled = YES; + config.telemetry.viewInputsScrubber.enabled = YES; + [config.telemetry.viewInputsScrubber addScrubField:@"password"]; + [config.telemetry.viewInputsScrubber addScrubField:@"pin"]; + [Rollbar updateWithConfiguration:config]; [Rollbar recordViewEventForLevel:RollbarLevel_Debug element:@"password" @@ -154,9 +170,10 @@ - (void)testTelemetryViewEventScrubbing { - (void)testRollbarLog { - Rollbar.currentConfiguration.telemetry.enabled = YES; - Rollbar.currentConfiguration.telemetry.captureLog = YES; - [Rollbar reapplyConfiguration]; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + config.telemetry.enabled = YES; + config.telemetry.captureLog = YES; + [Rollbar updateWithConfiguration:config]; [RollbarTelemetry.sharedInstance clearAllData]; [NSThread sleepForTimeInterval:2.0f]; diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTests.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTests.m index db7e8dba..8f9c1cc2 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTests.m +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/RollbarTests.m @@ -16,28 +16,41 @@ - (void)setUp { [super setUp]; - [RollbarLogger clearSdkDataStore]; - - if (!Rollbar.currentConfiguration) { - - RollbarConfig *config = [[RollbarConfig alloc] init]; - config.destination.accessToken = [RollbarTestHelper getRollbarPayloadsAccessToken]; - config.destination.environment = [RollbarTestHelper getRollbarEnvironment]; - config.developerOptions.transmit = YES; - config.developerOptions.logPayload = YES; - config.loggingOptions.maximumReportsPerMinute = 5000; - // for the stress test specifically: - config.telemetry.enabled = YES; - config.loggingOptions.captureIp = RollbarCaptureIpType_Full; - NSLog(@"%@", config) - - [Rollbar initWithConfiguration:config]; - } + [RollbarTestUtil deletePayloadsStoreFile]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + RollbarMutableConfig *config = [[RollbarMutableConfig alloc] init]; + config.destination.accessToken = [RollbarTestHelper getRollbarPayloadsAccessToken]; + config.destination.environment = [RollbarTestHelper getRollbarEnvironment]; + config.developerOptions.transmit = YES; + config.developerOptions.logIncomingPayloads = YES; + config.developerOptions.logTransmittedPayloads = YES; + config.developerOptions.logDroppedPayloads = YES; + config.loggingOptions.maximumReportsPerMinute = 5000; + config.telemetry.memoryStatsAutocollectionInterval = 0; + // for the stress test specifically: + config.telemetry.enabled = YES; + config.loggingOptions.captureIp = RollbarCaptureIpType_Full; + NSLog(@"%@", config) + + [Rollbar initWithConfiguration:config]; + + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:2]; + [RollbarTestUtil deleteLogFiles]; + [RollbarTestUtil waitWithWaitTimeInSeconds:1]; + + NSArray *items = [RollbarTestUtil readIncomingPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 0); + items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 0); + items = [RollbarTestUtil readDroppedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, 0); } - (void)tearDown { - [Rollbar updateConfiguration:[RollbarConfig new]]; [super tearDown]; } @@ -45,8 +58,9 @@ - (void)testMultithreadedStressCase { for( int i = 0; i < 20; i++) { dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY,0), ^(){ - RollbarLogger *logger = [[RollbarLogger alloc] initWithAccessToken:Rollbar.currentConfiguration.destination.accessToken]; - logger.configuration.destination.environment = [RollbarTestHelper getRollbarEnvironment]; + RollbarMutableConfig *config = [Rollbar.configuration mutableCopy]; + config.destination.environment = [RollbarTestHelper getRollbarEnvironment]; + RollbarLogger *logger = [[RollbarLogger alloc] initWithConfiguration:config]; for (int j = 0; j < 20; j++) { [logger log:RollbarLevel_Error message:@"error" @@ -65,38 +79,47 @@ - (void)testMultithreadedStressCase { - (void)testRollbarNotifiersIndependentConfiguration { - Rollbar.currentConfiguration.developerOptions.transmit = NO; - Rollbar.currentConfiguration.developerOptions.logPayload = YES; + RollbarMutableConfig *config = [[RollbarMutableConfig alloc] init]; + + config.developerOptions.transmit = NO; + config.developerOptions.logTransmittedPayloads = YES; // configure the root notifier: - Rollbar.currentConfiguration.destination.accessToken = @"AT_0"; - Rollbar.currentConfiguration.destination.environment = @"ENV_0"; + config.destination.accessToken = @"AT_0"; + config.destination.environment = @"ENV_0"; - XCTAssertEqual(Rollbar.currentLogger.configuration.destination.accessToken, - Rollbar.currentConfiguration.destination.accessToken); - XCTAssertEqual(Rollbar.currentLogger.configuration.destination.environment, - Rollbar.currentConfiguration.destination.environment); + [Rollbar updateWithConfiguration:config]; - XCTAssertEqual(Rollbar.currentLogger.configuration.destination.accessToken, - Rollbar.currentConfiguration.destination.accessToken); - XCTAssertEqual(Rollbar.currentLogger.configuration.destination.environment, - Rollbar.currentConfiguration.destination.environment); + XCTAssertTrue([[Rollbar configuration].destination.accessToken + isEqualToString:config.destination.accessToken]); + XCTAssertTrue([[Rollbar configuration].destination.environment + isEqualToString:config.destination.environment]); // create and configure another notifier: - RollbarLogger *notifier = [[RollbarLogger alloc] initWithAccessToken:@"AT_1"]; - notifier.configuration.destination.environment = @"ENV_1"; - XCTAssertTrue([notifier.configuration.destination.accessToken compare:@"AT_1"] == NSOrderedSame); - XCTAssertTrue([notifier.configuration.destination.environment compare:@"ENV_1"] == NSOrderedSame); + config = [RollbarMutableConfig new]; + config.destination.accessToken = @"AT_1"; + config.destination.environment = @"ENV_1"; + RollbarLogger *notifier = [[RollbarLogger alloc] initWithConfiguration:config]; + XCTAssertTrue([notifier.configuration.destination.accessToken + isEqualToString:@"AT_1"]); + XCTAssertTrue([notifier.configuration.destination.environment + isEqualToString:@"ENV_1"]); // reconfigure the root notifier: - Rollbar.currentConfiguration.destination.accessToken = @"AT_N"; - Rollbar.currentConfiguration.destination.environment = @"ENV_N"; - XCTAssertTrue([Rollbar.currentLogger.configuration.destination.accessToken compare:@"AT_N"] == NSOrderedSame); - XCTAssertTrue([Rollbar.currentLogger.configuration.destination.environment compare:@"ENV_N"] == NSOrderedSame); + config = [[Rollbar configuration] mutableCopy]; + config.destination.accessToken = @"AT_N"; + config.destination.environment = @"ENV_N"; + [Rollbar updateWithConfiguration:config]; + XCTAssertTrue([[RollbarInfrastructure sharedInstance].logger.configuration.destination.accessToken + isEqualToString:@"AT_N"]); + XCTAssertTrue([[RollbarInfrastructure sharedInstance].logger.configuration.destination.environment + isEqualToString:@"ENV_N"]); // make sure the other notifier is still has its original configuration: - XCTAssertTrue([notifier.configuration.destination.accessToken compare:@"AT_1"] == NSOrderedSame); - XCTAssertTrue([notifier.configuration.destination.environment compare:@"ENV_1"] == NSOrderedSame); + XCTAssertTrue([notifier.configuration.destination.accessToken + isEqualToString:@"AT_1"]); + XCTAssertTrue([notifier.configuration.destination.environment + isEqualToString:@"ENV_1"]); //TODO: to make this test even more valuable we need to make sure the other notifier's payloads // are actually sent to its intended destination. But that is something we will be able to do @@ -105,20 +128,25 @@ - (void)testRollbarNotifiersIndependentConfiguration { - (void)testRollbarTransmit { - Rollbar.currentConfiguration.destination.accessToken = @"09da180aba21479e9ed3d91e0b8d58d6"; - Rollbar.currentConfiguration.destination.environment = [RollbarTestHelper getRollbarEnvironment]; - Rollbar.currentConfiguration.developerOptions.transmit = YES; + RollbarMutableConfig *config = [[Rollbar configuration] mutableCopy]; + + config.destination.accessToken = @"09da180aba21479e9ed3d91e0b8d58d6"; + config.destination.environment = [RollbarTestHelper getRollbarEnvironment]; + config.developerOptions.transmit = YES; - Rollbar.currentConfiguration.developerOptions.transmit = YES; + config.developerOptions.transmit = YES; + [Rollbar updateWithConfiguration:config]; [Rollbar criticalMessage:@"Transmission test YES"]; [NSThread sleepForTimeInterval:2.0f]; - Rollbar.currentConfiguration.developerOptions.transmit = NO; + config.developerOptions.transmit = NO; + [Rollbar updateWithConfiguration:config]; [Rollbar criticalMessage:@"Transmission test NO"]; [NSThread sleepForTimeInterval:2.0f]; - Rollbar.currentConfiguration.developerOptions.transmit = YES; - //Rollbar.currentConfiguration.enabled = NO; + config.developerOptions.transmit = YES; + //config.enabled = NO; + [Rollbar updateWithConfiguration:config]; [Rollbar criticalMessage:@"Transmission test YES2"]; [NSThread sleepForTimeInterval:2.0f]; @@ -130,10 +158,19 @@ - (void)testRollbarTransmit { count--; } + + //TODO: this test will need asserts added based on content of the local log files... } - (void)testNotification { +// [RollbarLogger flushRollbarThread]; +// [RollbarTestUtil waitWithWaitTimeInSeconds:1]; +// [RollbarTestUtil deleteLogFiles]; +// NSArray *items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; +// XCTAssertEqual(items.count, 0); + //[RollbarTestUtil waitWithWaitTimeInSeconds:1]; + NSDictionary *notificationText = @{ @"error": @[@"testing-error-with-message"], @"debug": @[@"testing-debug"], @@ -154,9 +191,11 @@ - (void)testNotification { } } - [NSThread sleepForTimeInterval:3.0f]; + [RollbarLogger flushRollbarThread]; + [RollbarTestUtil waitWithWaitTimeInSeconds:2]; - NSArray *items = [RollbarLogger readLogItemsFromStore]; + NSArray *items = [RollbarTestUtil readTransmittedPayloadsAsDictionaries]; + XCTAssertEqual(items.count, notificationText.count); for (id item in items) { NSString *level = [item valueForKeyPath:@"level"]; NSString *message = [item valueForKeyPath:@"body.message.body"]; diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.h b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.h new file mode 100644 index 00000000..bfb1d506 --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.h @@ -0,0 +1,32 @@ +// +// XCTestCase+RollbarNotifierTest.h +// +// +// Created by Andrey Kornich on 2022-06-15. +// + +#import + +@class RollbarMutableConfig; +@class RollbarPayloadFactory; +@class RollbarPayload; + +NS_ASSUME_NONNULL_BEGIN + +@interface XCTestCase (RollbarNotifierTest) + +- (nonnull RollbarPayloadFactory *)getPayloadFactory_Live_Default; + +- (nonnull RollbarMutableConfig*) getConfig_Live_Default; + +- (nonnull NSString *)getCrashReportMock; +- (nonnull NSString *)getMessageMock; + +- (nonnull RollbarPayload *)getPayload_CrashReport; +- (nonnull RollbarPayload *)getPayload_Message; +- (nonnull RollbarPayload *)getPayload_Error; +- (nonnull RollbarPayload *)getPayload_Exception; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.m b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.m new file mode 100644 index 00000000..703b434a --- /dev/null +++ b/RollbarNotifier/Tests/RollbarNotifierTests-ObjC/XCTestCase+RollbarNotifierTest.m @@ -0,0 +1,143 @@ +// +// XCTestCase+RollbarNotifierTest.m +// +// +// Created by Andrey Kornich on 2022-06-15. +// + +#import "XCTestCase+RollbarNotifierTest.h" +#import "TestData/CrashReports.h" + +#import "../../Sources/RollbarNotifier/RollbarPayloadFactory.h" + +@import RollbarNotifier; +@import UnitTesting; + +@implementation XCTestCase (RollbarNotifierTest) + +- (nonnull RollbarPayloadFactory *)getPayloadFactory_Live_Default { + + RollbarPayloadFactory *factory = [RollbarPayloadFactory factoryWithConfig:[self getConfig_Live_Default]]; + return factory; +} + +- (nonnull RollbarMutableConfig*) getConfig_Live_Default { + + RollbarMutableConfig *config = + [RollbarConfig mutableConfigWithAccessToken:[RollbarTestHelper getRollbarPayloadsAccessToken] + environment:[RollbarTestHelper getRollbarEnvironment]]; + + return config; +} + +- (nonnull NSString *)getCrashReport_PLCrashReporter_Symbolicated { + + return CRASH_REPORT_PLCRASH_SYMBOLICATED; +} + + +- (nonnull NSString *)getContextMock { + + return @"Unit test context mock..."; +} + +- (nonnull NSDictionary *)getDataMock { + + return @{ + @"data1": @"data 1", + @"data2": @200, + }; +} + +- (nonnull NSString *)getCrashReportMock { + + return [self getCrashReport_PLCrashReporter_Symbolicated]; +} + +- (nonnull NSString *)getMessageMock { + + return @"Unit test message mock..."; +} + +- (nonnull NSError *)getErrorMock { + + NSError *error = [NSError errorWithDomain:@"NSErrorDomain" + code:101 + userInfo:@{ + @"Arror attribute 1":@"attr 1", + @"Arror attribute 2":@202, + }]; + + return error; +} + +- (nonnull NSException *)getExceptionMock { + + NSException *simulatedException = nil; + @try { + [self callWithTrouble]; + } @catch (NSException *exception) { + simulatedException = exception; + } @finally { + return simulatedException; + } +} + +- (void)callWithTrouble { + + [self simulateException]; +} + +- (void)simulateException { + + @throw [NSException exceptionWithName:NSGenericException //@"UnitTestException" + reason:@"simulation for a unit test" + userInfo:@{ + + }]; +} + +#pragma mark - different types of payloads + +- (nonnull RollbarPayload *)getPayload_CrashReport { + + RollbarPayload *payload = + [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Critical + crashReport:[self getCrashReportMock]]; + return payload; +} + +- (nonnull RollbarPayload *)getPayload_Message { + + RollbarPayload *payload = + [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Warning + message:[self getMessageMock] + data:[self getDataMock] + context:[self getContextMock] + ]; + return payload; +} + +- (nonnull RollbarPayload *)getPayload_Error { + + RollbarPayload *payload = + [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Warning + error:[self getErrorMock] + data:[self getDataMock] + context:[self getContextMock] + ]; + return payload; +} + +- (nonnull RollbarPayload *)getPayload_Exception { + + RollbarPayload *payload = + [[self getPayloadFactory_Live_Default] payloadWithLevel:RollbarLevel_Warning + exception:[self getExceptionMock] + data:[self getDataMock] + context:[self getContextMock] + ]; + return payload; +} + +@end diff --git a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigUtilTests.swift b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigUtilTests.swift index 5fde124e..0e36074e 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigUtilTests.swift +++ b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigUtilTests.swift @@ -61,7 +61,7 @@ final class RolllbarNotifierConfigUtilTests: XCTestCase { } func testWithConfigFile() { - var config : RollbarConfig = RollbarConfig(); + var config : RollbarConfig = RollbarMutableConfig(); XCTAssertNotNil(config); do { _ = try RollbarConfigUtil.save(config); diff --git a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigurationTests.swift b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigurationTests.swift index 2e207850..339d3bf9 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigurationTests.swift +++ b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierConfigurationTests.swift @@ -11,33 +11,37 @@ final class RollbarNotifierConfigurationTests: XCTestCase { super.setUp(); - RollbarTestUtil.clearLogFile(); + RollbarTestUtil.deleteLogFiles(); + RollbarTestUtil.deletePayloadsStoreFile(); RollbarTestUtil.clearTelemetryFile(); -// if Rollbar.currentConfiguration() != nil { -// Rollbar.initWithAccessToken(""); -// } - Rollbar.initWithAccessToken(""); - + let config = RollbarMutableConfig(); + config.developerOptions.transmit = false; + config.developerOptions.logIncomingPayloads = true; + config.developerOptions.logTransmittedPayloads = true; + config.developerOptions.logDroppedPayloads = true; + config.loggingOptions.enableOomDetection = false; + Rollbar.initWithConfiguration(config); } override func tearDown() { - Rollbar.updateConfiguration(RollbarConfig()); + super.tearDown(); } func testDefaultRollbarConfiguration() { - let rc = RollbarConfig(); + + let rc = RollbarMutableConfig(); NSLog("%@", rc); } func testTelemetryEnabled() { - RollbarTestUtil.clearLogFile(); - var expectedFlag = false; - Rollbar.currentConfiguration()?.telemetry.enabled = expectedFlag; - Rollbar.reapplyConfiguration(); + + let config = Rollbar.configuration().mutableCopy(); + config.telemetry.enabled = expectedFlag; + Rollbar.update(withConfiguration: config); XCTAssertTrue(RollbarTelemetry.sharedInstance().enabled == expectedFlag, "RollbarTelemetry.sharedInstance.enabled is expected to be NO." @@ -48,8 +52,8 @@ final class RollbarNotifierConfigurationTests: XCTestCase { Rollbar.recordErrorEvent(for: .debug, message: "test"); } - Rollbar.currentConfiguration()?.loggingOptions.maximumReportsPerMinute = max; - Rollbar.reapplyConfiguration(); + config.loggingOptions.maximumReportsPerMinute = max; + Rollbar.update(withConfiguration: config); var telemetryCollection = RollbarTelemetry.sharedInstance().getAllData()!; XCTAssertTrue(telemetryCollection.count == 0, @@ -57,8 +61,8 @@ final class RollbarNotifierConfigurationTests: XCTestCase { ); expectedFlag = true; - Rollbar.currentConfiguration()?.telemetry.enabled = expectedFlag; - Rollbar.reapplyConfiguration(); + config.telemetry.enabled = expectedFlag; + Rollbar.update(withConfiguration: config); XCTAssertTrue(RollbarTelemetry.sharedInstance().enabled == expectedFlag, "RollbarTelemetry.sharedInstance.enabled is expected to be YES." @@ -67,8 +71,8 @@ final class RollbarNotifierConfigurationTests: XCTestCase { Rollbar.recordErrorEvent(for: .debug, message: "test"); } - Rollbar.currentConfiguration()?.loggingOptions.maximumReportsPerMinute = max; - Rollbar.reapplyConfiguration(); + config.loggingOptions.maximumReportsPerMinute = max; + Rollbar.update(withConfiguration: config); telemetryCollection = RollbarTelemetry.sharedInstance().getAllData()!; XCTAssertTrue(telemetryCollection.count == max, @@ -79,27 +83,24 @@ final class RollbarNotifierConfigurationTests: XCTestCase { func testMaximumTelemetryEvents() { - RollbarTestUtil.clearLogFile(); - - Rollbar.currentConfiguration()?.telemetry.enabled = true; - Rollbar.updateConfiguration(Rollbar.currentConfiguration()!); - + let config = Rollbar.configuration().mutableCopy(); + config.telemetry.enabled = true; + Rollbar.update(withConfiguration: config); + let testCount = 10; let max:UInt = 5; for _ in 0.. 0) { Rollbar.criticalMessage("Rate Limit Test \(count)"); - RollbarTestUtil.waitForPesistenceToComplete(); + RollbarTestUtil.wait(waitTimeInSeconds: 1.0); count -= 1; } } func testNotification() { -// RollbarTestUtil.clearLogFile(); -// RollbarTestUtil.clearTelemetryFile(); - let notificationText = [ "error": ["testing-error"], "debug": ["testing-debug"], @@ -140,49 +151,57 @@ final class RollbarNotifierLoggerTests: XCTestCase { } } - RollbarTestUtil.waitForPesistenceToComplete(); + RollbarLogger.flushRollbarThread(); + RollbarTestUtil.wait(waitTimeInSeconds: 6.0); - let items = RollbarTestUtil.readItemStringsFromLogFile(); + let items = RollbarTestUtil.readTransmittedPayloadsAsStrings(); + XCTAssertTrue(items.count >= notificationText.count); + var count:Int = 0; for item in items { + if (!item.contains("testing-")) { + continue; + } let payload = RollbarPayload(jsonString: item); let level = payload.data.level; let message: String? = payload.data.body.message?.body; let params = notificationText[RollbarLevelUtil.rollbarLevel(toString: level)]!; XCTAssertTrue(message!.compare(params[0] as String) == .orderedSame, "Expects '\(params[0])', got '\(message ?? "")'."); + count += 1; } + XCTAssertEqual(count, notificationText.count); } - func testNSErrorReporting() { - do { - try RollbarTestUtil.makeTroubledCall(); - //var expectedErrorCallDepth: uint = 5; - //try RollbarTestUtil.simulateError(callDepth: &expectedErrorCallDepth); - } - catch RollbarTestUtilError.simulatedException(let errorDescription, let errorCallStack) { - print("Caught an error: \(errorDescription)"); - print("Caught error's call stack:"); - errorCallStack.forEach({print($0)}); - } - catch let e as BackTracedErrorProtocol { - //print("Caught an error: \(e.localizedDescription)"); - print("Caught an error: \(e.errorDescription)"); - print("Caught error's call stack:"); - e.errorCallStack.forEach({print($0)}); - } - catch { - print("Caught an error: \(error)"); - //print("Caught an error: \(error.localizedDescription)"); - //print("Corresponding call stack trace at the catch point:"); - Thread.callStackSymbols.forEach{print($0)} - } - } +// func testNSErrorReporting() { +// do { +// try RollbarTestUtil.makeTroubledCall(); +// //var expectedErrorCallDepth: uint = 5; +// //try RollbarTestUtil.simulateError(callDepth: &expectedErrorCallDepth); +// } +// catch RollbarTestUtilError.simulatedException(let errorDescription, let errorCallStack) { +// print("Caught an error: \(errorDescription)"); +// print("Caught error's call stack:"); +// errorCallStack.forEach({print($0)}); +// } +// catch let e as BackTracedErrorProtocol { +// //print("Caught an error: \(e.localizedDescription)"); +// print("Caught an error: \(e.errorDescription)"); +// print("Caught error's call stack:"); +// e.errorCallStack.forEach({print($0)}); +// } +// catch { +// print("Caught an error: \(error)"); +// //print("Caught an error: \(error.localizedDescription)"); +// //print("Corresponding call stack trace at the catch point:"); +// Thread.callStackSymbols.forEach{print($0)} +// } +// } static var allTests = [ ("testRollbarConfiguration", testRollbarConfiguration), ("testRollbarNotifiersIndependentConfiguration", testRollbarNotifiersIndependentConfiguration), ("testRollbarTransmit", testRollbarTransmit), ("testNotification", testNotification), - ("testNSErrorReporting", testNSErrorReporting), + //("testNSErrorReporting", testNSErrorReporting), ] } #endif diff --git a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift index 4684081a..af20fbd6 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift +++ b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift @@ -10,41 +10,41 @@ final class RollbarNotifierTelemetryTests: XCTestCase { override func setUp() { super.setUp(); - RollbarTestUtil.clearLogFile(); + + RollbarTestUtil.deleteLogFiles(); + RollbarTestUtil.deletePayloadsStoreFile(); RollbarTestUtil.clearTelemetryFile(); + + let config = RollbarConfig.mutableConfig( + withAccessToken: RollbarTestHelper.getRollbarPayloadsAccessToken(), + environment: RollbarTestHelper.getRollbarEnvironment() + ); + config.developerOptions.transmit = false; + config.developerOptions.logIncomingPayloads = true; + config.developerOptions.logTransmittedPayloads = true; + config.developerOptions.logDroppedPayloads = true; - if Rollbar.currentConfiguration() != nil { - print("Info: Rollbar already pre-configured!"); - } - else { - Rollbar.initWithAccessToken(RollbarTestHelper.getRollbarPayloadsAccessToken()); - Rollbar.currentConfiguration()?.destination.environment = RollbarTestHelper.getRollbarEnvironment(); - } + Rollbar.update(withConfiguration: config); } override func tearDown() { - Rollbar.updateConfiguration(RollbarConfig()); + super.tearDown(); } func testMemoryTelemetryAutocapture() { - RollbarTestUtil.clearLogFile(); - RollbarTestUtil.clearTelemetryFile(); - - let config = RollbarConfig(); - config.destination.accessToken = RollbarTestHelper.getRollbarPayloadsAccessToken(); - config.destination.environment = RollbarTestHelper.getRollbarEnvironment(); - config.developerOptions.transmit = false; + let config = Rollbar.configuration().mutableCopy(); config.telemetry.enabled = true; config.telemetry.memoryStatsAutocollectionInterval = 0.5; - Rollbar.updateConfiguration(config); + Rollbar.update(withConfiguration: config); - Thread.sleep(forTimeInterval: 5.0); + RollbarTestUtil .wait(waitTimeInSeconds: 3); Rollbar.criticalMessage("Must contain memory telemetry!"); - RollbarTestUtil.waitForPesistenceToComplete(); + RollbarLogger.flushRollbarThread(); - let logItem = RollbarTestUtil.readFirstItemStringsFromLogFile()!; + let logItems = RollbarTestUtil.readIncomingPayloadsAsStrings(); + let logItem = logItems[logItems.count - 1]; let payload = RollbarPayload(jsonString: logItem); let telemetryEvents = payload.data.body.telemetry!; XCTAssertTrue(telemetryEvents.count > 0); @@ -63,35 +63,28 @@ final class RollbarNotifierTelemetryTests: XCTestCase { func testMemoryTelemetryAutocapture_Live() { - RollbarTestUtil.clearLogFile(); - RollbarTestUtil.clearTelemetryFile(); - - let config = RollbarConfig(); + let config = RollbarMutableConfig(); config.destination.accessToken = RollbarTestHelper.getRollbarPayloadsAccessToken(); config.destination.environment = RollbarTestHelper.getRollbarEnvironment(); + config.developerOptions.transmit = true; config.telemetry.enabled = true; config.telemetry.memoryStatsAutocollectionInterval = 0.5; - Rollbar.updateConfiguration(config); + Rollbar.update(withConfiguration: config); - //let resultingConfig = Rollbar.currentConfiguration(); Rollbar.criticalMessage("Rollbar will be testing memory telemetry!"); - RollbarTestUtil.waitForPesistenceToComplete(); - Thread.sleep(forTimeInterval: 2.0); + RollbarTestUtil.wait(waitTimeInSeconds: 2.0); Rollbar.criticalMessage("Must contain memory telemetry!"); - RollbarTestUtil.waitForPesistenceToComplete(); - Thread.sleep(forTimeInterval: 3.0); + RollbarTestUtil.wait(waitTimeInSeconds: 3.0); } func testTelemetryCapture() { - RollbarTestUtil.clearLogFile(); - RollbarTestUtil.clearTelemetryFile(); + let config = Rollbar.configuration().mutableCopy(); + config.telemetry.enabled = true; + Rollbar.update(withConfiguration: config); + RollbarLogger.flushRollbarThread(); - Rollbar.currentConfiguration()?.telemetry.enabled = true; - - Rollbar.updateConfiguration(Rollbar.currentConfiguration()!); - Rollbar.recordNavigationEvent( for: .info, from: "from", @@ -121,13 +114,11 @@ final class RollbarNotifierTelemetryTests: XCTestCase { withData: ["data" : "content"] ); - //RollbarTestUtil.waitForPesistenceToComplete(); - Rollbar.debugMessage("Test"); + RollbarLogger.flushRollbarThread(); - RollbarTestUtil.waitForPesistenceToComplete(); - - let logItem = RollbarTestUtil.readFirstItemStringsFromLogFile()!; + let logItems = RollbarTestUtil.readIncomingPayloadsAsStrings(); + let logItem = logItems[logItems.count - 1]; let payload = RollbarPayload(jsonString: logItem); let telemetryEvents = payload.data.body.telemetry!; XCTAssertTrue(telemetryEvents.count > 0); @@ -175,10 +166,9 @@ final class RollbarNotifierTelemetryTests: XCTestCase { func testErrorReportingWithTelemetry() { - RollbarTestUtil.clearLogFile(); - RollbarTestUtil.clearTelemetryFile(); - - Rollbar.currentConfiguration()!.telemetry.enabled = true; + let config = Rollbar.configuration().mutableCopy(); + config.telemetry.enabled = true; + Rollbar.update(withConfiguration: config); Rollbar.recordNavigationEvent( for: .info, @@ -213,15 +203,12 @@ final class RollbarNotifierTelemetryTests: XCTestCase { withData: ["myTelemetryParameter": "itsValue"] ); - //RollbarTestUtil.waitForPesistenceToComplete(); - Rollbar.debugMessage("Demonstrate Telemetry capture"); Rollbar.debugMessage("Demonstrate Telemetry capture once more..."); Rollbar.debugMessage("DO Demonstrate Telemetry capture once more..."); + RollbarLogger.flushRollbarThread(); - RollbarTestUtil.waitForPesistenceToComplete(); - - let logItems = RollbarTestUtil.readItemStringsFromLogFile(); + let logItems = RollbarTestUtil.readTransmittedPayloadsAsStrings(); for item in logItems { let payload = RollbarPayload(jsonString: item); let telemetryEvents = payload.data.body.telemetry; @@ -267,12 +254,12 @@ final class RollbarNotifierTelemetryTests: XCTestCase { func testTelemetryViewEventScrubbing() { - Rollbar.currentConfiguration()?.telemetry.enabled = true; - Rollbar.currentConfiguration()?.telemetry.viewInputsScrubber.enabled = true; - Rollbar.currentConfiguration()?.telemetry.viewInputsScrubber.scrubFields.append("password"); - Rollbar.currentConfiguration()?.telemetry.viewInputsScrubber.scrubFields.append("pin"); - - Rollbar.updateConfiguration(Rollbar.currentConfiguration()!); + let config = Rollbar.configuration().mutableCopy(); + config.telemetry.enabled = true; + config.telemetry.viewInputsScrubber.enabled = true; + config.telemetry.viewInputsScrubber.scrubFields.add("password"); + config.telemetry.viewInputsScrubber.scrubFields.add("pin"); + Rollbar.update(withConfiguration: config); Rollbar.recordViewEvent( for: .debug, diff --git a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTruncationTests.swift b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTruncationTests.swift index 71016c7e..8b5e7e07 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTruncationTests.swift +++ b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTruncationTests.swift @@ -11,24 +11,13 @@ final class RollbarNotifierTruncationTests: XCTestCase { super.setUp(); - RollbarTestUtil.clearLogFile(); - RollbarTestUtil.clearTelemetryFile(); - - //if Rollbar.currentConfiguration() != nil { - Rollbar.initWithAccessToken(RollbarTestHelper.getRollbarPayloadsAccessToken()); - Rollbar.currentConfiguration()?.destination.environment = RollbarTestHelper.getRollbarEnvironment(); - //} } override func tearDown() { - Rollbar.updateConfiguration(RollbarConfig()); + super.tearDown(); } - func testDefaultRollbarConfiguration() { - NSLog("%@", Rollbar.currentConfiguration()!); - } - func testMeasureTotalEncodingBytes() { let testString1 = "ABCD"; @@ -267,7 +256,6 @@ final class RollbarNotifierTruncationTests: XCTestCase { static var allTests = [ - ("testDefaultRollbarConfiguration", testDefaultRollbarConfiguration), ("testMeasureTotalEncodingBytes", testMeasureTotalEncodingBytes), ("testTruncateStringToTotalBytes", testTruncateStringToTotalBytes), ("testTruncateStringToTotalBytesUnicode", testTruncateStringToTotalBytesUnicode), diff --git a/RollbarPLCrashReporter.podspec b/RollbarPLCrashReporter.podspec index af4fa98a..491a66e9 100644 --- a/RollbarPLCrashReporter.podspec +++ b/RollbarPLCrashReporter.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarPLCrashReporter" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -55,7 +55,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true # s.xcconfig = { # "USE_HEADERMAP" => "NO", diff --git a/RollbarSDK.experimental_podspec b/RollbarSDK.experimental_podspec index 49f14cea..3ed56460 100644 --- a/RollbarSDK.experimental_podspec +++ b/RollbarSDK.experimental_podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |sdk| # Rollbar SDK: # ============ - sdk.version = "2.3.4" + sdk.version = "2.4.0" sdk.name = "RollbarSDK" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." sdk.description = <<-DESC diff --git a/RollbarSDK.xcworkspace/contents.xcworkspacedata b/RollbarSDK.xcworkspace/contents.xcworkspacedata index 0ab21587..51ad510b 100644 --- a/RollbarSDK.xcworkspace/contents.xcworkspacedata +++ b/RollbarSDK.xcworkspace/contents.xcworkspacedata @@ -89,12 +89,6 @@ - - - - @@ -111,6 +105,9 @@ + + diff --git a/RollbarSwift.podspec b/RollbarSwift.podspec index 37e0df2a..684936a7 100644 --- a/RollbarSwift.podspec +++ b/RollbarSwift.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.3.4" + s.version = "2.4.0" s.name = "RollbarSwift" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC @@ -54,7 +54,7 @@ Pod::Spec.new do |s| # s.library = "iconv" # s.libraries = "iconv", "xml2" # s.dependency "JSONKit", "~> 1.4" - + s.requires_arc = true s.xcconfig = { "USE_HEADERMAP" => "NO", diff --git a/UnitTesting/Sources/UnitTesting/RollbarTestUtil.swift b/UnitTesting/Sources/UnitTesting/RollbarTestUtil.swift index 76b47805..a336c6cb 100644 --- a/UnitTesting/Sources/UnitTesting/RollbarTestUtil.swift +++ b/UnitTesting/Sources/UnitTesting/RollbarTestUtil.swift @@ -10,29 +10,57 @@ import RollbarCommon @objc public class RollbarTestUtil: NSObject { - private static let queuedItemsFileName = "rollbar.items"; - private static let queuedItemsStateFileName = "rollbar.state"; + private static let payloadsStore = "rollbar.db"; + + private static let incomingPayloadsLog = "rollbar.incoming"; + private static let transmittedPayloadsLog = "rollbar.transmitted"; + private static let droppedPayloadsLog = "rollbar.dropped"; + private static let telemetryFileName = "rollbar.telemetry"; - private static func getQueuedItemsFilePath() -> String { + private static func getPayloadsStoreFilePath() -> String { let cachesDirectory = RollbarCachesDirectory.directory() ; - let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(queuedItemsFileName); + let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(payloadsStore); + return filePath.path; + } + + private static func getIncomingPayloadsLogFilePath() -> String { + let cachesDirectory = RollbarCachesDirectory.directory() ; + let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(incomingPayloadsLog); return filePath.path; } - private static func getQueuedItemsStateFilePath() -> String { + private static func getTransmittedPayloadsLogFilePath() -> String { let cachesDirectory = RollbarCachesDirectory.directory() ; - let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(queuedItemsStateFileName); + let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(transmittedPayloadsLog); return filePath.path; } + + private static func getDroppedPayloadsLogFilePath() -> String { + let cachesDirectory = RollbarCachesDirectory.directory() ; + let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(droppedPayloadsLog); + return filePath.path; + } + private static func getTelemetryFilePath() -> String { let cachesDirectory = RollbarCachesDirectory.directory() ; let filePath = URL(fileURLWithPath: cachesDirectory).appendingPathComponent(telemetryFileName); return filePath.path; } - @objc public static func clearTelemetryFile() { - let filePath = RollbarTestUtil.getTelemetryFilePath(); + @objc public static func checkFileExists(filePath:String!) -> Bool { + if ((filePath == nil) || filePath.isEmpty) { + return false; + } + let fileManager = FileManager.default; + let fileExists = fileManager.fileExists(atPath: filePath); + return fileExists; + } + + @objc public static func deleteFile(filePath:String!) { + if ((filePath == nil) || filePath.isEmpty) { + return; + } let fileManager = FileManager.default; let fileExists = fileManager.fileExists(atPath: filePath); if fileExists { @@ -41,42 +69,92 @@ import RollbarCommon } catch { print("Unexpected error: \(error).") } + } + } + + @objc public static func clearFile(filePath:String!) { + if ((filePath == nil) || filePath.isEmpty) { + return; + } + let fileManager = FileManager.default; + let fileExists = fileManager.fileExists(atPath: filePath); + if fileExists { + RollbarTestUtil.deleteFile(filePath: filePath); fileManager.createFile(atPath: filePath, contents: nil, attributes: nil); } } - @objc public static func clearLogFile() { - let itemsStateFilePath = RollbarTestUtil.getQueuedItemsStateFilePath(); - let itemsFilePath = RollbarTestUtil.getQueuedItemsFilePath(); + @objc public static func checkPayloadsStoreFileExists() -> Bool { + let filePath = RollbarTestUtil.getPayloadsStoreFilePath(); + return RollbarTestUtil.checkFileExists(filePath: filePath); + } + + @objc public static func deletePayloadsStoreFile() { + let filePath = RollbarTestUtil.getPayloadsStoreFilePath(); + RollbarTestUtil.deleteFile(filePath: filePath); + } + + @objc public static func clearTelemetryFile() { + let filePath = RollbarTestUtil.getTelemetryFilePath(); + RollbarTestUtil.clearFile(filePath: filePath); + } + + @objc public static func deleteLogFiles() { + + let logs : [String] = [ + RollbarTestUtil.getIncomingPayloadsLogFilePath(), + RollbarTestUtil.getTransmittedPayloadsLogFilePath(), + RollbarTestUtil.getDroppedPayloadsLogFilePath(), + ]; + let fileManager = FileManager.default; - do { - if fileManager.fileExists(atPath: itemsStateFilePath) { - try fileManager.removeItem(atPath: itemsStateFilePath); - } - if fileManager.fileExists(atPath: itemsFilePath) { - try fileManager.removeItem(atPath: itemsFilePath); - fileManager.createFile( - atPath: itemsFilePath, - contents: nil, - attributes: nil - ); + + for log in logs { + if fileManager.fileExists(atPath: log) { + RollbarTestUtil.deleteFile(filePath: log); } - } catch { - print("Unexpected error: \(error).") } } + + @objc public static func readFirstIncomingPayloadAsString() -> String? { + + return RollbarTestUtil.readFirstItemStringFromLogFile(filePath: RollbarTestUtil.getIncomingPayloadsLogFilePath()); + } + + @objc public static func readFirstTransmittedPayloadAsString() -> String? { + + return RollbarTestUtil.readFirstItemStringFromLogFile(filePath: RollbarTestUtil.getTransmittedPayloadsLogFilePath()); + } + + @objc public static func readFirstDroppedPayloadAsString() -> String? { + + return RollbarTestUtil.readFirstItemStringFromLogFile(filePath: RollbarTestUtil.getDroppedPayloadsLogFilePath()); + } - @objc public static func readFirstItemStringsFromLogFile() -> String? { + @objc public static func readFirstItemStringFromLogFile(filePath: String) -> String? { - let filePath = RollbarTestUtil.getQueuedItemsFilePath(); let fileReader = RollbarFileReader(filePath: filePath, andOffset: 0); let item = fileReader.readLine(); return item; } - @objc public static func readItemStringsFromLogFile() -> [String] { + @objc public static func readIncomingPayloadsAsStrings() -> [String] { + + return RollbarTestUtil.readItemStringsFromLogFile(filePath: RollbarTestUtil.getIncomingPayloadsLogFilePath()); + } + + @objc public static func readTransmittedPayloadsAsStrings() -> [String] { + + return RollbarTestUtil.readItemStringsFromLogFile(filePath: RollbarTestUtil.getTransmittedPayloadsLogFilePath()); + } + + @objc public static func readDroppedPayloadsAsStrings() -> [String] { + + return RollbarTestUtil.readItemStringsFromLogFile(filePath: RollbarTestUtil.getDroppedPayloadsLogFilePath()); + } + + @objc public static func readItemStringsFromLogFile(filePath: String) -> [String] { - let filePath = RollbarTestUtil.getQueuedItemsFilePath(); let fileReader = RollbarFileReader(filePath: filePath, andOffset: 0); var items = [String](); fileReader.enumerateLines({ (line, nextOffset, stop) in @@ -88,8 +166,23 @@ import RollbarCommon return items; } - @objc public static func readItemsFromLogFile() -> [NSMutableDictionary] { - let filePath = RollbarTestUtil.getQueuedItemsFilePath(); + @objc public static func readIncomingPayloadsAsDictionaries() -> [NSMutableDictionary] { + + return RollbarTestUtil.readItemDictionariesFromLogFile(filePath: RollbarTestUtil.getIncomingPayloadsLogFilePath()); + } + + @objc public static func readTransmittedPayloadsAsDictionaries() -> [NSMutableDictionary] { + + return RollbarTestUtil.readItemDictionariesFromLogFile(filePath: RollbarTestUtil.getTransmittedPayloadsLogFilePath()); + } + + @objc public static func readDroppedPayloadsAsDictionaries() -> [NSMutableDictionary] { + + return RollbarTestUtil.readItemDictionariesFromLogFile(filePath: RollbarTestUtil.getDroppedPayloadsLogFilePath()); + } + + @objc public static func readItemDictionariesFromLogFile(filePath: String) -> [NSMutableDictionary] { + let fileReader = RollbarFileReader(filePath: filePath, andOffset: 0); var items = [NSMutableDictionary] (); fileReader.enumerateLines({ (line, nextOffset, stop) in @@ -114,7 +207,7 @@ import RollbarCommon return items; } - @objc public static func waitForPesistenceToComplete(waitTimeInSeconds: TimeInterval = 0.5) { + @objc public static func wait(waitTimeInSeconds: TimeInterval = 2.0) { Thread.sleep(forTimeInterval: waitTimeInSeconds); }