From 74ecf3bc67f3a28ae33a2b1d43798ef27277d21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 12:17:02 +0300 Subject: [PATCH 01/16] Update to Android 24.4.1 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index e87cd84e..54ccdcec 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,7 +41,7 @@ repositories { dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation 'ly.count.android:sdk:24.4.0' + implementation 'ly.count.android:sdk:24.4.1' // Import the BoM for the Firebase platform // The BoM version of 28.4.2 is the newest release that will target firebase-messaging version 22 From 5ec213e47355c5203dee4cf8f7fa9300a3f17dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 12:24:40 +0300 Subject: [PATCH 02/16] Update iOS to 24.4.1 --- ios/src/CHANGELOG.md | 11 +++ ios/src/Countly-PL.podspec | 2 +- ios/src/Countly.m | 63 +++++++-------- ios/src/Countly.podspec | 2 +- ios/src/Countly.xcodeproj/project.pbxproj | 10 ++- .../UserInterfaceState.xcuserstate | Bin 14499 -> 0 bytes ...454A8605-5E20-454E-97DD-739F4D09680E.plist | 22 ++++++ .../Info.plist | 40 ++++++++++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 -- .../xcschemes/xcschememanagement.plist | 19 ----- ios/src/CountlyCommon.h | 2 +- ios/src/CountlyCommon.m | 37 ++++++++- ios/src/CountlyConnectionManager.m | 24 ++++-- ios/src/CountlyCrashReporter.m | 24 +++--- ios/src/CountlyFeedbackWidget.m | 15 ++++ ios/src/CountlyPerformanceMonitoring.m | 3 +- ios/src/CountlyPersistency.m | 22 +++--- .../CountlyTests/CountlyBaseTestCase.swift | 2 +- .../CountlyConnectionManagerTests.swift | 72 ++++++++++++++++++ ios/src/CountlyTests/CountlyTests.swift | 2 +- ios/src/CountlyUserDetails.m | 52 ++++++++++++- ios/src/CountlyViewTrackingInternal.m | 29 ++++--- ios/src/Package.swift | 1 + ios/src/format.sh | 17 +++++ 24 files changed, 368 insertions(+), 109 deletions(-) delete mode 100644 ios/src/Countly.xcodeproj/project.xcworkspace/xcuserdata/muhammadjunaidakram.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/454A8605-5E20-454E-97DD-739F4D09680E.plist create mode 100644 ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/Info.plist delete mode 100644 ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 ios/src/CountlyTests/CountlyConnectionManagerTests.swift create mode 100755 ios/src/format.sh diff --git a/ios/src/CHANGELOG.md b/ios/src/CHANGELOG.md index 478a8faf..64bde998 100644 --- a/ios/src/CHANGELOG.md +++ b/ios/src/CHANGELOG.md @@ -1,3 +1,14 @@ +## 24.4.1 +* Added support for Feedback Widget terms and conditions + +* Mitigated an issue where SDK limits could affect internal keys +* Mitigated an issue that enabled recording reserved events +* Mitigated an issue where timed events could have no ID +* Mitigated an issue where internal limits were not being applied to some values +* Mitigated an issue where the request queue could overflow while sending a request + +* Removed timestamps from crash breadcrumbs + ## 24.4.0 * Added `attemptToSendStoredRequests` method to combine all events in event queue into a request and attempt to process stored requests * Added the iOS privacy manifest to the Countly SDK diff --git a/ios/src/Countly-PL.podspec b/ios/src/Countly-PL.podspec index 3f661a6a..e1590e8e 100644 --- a/ios/src/Countly-PL.podspec +++ b/ios/src/Countly-PL.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Countly-PL' - s.version = '24.4.0' + s.version = '24.4.1' s.license = { :type => 'MIT', :file => 'LICENSE' } s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.' s.homepage = 'https://github.com/Countly/countly-sdk-ios' diff --git a/ios/src/Countly.m b/ios/src/Countly.m index f5f70233..01f6c2ee 100644 --- a/ios/src/Countly.m +++ b/ios/src/Countly.m @@ -153,7 +153,8 @@ - (void)startWithConfig:(CountlyConfig *)config CountlyCommon.sharedInstance.attributionID = config.attributionID; - CountlyDeviceInfo.sharedInstance.customMetrics = [config.customMetrics cly_truncated:@"Custom metric"]; + NSDictionary* customMetricsTruncated = [config.customMetrics cly_truncated:@"Custom metric"]; + CountlyDeviceInfo.sharedInstance.customMetrics = [customMetricsTruncated cly_limited:@"Custom metric"]; [Countly.user save]; @@ -206,7 +207,7 @@ - (void)startWithConfig:(CountlyConfig *)config #endif #endif - CountlyCrashReporter.sharedInstance.crashSegmentation = [config.crashSegmentation cly_truncated:@"Crash segmentation"]; + CountlyCrashReporter.sharedInstance.crashSegmentation = config.crashSegmentation; CountlyCrashReporter.sharedInstance.crashLogLimit = config.sdkInternalLimits.getMaxBreadcrumbCount; // For backward compatibility, deprecated values are only set incase new values are not provided using sdkInternalLimits interface if(CountlyCrashReporter.sharedInstance.crashLogLimit == kCountlyMaxBreadcrumbCount && config.crashLogLimit != kCountlyMaxBreadcrumbCount) { @@ -759,25 +760,22 @@ - (void)recordEvent:(NSString *)key segmentation:(NSDictionary *)segmentation co { CLY_LOG_I(@"%s %@ %@ %lu %f %f", __FUNCTION__, key, segmentation, (unsigned long)count, sum, duration); + if (!CountlyConsentManager.sharedInstance.consentForEvents) + { + CLY_LOG_W(@"Consent for events not given! Event will not be recorded."); + return; + } + BOOL isReservedEvent = [self isReservedEvent:key]; if (isReservedEvent) { - CLY_LOG_V(@"A reserved event detected: %@", key); - - if (!isReservedEvent) - { - CLY_LOG_W(@"Specific consent not given for the reserved event! So, it will not be recorded."); - return; - } - - CLY_LOG_V(@"Specific consent given for the reserved event! So, it will be recorded."); - } - else if (!CountlyConsentManager.sharedInstance.consentForEvents) - { - CLY_LOG_W(@"Events consent not given! Event will not be recorded."); + CLY_LOG_W(@"A reserved event detected for key: '%@', event will not be recorded.", key); return; } + + NSDictionary* truncated = [segmentation cly_truncated:@"Event segmentation"]; + segmentation = [truncated cly_limited:@"Event segmentation"]; [self recordEvent:key segmentation:segmentation count:count sum:sum duration:duration ID:nil timestamp:CountlyCommon.sharedInstance.uniqueTimestamp]; } @@ -807,7 +805,6 @@ - (void)recordEvent:(NSString *)key segmentation:(NSDictionary *)segmentation co return; CountlyEvent *event = CountlyEvent.new; - event.key = key; event.ID = ID; if (!event.ID.length) { @@ -829,10 +826,11 @@ - (void)recordEvent:(NSString *)key segmentation:(NSDictionary *)segmentation co // If the event is not reserved, assign the previous event ID to the current event's PEID property, or an empty string if previousEventID is nil. Then, update previousEventID to the current event's ID. if (!isReservedEvent) { + key = [key cly_truncatedKey:@"Event key"]; event.PEID = previousEventID ?: @""; previousEventID = event.ID; } - + event.key = key; event.segmentation = segmentation; event.count = MAX(count, 1); event.sum = sum; @@ -846,18 +844,17 @@ - (void)recordEvent:(NSString *)key segmentation:(NSDictionary *)segmentation co - (BOOL)isReservedEvent:(NSString *)key { - NSDictionary * reservedEvents = - @{ - kCountlyReservedEventOrientation: @(CountlyConsentManager.sharedInstance.consentForUserDetails), - kCountlyReservedEventStarRating: @(CountlyConsentManager.sharedInstance.consentForFeedback), - kCountlyReservedEventSurvey: @(CountlyConsentManager.sharedInstance.consentForFeedback), - kCountlyReservedEventNPS: @(CountlyConsentManager.sharedInstance.consentForFeedback), - kCountlyReservedEventPushAction: @(CountlyConsentManager.sharedInstance.consentForPushNotifications), - kCountlyReservedEventView: @(CountlyConsentManager.sharedInstance.consentForViewTracking), - }; + NSArray* reservedEvents = + @[ + kCountlyReservedEventOrientation, + kCountlyReservedEventStarRating, + kCountlyReservedEventSurvey, + kCountlyReservedEventNPS, + kCountlyReservedEventPushAction, + kCountlyReservedEventView, + ]; - NSNumber* aReservedEvent = reservedEvents[key]; - return aReservedEvent.boolValue; + return [reservedEvents containsObject:key]; } #pragma mark - @@ -872,8 +869,6 @@ - (void)startEvent:(NSString *)key CountlyEvent *event = CountlyEvent.new; event.key = key; event.timestamp = CountlyCommon.sharedInstance.uniqueTimestamp; - event.hourOfDay = CountlyCommon.sharedInstance.hourOfDay; - event.dayOfWeek = CountlyCommon.sharedInstance.dayOfWeek; [CountlyPersistency.sharedInstance recordTimedEvent:event]; } @@ -898,12 +893,8 @@ - (void)endEvent:(NSString *)key segmentation:(NSDictionary *)segmentation count return; } - event.segmentation = segmentation; - event.count = MAX(count, 1); - event.sum = sum; - event.duration = NSDate.date.timeIntervalSince1970 - event.timestamp; - - [CountlyPersistency.sharedInstance recordEvent:event]; + NSTimeInterval duration = NSDate.date.timeIntervalSince1970 - event.timestamp; + [self recordEvent:key segmentation:segmentation count:count sum:sum duration:duration]; } - (void)cancelEvent:(NSString *)key diff --git a/ios/src/Countly.podspec b/ios/src/Countly.podspec index 54e7f614..bd9efb1d 100644 --- a/ios/src/Countly.podspec +++ b/ios/src/Countly.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Countly' - s.version = '24.4.0' + s.version = '24.4.1' s.license = { :type => 'MIT', :file => 'LICENSE' } s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.' s.homepage = 'https://github.com/Countly/countly-sdk-ios' diff --git a/ios/src/Countly.xcodeproj/project.pbxproj b/ios/src/Countly.xcodeproj/project.pbxproj index a76ecdf4..c7cddef1 100644 --- a/ios/src/Countly.xcodeproj/project.pbxproj +++ b/ios/src/Countly.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 3B20A9D32245228700E3D7AE /* CountlyPushNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B20A9AE2245228600E3D7AE /* CountlyPushNotifications.h */; }; 3B20A9D42245228700E3D7AE /* CountlyPersistency.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20A9AF2245228600E3D7AE /* CountlyPersistency.m */; }; 3B20A9D62245228700E3D7AE /* CountlyConsentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B20A9B12245228700E3D7AE /* CountlyConsentManager.h */; }; + 968426812BF2302C007B303E /* CountlyConnectionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 968426802BF2302C007B303E /* CountlyConnectionManagerTests.swift */; }; D219374B248AC71C00E5798B /* CountlyPerformanceMonitoring.h in Headers */ = {isa = PBXBuildFile; fileRef = D2193749248AC71C00E5798B /* CountlyPerformanceMonitoring.h */; }; D219374C248AC71C00E5798B /* CountlyPerformanceMonitoring.m in Sources */ = {isa = PBXBuildFile; fileRef = D219374A248AC71C00E5798B /* CountlyPerformanceMonitoring.m */; }; D249BF5E254D3D180058A6C2 /* CountlyFeedbackWidget.h in Headers */ = {isa = PBXBuildFile; fileRef = D249BF5C254D3D170058A6C2 /* CountlyFeedbackWidget.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -131,6 +132,7 @@ 3B20A9AE2245228600E3D7AE /* CountlyPushNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountlyPushNotifications.h; sourceTree = ""; }; 3B20A9AF2245228600E3D7AE /* CountlyPersistency.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountlyPersistency.m; sourceTree = ""; }; 3B20A9B12245228700E3D7AE /* CountlyConsentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountlyConsentManager.h; sourceTree = ""; }; + 968426802BF2302C007B303E /* CountlyConnectionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountlyConnectionManagerTests.swift; sourceTree = ""; }; D2193749248AC71C00E5798B /* CountlyPerformanceMonitoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountlyPerformanceMonitoring.h; sourceTree = ""; }; D219374A248AC71C00E5798B /* CountlyPerformanceMonitoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountlyPerformanceMonitoring.m; sourceTree = ""; }; D249BF5C254D3D170058A6C2 /* CountlyFeedbackWidget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountlyFeedbackWidget.h; sourceTree = ""; }; @@ -164,6 +166,7 @@ 1A5C4C962B35B0850032EE1F /* CountlyTests.swift */, 1A50D7042B3C5AA3009C6938 /* CountlyBaseTestCase.swift */, 1AFD79012B3EF82C00772FBD /* CountlyTests-Bridging-Header.h */, + 968426802BF2302C007B303E /* CountlyConnectionManagerTests.swift */, ); path = CountlyTests; sourceTree = ""; @@ -333,7 +336,7 @@ attributes = { LastSwiftUpdateCheck = 1410; LastUpgradeCheck = 1410; - ORGANIZATIONNAME = "Alin Radut"; + ORGANIZATIONNAME = "Countly"; TargetAttributes = { 1A5C4C932B35B0850032EE1F = { CreatedOnToolsVersion = 14.1; @@ -387,6 +390,7 @@ files = ( 1A5C4C972B35B0850032EE1F /* CountlyTests.swift in Sources */, 1A50D7052B3C5AA3009C6938 /* CountlyBaseTestCase.swift in Sources */, + 968426812BF2302C007B303E /* CountlyConnectionManagerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -634,7 +638,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 24.4.0; + MARKETING_VERSION = 24.4.1; PRODUCT_BUNDLE_IDENTIFIER = ly.count.CountlyiOSSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -666,7 +670,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 24.4.0; + MARKETING_VERSION = 24.4.1; PRODUCT_BUNDLE_IDENTIFIER = ly.count.CountlyiOSSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/ios/src/Countly.xcodeproj/project.xcworkspace/xcuserdata/muhammadjunaidakram.xcuserdatad/UserInterfaceState.xcuserstate b/ios/src/Countly.xcodeproj/project.xcworkspace/xcuserdata/muhammadjunaidakram.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 7038fd5e61549cc528af476b94468603bbe4918b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14499 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 Mihk8lWWD%*00UrhOaK4? diff --git a/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/454A8605-5E20-454E-97DD-739F4D09680E.plist b/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/454A8605-5E20-454E-97DD-739F4D09680E.plist new file mode 100644 index 00000000..2031ab8e --- /dev/null +++ b/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/454A8605-5E20-454E-97DD-739F4D09680E.plist @@ -0,0 +1,22 @@ + + + + + classNames + + CountlyTests + + testPerformanceExample() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.000054 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/Info.plist b/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/Info.plist new file mode 100644 index 00000000..de772be7 --- /dev/null +++ b/ios/src/Countly.xcodeproj/xcshareddata/xcbaselines/1A5C4C932B35B0850032EE1F.xcbaseline/Info.plist @@ -0,0 +1,40 @@ + + + + + runDestinationsByUUID + + 454A8605-5E20-454E-97DD-739F4D09680E + + localComputer + + busSpeedInMHz + 0 + cpuCount + 1 + cpuKind + Apple M1 Pro + cpuSpeedInMHz + 0 + logicalCPUCoresPerPackage + 8 + modelCode + MacBookPro18,3 + physicalCPUCoresPerPackage + 8 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + arm64 + targetDevice + + modelCode + iPhone14,7 + platformIdentifier + com.apple.platform.iphonesimulator + + + + + diff --git a/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index 29befaf8..00000000 --- a/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index e53deb0a..00000000 --- a/ios/src/Countly.xcodeproj/xcuserdata/muhammadjunaidakram.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - SchemeUserState - - Countly.xcscheme_^#shared#^_ - - orderHint - 0 - - CountlyTests.xcscheme_^#shared#^_ - - orderHint - 1 - - - - diff --git a/ios/src/CountlyCommon.h b/ios/src/CountlyCommon.h index 36a8562b..f5088e3a 100644 --- a/ios/src/CountlyCommon.h +++ b/ios/src/CountlyCommon.h @@ -117,7 +117,7 @@ void CountlyPrint(NSString *stringToPrint); #if (TARGET_OS_IOS) -@interface CLYInternalViewController : UIViewController +@interface CLYInternalViewController : UIViewController @property (nonatomic, weak) WKWebView* webView; @end diff --git a/ios/src/CountlyCommon.m b/ios/src/CountlyCommon.m index 75c9ddd2..f2405f25 100644 --- a/ios/src/CountlyCommon.m +++ b/ios/src/CountlyCommon.m @@ -26,7 +26,7 @@ @interface CountlyCommon () #endif @end -NSString* const kCountlySDKVersion = @"24.4.0"; +NSString* const kCountlySDKVersion = @"24.4.1"; NSString* const kCountlySDKName = @"objc-native-ios"; NSString* const kCountlyErrorDomain = @"ly.count.ErrorDomain"; @@ -36,6 +36,8 @@ @interface CountlyCommon () @implementation CountlyCommon +@synthesize lastTimestamp; + static CountlyCommon *s_sharedInstance = nil; static dispatch_once_t onceToken; + (instancetype)sharedInstance @@ -50,7 +52,8 @@ - (instancetype)init { gregorianCalendar = [NSCalendar.alloc initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; startTime = NSDate.date.timeIntervalSince1970; - + + self.lastTimestamp = 0; self.SDKVersion = kCountlySDKVersion; self.SDKName = kCountlySDKName; } @@ -328,11 +331,41 @@ - (void)viewWillLayoutSubviews #pragma GCC diagnostic pop } + self.webView.navigationDelegate = self; frame = UIEdgeInsetsInsetRect(frame, insets); self.webView.frame = frame; } } +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSString *url = navigationAction.request.URL.absoluteString; + if ([url containsString:@"cly_x_int=1"]) { + CLY_LOG_I(@"%s Opening url [%@] in external browser", __FUNCTION__, url); + [[UIApplication sharedApplication] openURL:navigationAction.request.URL options:@{} completionHandler:^(BOOL success) { + if (success) { + CLY_LOG_I(@"%s url [%@] opened in external browser", __FUNCTION__, url); + } + else { + CLY_LOG_I(@"%s unable to open url [%@] in external browser", __FUNCTION__, url); + } + }]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + decisionHandler(WKNavigationActionPolicyAllow); +} + +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation +{ + CLY_LOG_I(@"%s Web view has start loading", __FUNCTION__); + +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + CLY_LOG_I(@"%s Web view has finished loading", __FUNCTION__); +} + + @end diff --git a/ios/src/CountlyConnectionManager.m b/ios/src/CountlyConnectionManager.m index e389c635..50ad12df 100644 --- a/ios/src/CountlyConnectionManager.m +++ b/ios/src/CountlyConnectionManager.m @@ -14,6 +14,8 @@ @interface CountlyConnectionManager () BOOL isSessionStarted; } @property (nonatomic) NSURLSession* URLSession; + +@property (nonatomic, strong) NSDate *startTime; @end NSString* const kCountlyQSKeyAppKey = @"app_key"; @@ -137,37 +139,47 @@ - (void)proceedOnQueue CLY_LOG_D(@"Proceeding on queue is aborted: SDK Networking is disabled from server config!"); return; } - + if (self.connection) { CLY_LOG_D(@"Proceeding on queue is aborted: Already has a request in process!"); return; } - + if (isCrashing) { CLY_LOG_D(@"Proceeding on queue is aborted: Application is crashing!"); return; } - + if (self.isTerminating) { CLY_LOG_D(@"Proceeding on queue is aborted: Application is terminating!"); return; } - + if (CountlyPersistency.sharedInstance.isQueueBeingModified) { CLY_LOG_D(@"Proceeding on queue is aborted: Queue is being modified!"); return; } + + if (!self.startTime) { + self.startTime = [NSDate date]; // Record start time only when it's not already recorded + CLY_LOG_D(@"Proceeding on queue started, queued request count %lu", [CountlyPersistency.sharedInstance remainingRequestCount]); + } NSString* firstItemInQueue = [CountlyPersistency.sharedInstance firstItemInQueue]; if (!firstItemInQueue) { - CLY_LOG_D(@"Queue is empty. All requests are processed."); + // Calculate total time when the queue becomes empty + NSTimeInterval elapsedTime = -[self.startTime timeIntervalSinceNow]; + CLY_LOG_D(@"Queue is empty. All requests are processed. Total time taken: %.2f seconds", elapsedTime); + // Reset start time for future queue processing + self.startTime = nil; return; } + BOOL isOldRequest = [CountlyPersistency.sharedInstance isOldRequest:firstItemInQueue]; if(isOldRequest) { @@ -276,6 +288,7 @@ - (void)proceedOnQueue else { CLY_LOG_D(@"%s, request:[ <%p> ] failed! response:[ %@ ]", __FUNCTION__, request, [data cly_stringUTF8]); + self.startTime = nil; } } else @@ -284,6 +297,7 @@ - (void)proceedOnQueue #if (TARGET_OS_WATCH) [CountlyPersistency.sharedInstance saveToFile]; #endif + self.startTime = nil; } }]; diff --git a/ios/src/CountlyCrashReporter.m b/ios/src/CountlyCrashReporter.m index f3c6e30c..23b56e63 100644 --- a/ios/src/CountlyCrashReporter.m +++ b/ios/src/CountlyCrashReporter.m @@ -218,7 +218,10 @@ - (void)recordException:(NSException *)exception isFatal:(BOOL)isFatal stackTrac { NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithDictionary:exception.userInfo]; userInfo[kCountlyExceptionUserInfoBacktraceKey] = stackTrace; - userInfo[kCountlyExceptionUserInfoSegmentationOverrideKey] = segmentation; + if(segmentation) { + NSDictionary* truncatedSegmentation = [segmentation cly_truncated:@"Exception segmentation"]; + userInfo[kCountlyExceptionUserInfoSegmentationOverrideKey] = [truncatedSegmentation cly_limited:@"Exception segmentation"]; + } exception = [NSException exceptionWithName:exception.name reason:exception.reason userInfo:userInfo]; } @@ -350,21 +353,16 @@ - (void)log:(NSString *)log { if (!CountlyConsentManager.sharedInstance.consentForCrashReporting) return; - - const NSInteger kCountlyCustomCrashLogLengthLimit = 1000; - - if (log.length > kCountlyCustomCrashLogLengthLimit) - log = [log substringToIndex:kCountlyCustomCrashLogLengthLimit]; - - NSString* logWithDateTime = [NSString stringWithFormat:@"<%@> %@",[self.dateFormatter stringFromDate:NSDate.date], log]; + + log = [log cly_truncatedValue:@"Custom Crash log"]; if (self.shouldUsePLCrashReporter) { - [CountlyPersistency.sharedInstance writeCustomCrashLogToFile:logWithDateTime]; + [CountlyPersistency.sharedInstance writeCustomCrashLogToFile:log]; } else { - [self.customCrashLogs addObject:logWithDateTime]; + [self.customCrashLogs addObject:log]; if (self.customCrashLogs.count > self.crashLogLimit) [self.customCrashLogs removeObjectAtIndex:0]; @@ -478,6 +476,12 @@ - (BOOL)isMatchingFilter:(NSString *)string return YES; } +-(void) setCrashSegmentation:(NSDictionary*) crashSegmentation +{ + NSDictionary* truncatedSegmentation = [crashSegmentation cly_truncated:@"Crash segmentation"]; + _crashSegmentation = [truncatedSegmentation cly_limited:@"Crash segmentation"]; +} + @end diff --git a/ios/src/CountlyFeedbackWidget.m b/ios/src/CountlyFeedbackWidget.m index bb609a22..6dadbb3f 100644 --- a/ios/src/CountlyFeedbackWidget.m +++ b/ios/src/CountlyFeedbackWidget.m @@ -199,7 +199,22 @@ - (NSURLRequest *)displayRequest NSString* feedbackTypeEndpoint = [@"/" stringByAppendingString:self.type]; [URL appendString:feedbackTypeEndpoint]; [URL appendFormat:@"?%@", queryString]; + + // customParams is an NSDictionary containing the custom key-value pairs + NSDictionary *customParams = @{@"tc": @"1"}; + + // Build custom parameter string + NSMutableString *customString = [NSMutableString stringWithString:@"&custom="]; + [customString appendString:@"{"]; + [customParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [customString appendFormat:@"%@:%@,", key, obj]; + }]; + [customString deleteCharactersInRange:NSMakeRange(customString.length - 1, 1)]; // Remove the last comma + [customString appendString:@"}"]; + // Append custom parameter + [URL appendString:customString]; + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL]]; return request; } diff --git a/ios/src/CountlyPerformanceMonitoring.m b/ios/src/CountlyPerformanceMonitoring.m index 35fbbf26..3deb4c88 100644 --- a/ios/src/CountlyPerformanceMonitoring.m +++ b/ios/src/CountlyPerformanceMonitoring.m @@ -289,7 +289,8 @@ - (void)endCustomTrace:(NSString *)traceName metrics:(NSDictionary *)metrics } traceName = [traceName cly_truncatedKey:@"Custom trace name"]; - metrics = [metrics cly_truncated:@"Custom trace metric"]; + NSDictionary* metricsTruncated = [metrics cly_truncated:@"Custom trace metric"]; + metrics = [metricsTruncated cly_limited:@"Custom trace metric"]; NSNumber* endTime = @((long long)(CountlyCommon.sharedInstance.uniqueTimestamp * 1000)); diff --git a/ios/src/CountlyPersistency.m b/ios/src/CountlyPersistency.m index 3f64e365..d8bfa012 100644 --- a/ios/src/CountlyPersistency.m +++ b/ios/src/CountlyPersistency.m @@ -28,6 +28,8 @@ @implementation CountlyPersistency NSString* const kCountlyCustomCrashLogFileName = @"CountlyCustomCrash.log"; +NSUInteger const kCountlyRequestRemovalLoopLimit = 100; + static CountlyPersistency* s_sharedInstance = nil; static dispatch_once_t onceToken; @@ -86,15 +88,22 @@ - (void)addToQueue:(NSString *)queryString @synchronized (self) { - [self.queuedRequests addObject:queryString]; - if (self.queuedRequests.count > self.storedRequestsLimit && !CountlyConnectionManager.sharedInstance.connection) + if (self.queuedRequests.count >= self.storedRequestsLimit) { [self removeOldAgeRequestsFromQueue]; - if (self.queuedRequests.count > self.storedRequestsLimit && !CountlyConnectionManager.sharedInstance.connection) + if (self.queuedRequests.count >= self.storedRequestsLimit) { - [self.queuedRequests removeObjectAtIndex:0]; + NSUInteger exceededSize = self.queuedRequests.count - self.storedRequestsLimit; + // we should remove amount of limit at max + // for example if exceeded count is 136 and our limit is 100 we should remove 100 items + // in other case if exceeded count is 36 and out limit is 100 we can only remove 36 items because we have that amount + NSUInteger gonnaRemoveSize = MIN(exceededSize, kCountlyRequestRemovalLoopLimit) + 1; + CLY_LOG_W(@"[CountlyPersistency] addToQueue, request queue size:[ %lu ] exceeded limit:[ %lu ], will remove first:[ %lu ] request(s)", self.queuedRequests.count, self.storedRequestsLimit, gonnaRemoveSize); + NSRange itemsToRemove = NSMakeRange(0, gonnaRemoveSize); + [self.queuedRequests removeObjectsInRange:itemsToRemove]; } } + [self.queuedRequests addObject:queryString]; } } @@ -257,11 +266,6 @@ - (void)recordEvent:(CountlyEvent *)event { @synchronized (self.recordedEvents) { - event.key = [event.key cly_truncatedKey:@"Event key"]; - NSDictionary* truncated = [event.segmentation cly_truncated:@"Event segmentation"]; - NSDictionary* limited = [truncated cly_limited:@"Event segmentation"]; - event.segmentation = limited; - [self.recordedEvents addObject:event]; if (self.recordedEvents.count >= self.eventSendThreshold) diff --git a/ios/src/CountlyTests/CountlyBaseTestCase.swift b/ios/src/CountlyTests/CountlyBaseTestCase.swift index fde220b4..12052126 100644 --- a/ios/src/CountlyTests/CountlyBaseTestCase.swift +++ b/ios/src/CountlyTests/CountlyBaseTestCase.swift @@ -3,7 +3,7 @@ // CountlyTests // // Created by Muhammad Junaid Akram on 27/12/2023. -// Copyright © 2023 Alin Radut. All rights reserved. +// Copyright © 2023 Countly. All rights reserved. // import XCTest diff --git a/ios/src/CountlyTests/CountlyConnectionManagerTests.swift b/ios/src/CountlyTests/CountlyConnectionManagerTests.swift new file mode 100644 index 00000000..e2812d0b --- /dev/null +++ b/ios/src/CountlyTests/CountlyConnectionManagerTests.swift @@ -0,0 +1,72 @@ +// +// CountlyConnectionManagerTests.swift +// CountlyTests +// +// Created by Arif Burak Demiray on 13.05.2024. +// Copyright © 2024 Countly. All rights reserved. +// + +import XCTest +@testable import Countly + +class CountlyConnectionManagerTests: CountlyBaseTestCase { + + /** + *
+     * 1- Init countly with the limit of 250 requests
+     *  - Check RQ is empty
+     * 2- Add 300 requests
+     *  - Check if the first 50 requests are removed
+     *  - Check size is 250
+     * 3- Stop the countly
+     * 4 - Init countly with the limit of 10 requests
+     *  - Check RQ is 250
+     * 5- Add 20 requests
+     *  - On every request addition queue should be dropped to the limit of 10
+     *  - On first one queue should be dropped to the 150
+     *  - On second one queue should be dropped to the 50
+     *  - On third one queue should be dropped to the 10
+     *  - On the last one queue should be size of 10
+     *  
+ */ + func test_addRequest_maxQueueSizeLimit_Scenario() throws { + let config = createBaseConfig() + config.storedRequestsLimit = 250 + config.manualSessionHandling = true + // No Device ID provided during init + Countly.sharedInstance().start(with: config) + + XCTAssertEqual(0, CountlyPersistency.sharedInstance().remainingRequestCount()) + + addRequests(count: 300) + XCTAssertEqual(250, CountlyPersistency.sharedInstance().remainingRequestCount()) + + Countly.sharedInstance().halt(false) + config.storedRequestsLimit = 10 + Countly.sharedInstance().start(with: config) + + XCTAssertEqual(250, CountlyPersistency.sharedInstance().remainingRequestCount()) + + addRequests(count: 1) + XCTAssertEqual(150, CountlyPersistency.sharedInstance().remainingRequestCount()) + + addRequests(count: 1) + XCTAssertEqual(50, CountlyPersistency.sharedInstance().remainingRequestCount()) + + addRequests(count: 1) + XCTAssertEqual(10, CountlyPersistency.sharedInstance().remainingRequestCount()) + + addRequests(count: 17) + XCTAssertEqual(10, CountlyPersistency.sharedInstance().remainingRequestCount()) + + Countly.sharedInstance().halt(true) + + } + + func addRequests(count: Int) { + for loop in 0...count-1 { + CountlyPersistency.sharedInstance().add(toQueue: "&request=REQUEST\(loop)") + } + CountlyPersistency.sharedInstance().saveToFileSync() + } +} diff --git a/ios/src/CountlyTests/CountlyTests.swift b/ios/src/CountlyTests/CountlyTests.swift index d1289132..54fc4168 100644 --- a/ios/src/CountlyTests/CountlyTests.swift +++ b/ios/src/CountlyTests/CountlyTests.swift @@ -3,7 +3,7 @@ // CountlyTests // // Created by Muhammad Junaid Akram on 22/12/2023. -// Copyright © 2023 Alin Radut. All rights reserved. +// Copyright © 2023 Countly. All rights reserved. // import XCTest diff --git a/ios/src/CountlyUserDetails.m b/ios/src/CountlyUserDetails.m index 9cd646f9..23414bd2 100644 --- a/ios/src/CountlyUserDetails.m +++ b/ios/src/CountlyUserDetails.m @@ -91,7 +91,10 @@ - (NSString *)serializedUserDetails userDictionary[kCountlyUDKeyBirthyear] = self.birthYear; if ([self.custom isKindOfClass:NSDictionary.class]) - self.custom = [((NSDictionary *)self.custom) cly_truncated:@"User details custom dictionary"]; + { + NSDictionary* customTruncated = [((NSDictionary *)self.custom) cly_truncated:@"User details custom dictionary"]; + self.custom = [customTruncated cly_limited:@"User details custom dictionary"]; + } if (self.custom) userDictionary[kCountlyUDKeyCustom] = self.custom; @@ -396,9 +399,54 @@ - (void)save [CountlyConnectionManager.sharedInstance sendUserDetails:[@{kCountlyLocalPicturePath: self.pictureLocalPath} cly_JSONify]]; if (self.modifications.count) - [CountlyConnectionManager.sharedInstance sendUserDetails:[@{kCountlyUDKeyCustom: self.modifications} cly_JSONify]]; + { + [CountlyConnectionManager.sharedInstance sendUserDetails:[@{kCountlyUDKeyCustom: [self truncateModifications]} cly_JSONify]]; + } [self clearUserDetails]; } + +- (NSDictionary *)truncateModifications +{ + NSMutableDictionary* truncatedDict = self.modifications.mutableCopy; + [self.modifications enumerateKeysAndObjectsUsingBlock:^(NSString * key, id obj, BOOL * stop) + { + NSString* truncatedKey = [key cly_truncatedKey:@"User details modifications key"]; + if (![truncatedKey isEqualToString:key]) + { + truncatedDict[truncatedKey] = obj; + [truncatedDict removeObjectForKey:key]; + } + + if ([obj isKindOfClass:NSString.class]) + { + NSString* truncatedValue = [obj cly_truncatedValue:@"User details modifications value"]; + if (![truncatedValue isEqualToString:obj]) + { + truncatedDict[truncatedKey] = truncatedValue; + } + } + else if ([obj isKindOfClass:NSDictionary.class]) + { + NSMutableDictionary* truncatedValueDict = ((NSDictionary *)obj).mutableCopy; + [(NSDictionary *)obj enumerateKeysAndObjectsUsingBlock:^(NSString * key, id value, BOOL * stop) + { + if ([value isKindOfClass:NSString.class]) + { + NSString* truncatedValue = [value cly_truncatedValue:@"User details modifications value"]; + if (![truncatedValue isEqualToString:value]) + { + truncatedValueDict[key] = truncatedValue; + truncatedDict[truncatedKey] = truncatedValueDict; + } + } + }]; + } + + }]; + + return truncatedDict.copy; +} + @end diff --git a/ios/src/CountlyViewTrackingInternal.m b/ios/src/CountlyViewTrackingInternal.m index e36ffcb1..1450bfbf 100644 --- a/ios/src/CountlyViewTrackingInternal.m +++ b/ios/src/CountlyViewTrackingInternal.m @@ -345,8 +345,6 @@ - (void)stopViewWithIDInternal:(NSString *) viewKey customSegmentation:(NSDictio if (viewData) { NSMutableDictionary* segmentation = NSMutableDictionary.new; - segmentation[kCountlyVTKeyName] = viewData.viewName; - segmentation[kCountlyVTKeySegment] = CountlyDeviceInfo.osName; if (viewData.segmentation) { @@ -365,6 +363,12 @@ - (void)stopViewWithIDInternal:(NSString *) viewKey customSegmentation:(NSDictio [segmentation addEntriesFromDictionary:mutableCustomSegmentation]; } + NSDictionary* segmentationTruncated = [segmentation cly_truncated:@"View segmentation"]; + segmentation = [segmentationTruncated cly_limited:@"View segmentation"].mutableCopy; + + segmentation[kCountlyVTKeyName] = viewData.viewName; + segmentation[kCountlyVTKeySegment] = CountlyDeviceInfo.osName; + NSTimeInterval duration = viewData.duration; [Countly.sharedInstance recordReservedEvent:kCountlyReservedEventView segmentation:segmentation count:1 sum:0 duration:duration ID:viewData.viewID timestamp:CountlyCommon.sharedInstance.uniqueTimestamp]; @@ -403,15 +407,6 @@ - (NSString*)startViewInternal:(NSString *)viewName customSegmentation:(NSDictio viewName = [viewName cly_truncatedKey:@"View name"]; NSMutableDictionary* segmentation = NSMutableDictionary.new; - segmentation[kCountlyVTKeyName] = viewName; - segmentation[kCountlyVTKeySegment] = CountlyDeviceInfo.osName; - segmentation[kCountlyVTKeyVisit] = @1; - - if (self.isFirstView) - { - self.isFirstView = NO; - segmentation[kCountlyVTKeyStart] = @1; - } if (self.viewSegmentation) { @@ -425,6 +420,18 @@ - (NSString*)startViewInternal:(NSString *)viewName customSegmentation:(NSDictio [segmentation addEntriesFromDictionary:mutableCustomSegmentation]; } + NSDictionary* segmentationTruncated = [segmentation cly_truncated:@"View segmentation"]; + segmentation = [segmentationTruncated cly_limited:@"View segmentation"].mutableCopy; + + segmentation[kCountlyVTKeyName] = viewName; + segmentation[kCountlyVTKeySegment] = CountlyDeviceInfo.osName; + segmentation[kCountlyVTKeyVisit] = @1; + + if (self.isFirstView) + { + self.isFirstView = NO; + segmentation[kCountlyVTKeyStart] = @1; + } self.previousViewID = self.currentViewID; self.currentViewID = CountlyCommon.sharedInstance.randomEventID; diff --git a/ios/src/Package.swift b/ios/src/Package.swift index 70ecfc6f..10bf0da8 100644 --- a/ios/src/Package.swift +++ b/ios/src/Package.swift @@ -36,6 +36,7 @@ let package = Package( "LICENSE", "README.md", "countly_dsym_uploader.sh", + "format.sh", "CHANGELOG.md", "SECURITY.md", "CountlyTests/" diff --git a/ios/src/format.sh b/ios/src/format.sh new file mode 100755 index 00000000..a3ee02f3 --- /dev/null +++ b/ios/src/format.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +CDP=$(osascript -e ' +tell application "Xcode" + activate + --wait for Xcode to remove edited flag from filename + delay 0.3 + set last_word_in_main_window to (word -1 of (get name of window 1)) + set current_document to document 1 whose name ends with last_word_in_main_window + set current_document_path to path of current_document + --CDP is assigned last set value: current_document_path +end tell ') + +sleep 0.6 ### during save Xcode stops listening for file changes +/usr/local/bin/clang-format -style=file -i ${CDP} + +# EOF From e932760405a475ff91f74bc5c26b78e350d1fe98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 12:40:23 +0300 Subject: [PATCH 03/16] Updating Versions --- CHANGELOG.md | 4 ++++ CountlyReactNative.podspec | 2 +- .../java/ly/count/android/sdk/react/CountlyReactNative.java | 2 +- ios/src/CountlyReactNative.m | 2 +- package.json | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1cd50b..9e727dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 24.4.1 +* Updated the underlying Android SDK version to 24.4.1 +* Updated the underlying iOS SDK version to 24.4.1 + ## 24.4.0 * ! Minor breaking change ! Tracking of foreground and background time for APM is disabled by default diff --git a/CountlyReactNative.podspec b/CountlyReactNative.podspec index d32be47c..1688a4dd 100644 --- a/CountlyReactNative.podspec +++ b/CountlyReactNative.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'CountlyReactNative' - s.version = '24.4.0' + s.version = '24.4.1' s.license = { :type => 'COMMUNITY', :text => <<-LICENSE diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index 434e5d3e..5bbfc885 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -89,7 +89,7 @@ public String toString() { public class CountlyReactNative extends ReactContextBaseJavaModule implements LifecycleEventListener { public static final String TAG = "CountlyRNPlugin"; - private String COUNTLY_RN_SDK_VERSION_STRING = "24.4.0"; + private String COUNTLY_RN_SDK_VERSION_STRING = "24.4.1"; private String COUNTLY_RN_SDK_NAME = "js-rnb-android"; private static final CountlyConfig config = new CountlyConfig(); diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index 62ce9978..81a39f00 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -24,7 +24,7 @@ @interface CountlyFeedbackWidget () + (CountlyFeedbackWidget *)createWithDictionary:(NSDictionary *)dictionary; @end -NSString *const kCountlyReactNativeSDKVersion = @"24.4.0"; +NSString *const kCountlyReactNativeSDKVersion = @"24.4.1"; NSString *const kCountlyReactNativeSDKName = @"js-rnb-ios"; CLYPushTestMode const CLYPushTestModeProduction = @"CLYPushTestModeProduction"; diff --git a/package.json b/package.json index 1a613167..a0469c9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "countly-sdk-react-native-bridge", - "version": "24.4.0", + "version": "24.4.1", "author": "Countly (https://count.ly/)", "bugs": { "url": "https://github.com/Countly/countly-sdk-react-native-bridge/issues" From c74ab79d7d68bb72af6a92f32f116ac57f1a4690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 13:43:31 +0300 Subject: [PATCH 04/16] Update CHANGELOG.md --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e727dd6..3eee6cae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ ## 24.4.1 * Updated the underlying Android SDK version to 24.4.1 +* ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply +* Added support for Feedback Widget terms and conditions +* Added a new function "setID(newDeviceId)" which internally handles merge/non-merge selection while changing the device ID +* Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge +* Mitigated an issue where a session could have continued after a device ID change without merge + * Updated the underlying iOS SDK version to 24.4.1 +* Added support for Feedback Widget terms and conditions +* Mitigated an issue where SDK limits could affect internal keys +* Mitigated an issue that enabled recording reserved events +* Mitigated an issue where timed events could have no ID +* Mitigated an issue where internal limits were not being applied to some values +* Mitigated an issue where the request queue could overflow while sending a request +* Removed timestamps from crash breadcrumbs ## 24.4.0 * ! Minor breaking change ! Tracking of foreground and background time for APM is disabled by default From e2c7cfb871258d279822864e5dcde3c92bed1ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 13:48:26 +0300 Subject: [PATCH 05/16] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eee6cae..4302b428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ * Updated the underlying Android SDK version to 24.4.1 * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Added support for Feedback Widget terms and conditions -* Added a new function "setID(newDeviceId)" which internally handles merge/non-merge selection while changing the device ID * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge * Mitigated an issue where a session could have continued after a device ID change without merge From a65dc472a2a25b6e13dfc34605cd9e6d2234b620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 13:52:26 +0300 Subject: [PATCH 06/16] Update CHANGELOG.md --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4302b428..a2f85326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,8 @@ ## 24.4.1 -* Updated the underlying Android SDK version to 24.4.1 * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Added support for Feedback Widget terms and conditions * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge * Mitigated an issue where a session could have continued after a device ID change without merge - -* Updated the underlying iOS SDK version to 24.4.1 -* Added support for Feedback Widget terms and conditions * Mitigated an issue where SDK limits could affect internal keys * Mitigated an issue that enabled recording reserved events * Mitigated an issue where timed events could have no ID @@ -14,6 +10,9 @@ * Mitigated an issue where the request queue could overflow while sending a request * Removed timestamps from crash breadcrumbs +* Updated the underlying Android SDK version to 24.4.1 +* Updated the underlying iOS SDK version to 24.4.1 + ## 24.4.0 * ! Minor breaking change ! Tracking of foreground and background time for APM is disabled by default From 0ea506696793841db9412cde320f9fcc460a7957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 14:08:26 +0300 Subject: [PATCH 07/16] Update CHANGELOG.md --- CHANGELOG.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f85326..6c16b290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,15 @@ ## 24.4.1 * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Added support for Feedback Widget terms and conditions -* Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge -* Mitigated an issue where a session could have continued after a device ID change without merge -* Mitigated an issue where SDK limits could affect internal keys -* Mitigated an issue that enabled recording reserved events -* Mitigated an issue where timed events could have no ID -* Mitigated an issue where internal limits were not being applied to some values -* Mitigated an issue where the request queue could overflow while sending a request -* Removed timestamps from crash breadcrumbs - +* Android: + * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge + * Mitigated an issue where a session could have continued after a device ID change without merge +* iOS: + * Mitigated an issue where SDK limits could affect internal keys + * Mitigated an issue that enabled recording reserved events + * Mitigated an issue where timed events could have no ID + * Mitigated an issue where the request queue could overflow while sending a request + * Removed timestamps from crash breadcrumbs * Updated the underlying Android SDK version to 24.4.1 * Updated the underlying iOS SDK version to 24.4.1 From e5131ab9b51ac59196eea1736629836b5b125d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 14:20:40 +0300 Subject: [PATCH 08/16] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c16b290..a62ae424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 24.4.1 -* ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Added support for Feedback Widget terms and conditions * Android: + * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge * Mitigated an issue where a session could have continued after a device ID change without merge * iOS: + * Mitigated an issue where internal limits were not being applied to some values * Mitigated an issue where SDK limits could affect internal keys * Mitigated an issue that enabled recording reserved events * Mitigated an issue where timed events could have no ID From e99e4cae2674001371841c72db6b46d6059c5b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 14:22:08 +0300 Subject: [PATCH 09/16] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a62ae424..b3f93183 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ ## 24.4.1 * Added support for Feedback Widget terms and conditions + * Android: * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge * Mitigated an issue where a session could have continued after a device ID change without merge + * iOS: * Mitigated an issue where internal limits were not being applied to some values * Mitigated an issue where SDK limits could affect internal keys @@ -11,6 +13,7 @@ * Mitigated an issue where timed events could have no ID * Mitigated an issue where the request queue could overflow while sending a request * Removed timestamps from crash breadcrumbs + * Updated the underlying Android SDK version to 24.4.1 * Updated the underlying iOS SDK version to 24.4.1 From 9047bfa5f8574c25ffafc71137644c7f0ef93717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 15:39:01 +0300 Subject: [PATCH 10/16] SDK Internal Limits --- .vscode/settings.json | 2 +- CountlyConfig.js | 6 ++ Utils.js | 46 +++++++--- .../android/sdk/react/CountlyReactNative.java | 70 +++++++++++++++ .../countly_config_limits.js | 89 +++++++++++++++++++ 5 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 lib/configuration_interfaces/countly_config_limits.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 2d17cc55..04b5ffcd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "editor.formatOnSave": true, "cSpell.words": ["Countly"], "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.typescript-language-features" }, "java.configuration.updateBuildConfiguration": "interactive" } diff --git a/CountlyConfig.js b/CountlyConfig.js index 1f8395d7..1ebd368d 100644 --- a/CountlyConfig.js +++ b/CountlyConfig.js @@ -1,5 +1,6 @@ import { initialize } from "./Logger.js"; import CountlyConfigApm from "./lib/configuration_interfaces/countly_config_apm.js"; +import CountlyConfigSDKInternalLimits from "./lib/configuration_interfaces/countly_config_limits.js"; /** * Countly SDK React Native Bridge * https://github.com/Countly/countly-sdk-react-native-bridge @@ -18,6 +19,7 @@ class CountlyConfig { this.serverURL = serverURL; this.appKey = appKey; this._countlyConfigApmInstance = new CountlyConfigApm(); + this._countlyConfigSDKLimitsInstance = new CountlyConfigSDKInternalLimits(); } /** @@ -27,6 +29,10 @@ class CountlyConfig { return this._countlyConfigApmInstance; } + get limits() { + return this._countlyConfigSDKLimitsInstance; + } + /** * Method to set the server url * diff --git a/Utils.js b/Utils.js index 74f6bc86..91953ab0 100644 --- a/Utils.js +++ b/Utils.js @@ -17,19 +17,19 @@ const DeviceIdType = { function intToDeviceIDType(deviceIdType) { let result = null; switch (deviceIdType) { - case 10101: - result = DeviceIdType.SDK_GENERATED; - break; - case 20202: - result = DeviceIdType.DEVELOPER_SUPPLIED; - break; - case 30303: - result = DeviceIdType.TEMPORARY_ID; - break; - default: - L.e("_getDeviceIdType, " + `unexpected deviceIdType [${deviceIdType}] from native side`); - result = DeviceIdType.SDK_GENERATED; - break; + case 10101: + result = DeviceIdType.SDK_GENERATED; + break; + case 20202: + result = DeviceIdType.DEVELOPER_SUPPLIED; + break; + case 30303: + result = DeviceIdType.TEMPORARY_ID; + break; + default: + L.e("_getDeviceIdType, " + `unexpected deviceIdType [${deviceIdType}] from native side`); + result = DeviceIdType.SDK_GENERATED; + break; } L.d(`_getDeviceIdType, DeviceIDType: ${result}`); return result; @@ -134,6 +134,26 @@ function configToJson(config) { if (config.attributionValues) { json.attributionValues = config.attributionValues; } + // Limits ----------------------------------------------- + if (config.limits.maxKeyLength) { + json.maxKeyLength = config.limits.maxKeyLength; + } + if (config.limits.maxValueSize) { + json.maxValueSize = config.limits.maxValueSize; + } + if (config.limits.maxSegmentationValues) { + json.maxSegmentationValues = config.limits.maxSegmentationValues; + } + if (config.limits.maxBreadcrumbCount) { + json.maxBreadcrumbCount = config.limits.maxBreadcrumbCount; + } + if (config.limits.maxStackTraceLinesPerThread) { + json.maxStackTraceLinesPerThread = config.limits.maxStackTraceLinesPerThread; + } + if (config.limits.maxStackTraceLineLength) { + json.maxStackTraceLineLength = config.limits.maxStackTraceLineLength; + } + // Limits End -------------------------------------------- } catch (err) { L.e(`configToJson, Exception occured during converting config to json.${err.toString()}`); } diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index 434e5d3e..f120ae50 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -113,6 +113,14 @@ public class CountlyReactNative extends ReactContextBaseJavaModule implements Li private static String pushNotificationCallbackName = "pushNotificationCallback"; private List retrievedWidgetList = null; + // SDK Limit Defaults + final int maxKeyLengthDefault = 128; + final int maxValueSizeDefault = 256; + final int maxSegmentationValuesDefault = 100; + final int maxBreadcrumbCountDefault = 100; + final int maxStackTraceLinesPerThreadDefault = 30; + final int maxStackTraceLineLengthDefault = 200; + private final Set validConsentFeatureNames = new HashSet<>(Arrays.asList( Countly.CountlyFeatureNames.sessions, Countly.CountlyFeatureNames.events, @@ -235,6 +243,68 @@ private void populateConfig(JSONObject _config) { config.setRecordAppStartTime(_config.getBoolean("enableApm")); } // APM END -------------------------------------------- + // Limits ----------------------------------------------- + if(_config.has("maxKeyLength")) { + if (_config.getInt("maxKeyLength") < 1) { + log("[Init] provided 'maxKeyLength' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxKeyLength(1); + } + config.limits.setMaxKeyLength(_config.getInt("maxKeyLength")); + } + else { + config.limits.setMaxKeyLength(maxKeyLengthDefault); + } + if(_config.has("maxValueSize")) { + if (_config.getInt("maxValueSize") < 1) { + log("[Init] provided 'maxValueSize' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxValueSize(1); + } + config.limits.setMaxValueSize(_config.getInt("maxValueSize")); + } + else { + config.limits.setMaxValueSize(maxValueSizeDefault); + } + if(_config.has("maxSegmentationValues")) { + if (_config.getInt("maxSegmentationValues") < 1) { + log("[Init] provided 'maxSegmentationValues' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxSegmentationValues(1); + } + config.limits.setMaxSegmentationValues(_config.getInt("maxSegmentationValues")); + } + else { + config.limits.setMaxSegmentationValues(maxSegmentationValuesDefault); + } + if(_config.has("maxBreadcrumbCount")) { + if (_config.getInt("maxBreadcrumbCount") < 1) { + log("[Init] provided 'maxBreadcrumbCount' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxBreadcrumbCount(1); + } + config.limits.setMaxBreadcrumbCount(_config.getInt("maxBreadcrumbCount")); + } + else { + config.limits.setMaxBreadcrumbCount(maxBreadcrumbCountDefault); + } + if(_config.has("maxStackTraceLinesPerThread")) { + if (_config.getInt("maxStackTraceLinesPerThread") < 1) { + log("[Init] provided 'maxStackTraceLinesPerThread' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxStackTraceLinesPerThread(1); + } + config.limits.setMaxStackTraceLinesPerThread(_config.getInt("maxStackTraceLinesPerThread")); + } + else { + config.limits.setMaxStackTraceLinesPerThread(maxStackTraceLinesPerThreadDefault); + } + if(_config.has("maxStackTraceLineLength")) { + if (_config.getInt("maxStackTraceLineLength") < 1) { + log("[Init] provided 'maxStackTraceLineLength' is less than '1'. Setting it to '1'.", LogLevel.WARNING); + config.limits.setMaxStackTraceLineLength(1); + } + config.limits.setMaxStackTraceLineLength(_config.getInt("maxStackTraceLineLength")); + } + else { + config.limits.setMaxStackTraceLineLength(maxStackTraceLineLengthDefault); + } + // Limits End ------------------------------------------- if (_config.has("crashReporting")) { config.enableCrashReporting(); } diff --git a/lib/configuration_interfaces/countly_config_limits.js b/lib/configuration_interfaces/countly_config_limits.js new file mode 100644 index 00000000..ed410a86 --- /dev/null +++ b/lib/configuration_interfaces/countly_config_limits.js @@ -0,0 +1,89 @@ +/** + * Countly SDK React Native Bridge SDK Internal Limits + * https://github.com/Countly/countly-sdk-react-native-bridge + * @Countly + */ + +// This class holds SDK internal limits (https://support.count.ly/hc/en-us/articles/360037753291-SDK-development-guide#01H821RTQ7AZ6J858BHP4883ZC) specific configurations to be used with CountlyConfig class and serves as an interface. +// You can chain multiple configurations. +class CountlyConfigSDKInternalLimits { + constructor() { + _maxKeyLength = 0; + _maxValueSize = 0; + _maxSegmentationValues = 0; + _maxBreadcrumbCount = 0; + _maxStackTraceLinesPerThread = 0; + _maxStackTraceLineLength = 0; + } + + // getters + get maxKeyLength() { + return this._maxKeyLength; + } + + get maxValueSize() { + return this._maxValueSize; + } + + get maxSegmentationValues() { + return this._maxSegmentationValues; + } + + get maxBreadcrumbCount() { + return this._maxBreadcrumbCount; + } + + get maxStackTraceLinesPerThread() { + return this._maxStackTraceLinesPerThread; + } + + get maxStackTraceLineLength() { + return this._maxStackTraceLineLength; + } + + // setters / methods + + // Limits the maximum size of all string keys + // keyLengthLimit is the maximum char size of all string keys (default 128 chars) + setMaxKeyLength(keyLengthLimit) { + this._maxKeyLength = keyLengthLimit; + return this; + } + + // Limits the size of all values in segmentation key-value pairs + // valueSizeLimit is the maximum char size of all values in our key-value pairs (default 256 chars) + setMaxValueSize(valueSizeLimit) { + this._maxValueSize = valueSizeLimit; + return this; + } + + // Limits the max amount of custom segmentation in one event + // segmentationAmountLimit is the max amount of custom segmentation in one event (default 100 key-value pairs) + setMaxSegmentationValues(segmentationAmountLimit) { + this._maxSegmentationValues = segmentationAmountLimit; + return this; + } + + // Limits the max amount of breadcrumbs that can be recorded before the oldest one is deleted + // breadcrumbCountLimit is the max amount of breadcrumbs that can be recorded before the oldest one is deleted (default 100) + setMaxBreadcrumbCount(breadcrumbCountLimit) { + this._maxBreadcrumbCount = breadcrumbCountLimit; + return this; + } + + // Limits the max amount of stack trace lines to be recorded per thread + // stackTraceLinesPerThreadLimit is the max amount of stack trace lines to be recorded per thread (default 30) + setMaxStackTraceLinesPerThread(stackTraceLinesPerThreadLimit) { + this._maxStackTraceLinesPerThread = stackTraceLinesPerThreadLimit; + return this; + } + + // Limits the max characters allowed per stack trace lines. Also limits the crash message length + // stackTraceLineLengthLimit is the max length of each stack trace line (default 200) + setMaxStackTraceLineLength(stackTraceLineLengthLimit) { + this._maxStackTraceLineLength = stackTraceLineLengthLimit; + return this; + } +} + +export default CountlyConfigSDKInternalLimits; \ No newline at end of file From 9c81d2b6b38d1f0f57633698598e2c99a2df4354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 15:51:47 +0300 Subject: [PATCH 11/16] ios limit --- ios/src/CountlyReactNative.m | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index 62ce9978..bed3b9e6 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -50,6 +50,14 @@ + (CountlyFeedbackWidget *)createWithDictionary:(NSDictionary *)dictionary; NSString *const ratingWidgetCallbackName = @"ratingWidgetCallback"; NSString *const pushNotificationCallbackName = @"pushNotificationCallback"; +// SDK Limit Defaults +const int maxKeyLengthDefault = 150; +const int maxValueSizeDefault = 200; +const int maxSegmentationValuesDefault = 120; +const int maxBreadcrumbCountDefault = 120; +const int maxStackTraceLinesPerThreadDefault = 50; +const int maxStackTraceLineLengthDefault = 300; + @implementation CountlyReactNative NSString *const kCountlyNotificationPersistencyKey = @"kCountlyNotificationPersistencyKey"; @@ -147,7 +155,67 @@ - (void) populateConfig:(id) json { if (json[@"starRatingTextMessage"]) { config.starRatingMessage = json[@"starRatingTextMessage"]; } + // Limits ----------------------------------------------- + // maxKeyLength + if (json[@"maxKeyLength"]) { + if ([[_config objectForKey:@"maxKeyLength"] intValue] < 1) { + [config.limits setMaxKeyLength:1]; + } + [config.limits setMaxKeyLength:[[_config objectForKey:@"maxKeyLength"] intValue]]; + } else { + [config.limits setMaxKeyLength:maxKeyLengthDefault]; + } + + // maxValueSize + if (json[@"maxValueSize"]) { + if ([[_config objectForKey:@"maxValueSize"] intValue] < 1) { + [config.limits setMaxValueSize:1]; + } + [config.limits setMaxValueSize:[[_config objectForKey:@"maxValueSize"] intValue]]; + } else { + [config.limits setMaxValueSize:maxValueSizeDefault]; + } + + // maxSegmentationValues + if (json[@"maxSegmentationValues"]) { + if ([[_config objectForKey:@"maxSegmentationValues"] intValue] < 1) { + [config.limits setMaxSegmentationValues:1]; + } + [config.limits setMaxSegmentationValues:[[_config objectForKey:@"maxSegmentationValues"] intValue]]; + } else { + [config.limits setMaxSegmentationValues:maxSegmentationValuesDefault]; + } + // maxBreadcrumbCount + if (json[@"maxBreadcrumbCount"]) { + if ([[_config objectForKey:@"maxBreadcrumbCount"] intValue] < 1) { + [config.limits setMaxBreadcrumbCount:1]; + } + [config.limits setMaxBreadcrumbCount:[[_config objectForKey:@"maxBreadcrumbCount"] intValue]]; + } else { + [config.limits setMaxBreadcrumbCount:maxBreadcrumbCountDefault]; + } + + // maxStackTraceLinesPerThread + if (json[@"maxStackTraceLinesPerThread"]) { + if ([[_config objectForKey:@"maxStackTraceLinesPerThread"] intValue] < 1) { + [config.limits setMaxStackTraceLinesPerThread:1]; + } + [config.limits setMaxStackTraceLinesPerThread:[[_config objectForKey:@"maxStackTraceLinesPerThread"] intValue]]; + } else { + [config.limits setMaxStackTraceLinesPerThread:maxStackTraceLinesPerThreadDefault]; + } + + // maxStackTraceLineLength + if (json[@"maxStackTraceLineLength"]) { + if ([[_config objectForKey:@"maxStackTraceLineLength"] intValue] < 1) { + [config.limits setMaxStackTraceLineLength:1]; + } + [config.limits setMaxStackTraceLineLength:[[_config objectForKey:@"maxStackTraceLineLength"] intValue]]; + } else { + [config.limits setMaxStackTraceLineLength:maxStackTraceLineLengthDefault]; + } + // Limits End ------------------------------------------- // APM ------------------------------------------------ NSNumber *enableForegroundBackground = json[@"enableForegroundBackground"]; if (enableForegroundBackground) { From 3c319868b80db0006a4616895b0c1a3a3260e080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Wed, 25 Sep 2024 16:32:23 +0300 Subject: [PATCH 12/16] Refactoring Limits --- Utils.js | 36 ++++++++++--- .../android/sdk/react/CountlyReactNative.java | 50 ------------------- ios/src/CountlyReactNative.m | 43 ---------------- 3 files changed, 30 insertions(+), 99 deletions(-) diff --git a/Utils.js b/Utils.js index 91953ab0..e32bb3e1 100644 --- a/Utils.js +++ b/Utils.js @@ -136,22 +136,46 @@ function configToJson(config) { } // Limits ----------------------------------------------- if (config.limits.maxKeyLength) { - json.maxKeyLength = config.limits.maxKeyLength; + if (config.limits.maxKeyLength < 1) { + L.w(`configToJson, Provided value for maxKeyLength is invalid!`) + } else { + json.maxKeyLength = config.limits.maxKeyLength; + } } if (config.limits.maxValueSize) { - json.maxValueSize = config.limits.maxValueSize; + if (config.limits.maxValueSize < 1) { + L.w(`configToJson, Provided value for maxValueSize is invalid!`) + } else { + json.maxValueSize = config.limits.maxValueSize; + } } if (config.limits.maxSegmentationValues) { - json.maxSegmentationValues = config.limits.maxSegmentationValues; + if (config.limits.maxSegmentationValues < 1) { + L.w(`configToJson, Provided value for maxSegmentationValues is invalid!`) + } else { + json.maxSegmentationValues = config.limits.maxSegmentationValues; + } } if (config.limits.maxBreadcrumbCount) { - json.maxBreadcrumbCount = config.limits.maxBreadcrumbCount; + if (config.limits.maxBreadcrumbCount < 1) { + L.w(`configToJson, Provided value for maxBreadcrumbCount is invalid!`) + } else { + json.maxBreadcrumbCount = config.limits.maxBreadcrumbCount; + } } if (config.limits.maxStackTraceLinesPerThread) { - json.maxStackTraceLinesPerThread = config.limits.maxStackTraceLinesPerThread; + if (config.limits.maxStackTraceLinesPerThread < 1) { + L.w(`configToJson, Provided value for maxStackTraceLinesPerThread is invalid!`) + } else { + json.maxStackTraceLinesPerThread = config.limits.maxStackTraceLinesPerThread; + } } if (config.limits.maxStackTraceLineLength) { - json.maxStackTraceLineLength = config.limits.maxStackTraceLineLength; + if (config.limits.maxStackTraceLineLength < 1) { + L.w(`configToJson, Provided value for maxStackTraceLineLength is invalid!`) + } else { + json.maxStackTraceLineLength = config.limits.maxStackTraceLineLength; + } } // Limits End -------------------------------------------- } catch (err) { diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index f120ae50..7cf8ada2 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -113,14 +113,6 @@ public class CountlyReactNative extends ReactContextBaseJavaModule implements Li private static String pushNotificationCallbackName = "pushNotificationCallback"; private List retrievedWidgetList = null; - // SDK Limit Defaults - final int maxKeyLengthDefault = 128; - final int maxValueSizeDefault = 256; - final int maxSegmentationValuesDefault = 100; - final int maxBreadcrumbCountDefault = 100; - final int maxStackTraceLinesPerThreadDefault = 30; - final int maxStackTraceLineLengthDefault = 200; - private final Set validConsentFeatureNames = new HashSet<>(Arrays.asList( Countly.CountlyFeatureNames.sessions, Countly.CountlyFeatureNames.events, @@ -245,65 +237,23 @@ private void populateConfig(JSONObject _config) { // APM END -------------------------------------------- // Limits ----------------------------------------------- if(_config.has("maxKeyLength")) { - if (_config.getInt("maxKeyLength") < 1) { - log("[Init] provided 'maxKeyLength' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxKeyLength(1); - } config.limits.setMaxKeyLength(_config.getInt("maxKeyLength")); } - else { - config.limits.setMaxKeyLength(maxKeyLengthDefault); - } if(_config.has("maxValueSize")) { - if (_config.getInt("maxValueSize") < 1) { - log("[Init] provided 'maxValueSize' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxValueSize(1); - } config.limits.setMaxValueSize(_config.getInt("maxValueSize")); } - else { - config.limits.setMaxValueSize(maxValueSizeDefault); - } if(_config.has("maxSegmentationValues")) { - if (_config.getInt("maxSegmentationValues") < 1) { - log("[Init] provided 'maxSegmentationValues' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxSegmentationValues(1); - } config.limits.setMaxSegmentationValues(_config.getInt("maxSegmentationValues")); } - else { - config.limits.setMaxSegmentationValues(maxSegmentationValuesDefault); - } if(_config.has("maxBreadcrumbCount")) { - if (_config.getInt("maxBreadcrumbCount") < 1) { - log("[Init] provided 'maxBreadcrumbCount' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxBreadcrumbCount(1); - } config.limits.setMaxBreadcrumbCount(_config.getInt("maxBreadcrumbCount")); } - else { - config.limits.setMaxBreadcrumbCount(maxBreadcrumbCountDefault); - } if(_config.has("maxStackTraceLinesPerThread")) { - if (_config.getInt("maxStackTraceLinesPerThread") < 1) { - log("[Init] provided 'maxStackTraceLinesPerThread' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxStackTraceLinesPerThread(1); - } config.limits.setMaxStackTraceLinesPerThread(_config.getInt("maxStackTraceLinesPerThread")); } - else { - config.limits.setMaxStackTraceLinesPerThread(maxStackTraceLinesPerThreadDefault); - } if(_config.has("maxStackTraceLineLength")) { - if (_config.getInt("maxStackTraceLineLength") < 1) { - log("[Init] provided 'maxStackTraceLineLength' is less than '1'. Setting it to '1'.", LogLevel.WARNING); - config.limits.setMaxStackTraceLineLength(1); - } config.limits.setMaxStackTraceLineLength(_config.getInt("maxStackTraceLineLength")); } - else { - config.limits.setMaxStackTraceLineLength(maxStackTraceLineLengthDefault); - } // Limits End ------------------------------------------- if (_config.has("crashReporting")) { config.enableCrashReporting(); diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index bed3b9e6..a80a0cbe 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -50,14 +50,6 @@ + (CountlyFeedbackWidget *)createWithDictionary:(NSDictionary *)dictionary; NSString *const ratingWidgetCallbackName = @"ratingWidgetCallback"; NSString *const pushNotificationCallbackName = @"pushNotificationCallback"; -// SDK Limit Defaults -const int maxKeyLengthDefault = 150; -const int maxValueSizeDefault = 200; -const int maxSegmentationValuesDefault = 120; -const int maxBreadcrumbCountDefault = 120; -const int maxStackTraceLinesPerThreadDefault = 50; -const int maxStackTraceLineLengthDefault = 300; - @implementation CountlyReactNative NSString *const kCountlyNotificationPersistencyKey = @"kCountlyNotificationPersistencyKey"; @@ -158,62 +150,27 @@ - (void) populateConfig:(id) json { // Limits ----------------------------------------------- // maxKeyLength if (json[@"maxKeyLength"]) { - if ([[_config objectForKey:@"maxKeyLength"] intValue] < 1) { - [config.limits setMaxKeyLength:1]; - } [config.limits setMaxKeyLength:[[_config objectForKey:@"maxKeyLength"] intValue]]; - } else { - [config.limits setMaxKeyLength:maxKeyLengthDefault]; } - // maxValueSize if (json[@"maxValueSize"]) { - if ([[_config objectForKey:@"maxValueSize"] intValue] < 1) { - [config.limits setMaxValueSize:1]; - } [config.limits setMaxValueSize:[[_config objectForKey:@"maxValueSize"] intValue]]; - } else { - [config.limits setMaxValueSize:maxValueSizeDefault]; } - // maxSegmentationValues if (json[@"maxSegmentationValues"]) { - if ([[_config objectForKey:@"maxSegmentationValues"] intValue] < 1) { - [config.limits setMaxSegmentationValues:1]; - } [config.limits setMaxSegmentationValues:[[_config objectForKey:@"maxSegmentationValues"] intValue]]; - } else { - [config.limits setMaxSegmentationValues:maxSegmentationValuesDefault]; } - // maxBreadcrumbCount if (json[@"maxBreadcrumbCount"]) { - if ([[_config objectForKey:@"maxBreadcrumbCount"] intValue] < 1) { - [config.limits setMaxBreadcrumbCount:1]; - } [config.limits setMaxBreadcrumbCount:[[_config objectForKey:@"maxBreadcrumbCount"] intValue]]; - } else { - [config.limits setMaxBreadcrumbCount:maxBreadcrumbCountDefault]; } - // maxStackTraceLinesPerThread if (json[@"maxStackTraceLinesPerThread"]) { - if ([[_config objectForKey:@"maxStackTraceLinesPerThread"] intValue] < 1) { - [config.limits setMaxStackTraceLinesPerThread:1]; - } [config.limits setMaxStackTraceLinesPerThread:[[_config objectForKey:@"maxStackTraceLinesPerThread"] intValue]]; - } else { - [config.limits setMaxStackTraceLinesPerThread:maxStackTraceLinesPerThreadDefault]; } - // maxStackTraceLineLength if (json[@"maxStackTraceLineLength"]) { - if ([[_config objectForKey:@"maxStackTraceLineLength"] intValue] < 1) { - [config.limits setMaxStackTraceLineLength:1]; - } [config.limits setMaxStackTraceLineLength:[[_config objectForKey:@"maxStackTraceLineLength"] intValue]]; - } else { - [config.limits setMaxStackTraceLineLength:maxStackTraceLineLengthDefault]; } // Limits End ------------------------------------------- // APM ------------------------------------------------ From 5aa121d246ced5829c789d581c52f5426cd0f885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= <144099585+AliRKat@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:33:04 +0300 Subject: [PATCH 13/16] Update settings.json --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 04b5ffcd..2d17cc55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "editor.formatOnSave": true, "cSpell.words": ["Countly"], "[javascript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "java.configuration.updateBuildConfiguration": "interactive" } From 05b7871150cbf571e82d7e0606b1e83e18f31e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Thu, 26 Sep 2024 14:49:29 +0300 Subject: [PATCH 14/16] Some changes --- Countly.d.ts | 42 +++++++++++++++++++ .../android/sdk/react/CountlyReactNative.java | 12 +++--- example/CountlyRNExample/Configuration.tsx | 9 ++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/Countly.d.ts b/Countly.d.ts index 015e1860..e09052ec 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -1129,6 +1129,44 @@ declare module "countly-sdk-react-native-bridge/CountlyConfig" { setAppStartTimestampOverride(timestamp: number): CountlyConfigApm; } + class CountlyConfigSDKInternalLimits { + /** + * Limits the maximum size of all string keys + * @param keyLengthLimit - maximum char size of all string keys (default 128 chars) + */ + setMaxKeyLength(keyLengthLimit: number) : CountlyConfigSDKInternalLimits; + + /** + * Limits the size of all values in segmentation key-value pairs + * @param valueSizeLimit - the maximum char size of all values in our key-value pairs (default 256 chars) + */ + setMaxValueSize(valueSizeLimit: number) : CountlyConfigSDKInternalLimits; + + /** + * Limits the max amount of custom segmentation in one event + * @param segmentationAmountLimit - the maximum amount of custom segmentation in one event (default 100 key-value pairs) + */ + setMaxSegmentationValues(segmentationAmountLimit: number) : CountlyConfigSDKInternalLimits; + + /** + * Limits the max amount of breadcrumbs that can be recorded before the oldest one is deleted + * @param breadcrumbCountLimit - the maximum amount of breadcrumbs that can be recorded before the oldest one is deleted (default 100) + */ + setMaxBreadcrumbCount(breadcrumbCountLimit: number) : CountlyConfigSDKInternalLimits; + + /** + * Limits the max amount of stack trace lines to be recorded per thread + * @param stackTraceLinesPerThreadLimit - maximum amount of stack trace lines to be recorded per thread (default 30) + */ + setMaxStackTraceLinesPerThread(stackTraceLinesPerThreadLimit: number) : CountlyConfigSDKInternalLimits; + + /** + * Limits the max characters allowed per stack trace lines. Also limits the crash message length + * @param stackTraceLineLengthLimit - maximum length of each stack trace line (default 200) + */ + setMaxStackTraceLineLength(stackTraceLineLengthLimit: number) : CountlyConfigSDKInternalLimits; + } + /** * * Config object for Countly Init @@ -1146,6 +1184,10 @@ declare module "countly-sdk-react-native-bridge/CountlyConfig" { * getter for CountlyConfigApm instance that is used to access CountlyConfigApm methods */ apm: CountlyConfigApm; + /** + * getter for CountlySDKLimits instance that is used to access CountlyConfigSDKInternalLimits methods + */ + limits: CountlyConfigSDKInternalLimits; /** * Method to set the server url diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java index 7cf8ada2..366a11a2 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -237,22 +237,22 @@ private void populateConfig(JSONObject _config) { // APM END -------------------------------------------- // Limits ----------------------------------------------- if(_config.has("maxKeyLength")) { - config.limits.setMaxKeyLength(_config.getInt("maxKeyLength")); + config.sdkInternalLimits.setMaxKeyLength(_config.getInt("maxKeyLength")); } if(_config.has("maxValueSize")) { - config.limits.setMaxValueSize(_config.getInt("maxValueSize")); + config.sdkInternalLimits.setMaxValueSize(_config.getInt("maxValueSize")); } if(_config.has("maxSegmentationValues")) { - config.limits.setMaxSegmentationValues(_config.getInt("maxSegmentationValues")); + config.sdkInternalLimits.setMaxSegmentationValues(_config.getInt("maxSegmentationValues")); } if(_config.has("maxBreadcrumbCount")) { - config.limits.setMaxBreadcrumbCount(_config.getInt("maxBreadcrumbCount")); + config.sdkInternalLimits.setMaxBreadcrumbCount(_config.getInt("maxBreadcrumbCount")); } if(_config.has("maxStackTraceLinesPerThread")) { - config.limits.setMaxStackTraceLinesPerThread(_config.getInt("maxStackTraceLinesPerThread")); + config.sdkInternalLimits.setMaxStackTraceLinesPerThread(_config.getInt("maxStackTraceLinesPerThread")); } if(_config.has("maxStackTraceLineLength")) { - config.limits.setMaxStackTraceLineLength(_config.getInt("maxStackTraceLineLength")); + config.sdkInternalLimits.setMaxStackTraceLineLength(_config.getInt("maxStackTraceLineLength")); } // Limits End ------------------------------------------- if (_config.has("crashReporting")) { diff --git a/example/CountlyRNExample/Configuration.tsx b/example/CountlyRNExample/Configuration.tsx index b6ba1e64..38231af7 100644 --- a/example/CountlyRNExample/Configuration.tsx +++ b/example/CountlyRNExample/Configuration.tsx @@ -30,4 +30,13 @@ const countlyConfig = new CountlyConfig(COUNTLY_SERVER_KEY, COUNTLY_APP_KEY).set // .enableManualAppLoadedTrigger() // .setAppStartTimestampOverride(11223344); +// Countly SDK Limits ======================================== +// countlyConfig.limits +// .setMaxKeyLength() +// .setMaxValueSize() +// .setMaxSegmentationValues() +// .setMaxBreadcrumbCount() +// .setMaxStackTraceLineLength() +// .setMaxStackTraceLinesPerThread(); + export default countlyConfig; From d7cb811ea3bdeb411b3aa6f41eb95e6c0c726330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Thu, 26 Sep 2024 17:42:17 +0300 Subject: [PATCH 15/16] ios changes --- ios/src/CountlyReactNative.m | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index 6ebc0baf..a600a614 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -149,28 +149,29 @@ - (void) populateConfig:(id) json { } // Limits ----------------------------------------------- // maxKeyLength - if (json[@"maxKeyLength"]) { - [config.limits setMaxKeyLength:[[_config objectForKey:@"maxKeyLength"] intValue]]; + NSNumber *maxKeyLength = json[@"maxKeyLength"]; + if (maxKeyLength) { + [config.sdkInternalLimits setMaxKeyLength:[maxKeyLength intValue]]; } - // maxValueSize - if (json[@"maxValueSize"]) { - [config.limits setMaxValueSize:[[_config objectForKey:@"maxValueSize"] intValue]]; + NSNumber *maxValueSize = json[@"maxValueSize"]; + if (maxValueSize) { + [config.sdkInternalLimits setMaxValueSize:[maxValueSize intValue]]; } - // maxSegmentationValues - if (json[@"maxSegmentationValues"]) { - [config.limits setMaxSegmentationValues:[[_config objectForKey:@"maxSegmentationValues"] intValue]]; + NSNumber *maxSegmentationValues = json[@"maxSegmentationValues"]; + if (maxSegmentationValues) { + [config.sdkInternalLimits setMaxSegmentationValues:[maxSegmentationValues intValue]]; } - // maxBreadcrumbCount - if (json[@"maxBreadcrumbCount"]) { - [config.limits setMaxBreadcrumbCount:[[_config objectForKey:@"maxBreadcrumbCount"] intValue]]; + NSNumber *maxBreadcrumbCount = json[@"maxBreadcrumbCount"]; + if (maxBreadcrumbCount) { + [config.sdkInternalLimits setMaxBreadcrumbCount:[maxBreadcrumbCount intValue]]; } - // maxStackTraceLinesPerThread - if (json[@"maxStackTraceLinesPerThread"]) { - [config.limits setMaxStackTraceLinesPerThread:[[_config objectForKey:@"maxStackTraceLinesPerThread"] intValue]]; + NSNumber *maxStackTraceLineLength = json[@"maxStackTraceLineLength"]; + if (maxStackTraceLineLength) { + [config.sdkInternalLimits setMaxStackTraceLineLength:[maxStackTraceLineLength intValue]]; } - // maxStackTraceLineLength - if (json[@"maxStackTraceLineLength"]) { - [config.limits setMaxStackTraceLineLength:[[_config objectForKey:@"maxStackTraceLineLength"] intValue]]; + NSNumber *maxStackTraceLinesPerThread = json[@"maxStackTraceLinesPerThread"]; + if (maxStackTraceLinesPerThread) { + [config.sdkInternalLimits setMaxStackTraceLinesPerThread:[maxStackTraceLinesPerThread intValue]]; } // Limits End ------------------------------------------- // APM ------------------------------------------------ From 7f9b4c559e8f1b8267a818b3552dad4a07dafc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20R=C4=B1za=20Kat?= Date: Thu, 26 Sep 2024 18:03:34 +0300 Subject: [PATCH 16/16] Updates --- CHANGELOG.md | 15 +++++++++++---- Countly.d.ts | 2 +- CountlyConfig.js | 2 +- Utils.js | 36 ++++++++++++++++++------------------ 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3f93183..5c697b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,19 @@ ## 24.4.1 * Added support for Feedback Widget terms and conditions - -* Android: - * ! Minor breaking change ! Mitigated an issue where internal SDK limits did not apply +* Added six new configuration options under the 'sdkInternalLimits' interface of 'CountlyConfig': + * 'setMaxKeyLength' for limiting the maximum size of all user provided string keys + * 'setMaxValueSize' for limiting the size of all values in user provided segmentation key-value pairs + * 'setMaxSegmentationValues' for limiting the max amount of user provided segmentation key-value pair count in one event + * 'setMaxBreadcrumbCount' for limiting the max amount of breadcrumbs that can be recorded before the oldest one is deleted + * 'setMaxStackTraceLinesPerThread' for limiting the max amount of stack trace lines to be recorded per thread + * 'setMaxStackTraceLineLength' for limiting the max characters allowed per stack trace lines + +* Android Specific Changes: + * ! Minor breaking change ! Introduced SDK internal limits * Mitigated an issue where the session duration could have been calculated wrongly after a device ID change without merge * Mitigated an issue where a session could have continued after a device ID change without merge -* iOS: +* iOS Specific Changes: * Mitigated an issue where internal limits were not being applied to some values * Mitigated an issue where SDK limits could affect internal keys * Mitigated an issue that enabled recording reserved events diff --git a/Countly.d.ts b/Countly.d.ts index e09052ec..dd8d12c3 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -1187,7 +1187,7 @@ declare module "countly-sdk-react-native-bridge/CountlyConfig" { /** * getter for CountlySDKLimits instance that is used to access CountlyConfigSDKInternalLimits methods */ - limits: CountlyConfigSDKInternalLimits; + sdkInternalLimits: CountlyConfigSDKInternalLimits; /** * Method to set the server url diff --git a/CountlyConfig.js b/CountlyConfig.js index 1ebd368d..5ef6c516 100644 --- a/CountlyConfig.js +++ b/CountlyConfig.js @@ -29,7 +29,7 @@ class CountlyConfig { return this._countlyConfigApmInstance; } - get limits() { + get sdkInternalLimits() { return this._countlyConfigSDKLimitsInstance; } diff --git a/Utils.js b/Utils.js index e32bb3e1..37c7592a 100644 --- a/Utils.js +++ b/Utils.js @@ -135,46 +135,46 @@ function configToJson(config) { json.attributionValues = config.attributionValues; } // Limits ----------------------------------------------- - if (config.limits.maxKeyLength) { - if (config.limits.maxKeyLength < 1) { + if (config.sdkInternalLimits.maxKeyLength) { + if (config.sdkInternalLimits.maxKeyLength < 1) { L.w(`configToJson, Provided value for maxKeyLength is invalid!`) } else { - json.maxKeyLength = config.limits.maxKeyLength; + json.maxKeyLength = config.sdkInternalLimits.maxKeyLength; } } - if (config.limits.maxValueSize) { - if (config.limits.maxValueSize < 1) { + if (config.sdkInternalLimits.maxValueSize) { + if (config.sdkInternalLimits.maxValueSize < 1) { L.w(`configToJson, Provided value for maxValueSize is invalid!`) } else { - json.maxValueSize = config.limits.maxValueSize; + json.maxValueSize = config.sdkInternalLimits.maxValueSize; } } - if (config.limits.maxSegmentationValues) { - if (config.limits.maxSegmentationValues < 1) { + if (config.sdkInternalLimits.maxSegmentationValues) { + if (config.sdkInternalLimits.maxSegmentationValues < 1) { L.w(`configToJson, Provided value for maxSegmentationValues is invalid!`) } else { - json.maxSegmentationValues = config.limits.maxSegmentationValues; + json.maxSegmentationValues = config.sdkInternalLimits.maxSegmentationValues; } } - if (config.limits.maxBreadcrumbCount) { - if (config.limits.maxBreadcrumbCount < 1) { + if (config.sdkInternalLimits.maxBreadcrumbCount) { + if (config.sdkInternalLimits.maxBreadcrumbCount < 1) { L.w(`configToJson, Provided value for maxBreadcrumbCount is invalid!`) } else { - json.maxBreadcrumbCount = config.limits.maxBreadcrumbCount; + json.maxBreadcrumbCount = config.sdkInternalLimits.maxBreadcrumbCount; } } - if (config.limits.maxStackTraceLinesPerThread) { - if (config.limits.maxStackTraceLinesPerThread < 1) { + if (config.sdkInternalLimits.maxStackTraceLinesPerThread) { + if (config.sdkInternalLimits.maxStackTraceLinesPerThread < 1) { L.w(`configToJson, Provided value for maxStackTraceLinesPerThread is invalid!`) } else { - json.maxStackTraceLinesPerThread = config.limits.maxStackTraceLinesPerThread; + json.maxStackTraceLinesPerThread = config.sdkInternalLimits.maxStackTraceLinesPerThread; } } - if (config.limits.maxStackTraceLineLength) { - if (config.limits.maxStackTraceLineLength < 1) { + if (config.sdkInternalLimits.maxStackTraceLineLength) { + if (config.sdkInternalLimits.maxStackTraceLineLength < 1) { L.w(`configToJson, Provided value for maxStackTraceLineLength is invalid!`) } else { - json.maxStackTraceLineLength = config.limits.maxStackTraceLineLength; + json.maxStackTraceLineLength = config.sdkInternalLimits.maxStackTraceLineLength; } } // Limits End --------------------------------------------