From faa162714412e1d10af318819adaad5b80eaa4b2 Mon Sep 17 00:00:00 2001 From: Robin Macharg Date: Tue, 18 Feb 2020 13:41:46 +0000 Subject: [PATCH] feat: Added a mutable Bugsnag.context property --- CHANGELOG.md | 5 +++++ Source/Bugsnag.h | 8 ++++++++ Source/Bugsnag.m | 4 ++++ Source/BugsnagEvent.m | 12 ++++++++++- Tests/BugsnagTests.m | 47 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0604da6fc..7d3cfb1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,11 @@ Bugsnag Notifiers on other platforms. `BugsnagConfiguration` value but can be overridden in event passed to the `Bugsnag.notify()` callback. [#458](https://github.com/bugsnag/bugsnag-cocoa/pull/458) + + * Added `Bugsnag.context`, replicating the `BugsnagConfiguration` property. This is + mutable and may be changed at any point. Changes are propagated to the `BugsnagConfiguration` + property. + [#466](https://github.com/bugsnag/bugsnag-cocoa/pull/466) * `Bugsnag.stopSession()` is now `Bugsnag.pauseSession()`. This renaming has also been applied to the `BugsnagNotifier` and `BugsnagSessionTracker` classes. diff --git a/Source/Bugsnag.h b/Source/Bugsnag.h index 780b12e21..b8e44d941 100644 --- a/Source/Bugsnag.h +++ b/Source/Bugsnag.h @@ -300,4 +300,12 @@ static NSString *_Nonnull const BugsnagSeverityInfo = @"info"; + (void)setBreadcrumbCapacity:(NSUInteger)capacity __deprecated_msg("Use [BugsnagConfiguration setMaxBreadcrumbs:] instead"); + +/** + * Replicates BugsnagConfiguration.context + * + * @param context A general summary of what was happening in the application + */ ++ (void)setContext:(NSString *_Nullable)context; + @end diff --git a/Source/Bugsnag.m b/Source/Bugsnag.m index 0ed1fe340..3de54824d 100644 --- a/Source/Bugsnag.m +++ b/Source/Bugsnag.m @@ -314,6 +314,10 @@ + (id _Nullable )getMetadata:(NSString *_Nonnull)section return [[[self configuration] metadata] getMetadata:section key:key]; } ++ (void)setContext:(NSString *_Nullable)context { + [self configuration].context = context; +} + @end // diff --git a/Source/BugsnagEvent.m b/Source/BugsnagEvent.m index cb3777ca6..3fd6868e0 100644 --- a/Source/BugsnagEvent.m +++ b/Source/BugsnagEvent.m @@ -109,6 +109,15 @@ id BSGLoadConfigValue(NSDictionary *report, NSString *valueName) { ?: [report valueForKeyPath:fallbackKeypath]; // some custom values are nested } +/** + * Attempt to find a context (within which the event is being reported) + * This can be found in user-set metadata of varying specificity or the global + * configuration. Returns nil if no context can be found. + * + * @param report A dictionary of report data + * @param metadata Additional relevant data + * @returns A string context if found, or nil + */ NSString *BSGParseContext(NSDictionary *report, NSDictionary *metadata) { id context = [report valueForKeyPath:@"user.overrides.context"]; if ([context isKindOfClass:[NSString class]]) @@ -333,7 +342,8 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { _metadata = metadata ?: [NSDictionary new]; _releaseStage = config.releaseStage; _notifyReleaseStages = config.notifyReleaseStages; - _context = BSGParseContext(nil, metadata); + // Set context based on current values. May be nil. + _context = metadata[BSGKeyContext] ?: [[Bugsnag configuration] context]; _breadcrumbs = [config.breadcrumbs arrayValue]; _overrides = [NSDictionary new]; diff --git a/Tests/BugsnagTests.m b/Tests/BugsnagTests.m index 597e2d6da..8f81379d5 100644 --- a/Tests/BugsnagTests.m +++ b/Tests/BugsnagTests.m @@ -129,4 +129,51 @@ -(void)testBugsnagPauseSession { [Bugsnag pauseSession]; } +/** + * Test that the BugsnagConfiguration-mirroring Bugsnag.context is mutable + */ +- (void)testMutableContext { + // Allow for checks inside blocks that may (potentially) be run asynchronously + __block XCTestExpectation *expectation1 = [self expectationWithDescription:@"Localized metadata changes"]; + + NSError *error; + BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1 error:&error]; + [configuration setContext:@"firstContext"]; + [configuration addBeforeSendBlock:^bool(NSDictionary * _Nonnull rawEventData, + BugsnagEvent * _Nonnull reports) + { + return false; + }]; + + [Bugsnag startBugsnagWithConfiguration:configuration]; + + NSException *exception1 = [[NSException alloc] initWithName:@"exception1" reason:@"reason1" userInfo:nil]; + + // Check that the context is set going in to the test and that we can change it + [Bugsnag notify:exception1 block:^(BugsnagEvent * _Nonnull event) { + XCTAssertEqual([[Bugsnag configuration] context], @"firstContext"); + + // Change the global context + [Bugsnag setContext:@"secondContext"]; + + // Check that it's made it into the configuration (from the point of view of the block) + // and that setting it here doesn't affect the event's value. + XCTAssertEqual([[Bugsnag configuration] context], @"secondContext"); + XCTAssertEqual([event context], @"firstContext"); + + [expectation1 fulfill]; + }]; + + // Test that the context (changed inside the notify block) remains changed + // And that the event picks up this value. + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { + XCTAssertEqual([[Bugsnag configuration] context], @"secondContext"); + + [Bugsnag notify:exception1 block:^(BugsnagEvent * _Nonnull report) { + XCTAssertEqual([[Bugsnag configuration] context], @"secondContext"); + XCTAssertEqual([report context], @"secondContext"); + }]; + }]; +} + @end