Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Read initWithDict for Hybrid SDKs #2579

Merged
merged 6 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
7B2A70DB27D607CF008B0D15 /* SentryThreadWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B2A70DA27D607CF008B0D15 /* SentryThreadWrapper.h */; };
7B2A70DD27D6083D008B0D15 /* SentryThreadWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B2A70DC27D6083D008B0D15 /* SentryThreadWrapper.m */; };
7B2A70DF27D60904008B0D15 /* SentryTestThreadWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2A70DE27D60904008B0D15 /* SentryTestThreadWrapper.swift */; };
7B2BB0032966F55900A1E102 /* SentryOptions+HybridSDKs.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */; settings = {ATTRIBUTES = (Private, ); }; };
7B30B67C26527886006B2752 /* SentryDisplayLinkWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B30B67B26527886006B2752 /* SentryDisplayLinkWrapper.h */; };
7B30B67E26527894006B2752 /* SentryDisplayLinkWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B30B67D26527894006B2752 /* SentryDisplayLinkWrapper.m */; };
7B30B68026527C3C006B2752 /* SentryFramesTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B30B67F26527C3C006B2752 /* SentryFramesTrackerTests.swift */; };
Expand Down Expand Up @@ -1103,6 +1104,7 @@
7B2A70DA27D607CF008B0D15 /* SentryThreadWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryThreadWrapper.h; path = include/SentryThreadWrapper.h; sourceTree = "<group>"; };
7B2A70DC27D6083D008B0D15 /* SentryThreadWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryThreadWrapper.m; sourceTree = "<group>"; };
7B2A70DE27D60904008B0D15 /* SentryTestThreadWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTestThreadWrapper.swift; sourceTree = "<group>"; };
7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryOptions+HybridSDKs.h"; path = "include/HybridPublic/SentryOptions+HybridSDKs.h"; sourceTree = "<group>"; };
7B30B67B26527886006B2752 /* SentryDisplayLinkWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDisplayLinkWrapper.h; path = include/SentryDisplayLinkWrapper.h; sourceTree = "<group>"; };
7B30B67D26527894006B2752 /* SentryDisplayLinkWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SentryDisplayLinkWrapper.m; path = include/SentryDisplayLinkWrapper.m; sourceTree = "<group>"; };
7B30B67F26527C3C006B2752 /* SentryFramesTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFramesTrackerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1586,13 +1588,13 @@
D8BD2E67293619F600D96C6A /* PrivatesHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PrivatesHeader.h; path = include/HybridPublic/PrivatesHeader.h; sourceTree = "<group>"; };
D8C67E9928000E23007E326E /* SentryUIApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryUIApplication.h; path = include/SentryUIApplication.h; sourceTree = "<group>"; };
D8C67E9A28000E23007E326E /* SentryScreenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryScreenshot.h; path = include/SentryScreenshot.h; sourceTree = "<group>"; };
D8CB742A294B1DD000A5F964 /* SentryUIApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIApplicationTests.swift; sourceTree = "<group>"; };
D8CB742C294B294B00A5F964 /* MockUIScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockUIScene.h; sourceTree = "<group>"; };
D8CB742D294B294B00A5F964 /* MockUIScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockUIScene.m; sourceTree = "<group>"; };
D8CB74142947246600A5F964 /* SentryEnvelopeAttachmentHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEnvelopeAttachmentHeader.h; path = include/SentryEnvelopeAttachmentHeader.h; sourceTree = "<group>"; };
D8CB7416294724CC00A5F964 /* SentryEnvelopeAttachmentHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryEnvelopeAttachmentHeader.m; sourceTree = "<group>"; };
D8CB74182947285A00A5F964 /* SentryEnvelopeItemHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEnvelopeItemHeader.h; path = Public/SentryEnvelopeItemHeader.h; sourceTree = "<group>"; };
D8CB741A2947286500A5F964 /* SentryEnvelopeItemHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryEnvelopeItemHeader.m; sourceTree = "<group>"; };
D8CB742A294B1DD000A5F964 /* SentryUIApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIApplicationTests.swift; sourceTree = "<group>"; };
D8CB742C294B294B00A5F964 /* MockUIScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockUIScene.h; sourceTree = "<group>"; };
D8CB742D294B294B00A5F964 /* MockUIScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockUIScene.m; sourceTree = "<group>"; };
D8CE69BB277E39C700C6EC5C /* SentryFileIOTrackingIntegrationObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryFileIOTrackingIntegrationObjCTests.m; sourceTree = "<group>"; };
D8F6A2452885512100320515 /* SentryPredicateDescriptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryPredicateDescriptor.m; sourceTree = "<group>"; };
D8F6A24A2885515B00320515 /* SentryPredicateDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryPredicateDescriptor.h; path = include/SentryPredicateDescriptor.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2100,6 +2102,7 @@
63EED6BC2237923600E02400 /* SentryOptions.h */,
63EED6BD2237923600E02400 /* SentryOptions.m */,
7BDEAA002632A4580001EA25 /* SentryOptions+Private.h */,
7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */,
7D9B079F23D1E89800C5FC8E /* SentryMeta.h */,
7D082B8023C628780029866B /* SentryMeta.m */,
);
Expand Down Expand Up @@ -3271,6 +3274,7 @@
7B85DC1E24EFAFCD007D01D2 /* SentryClient+Private.h in Headers */,
A8AFFCCF2906C03700967CD7 /* SentryRequest.h in Headers */,
7BD4BD4327EB29BA0071F4FF /* SentryClientReport.h in Headers */,
7B2BB0032966F55900A1E102 /* SentryOptions+HybridSDKs.h in Headers */,
7BF9EF762722B34700B5BBEF /* SentrySubClassFinder.h in Headers */,
7BC852332458802C005A70F0 /* SentryDataCategoryMapper.h in Headers */,
7BDB03B7251364F800BAE198 /* SentryDispatchQueueWrapper.h in Headers */,
Expand Down
3 changes: 0 additions & 3 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Options)
@interface SentryOptions : NSObject

- (_Nullable instancetype)initWithDsn:(NSString *)dsn
didFailWithError:(NSError *_Nullable *_Nullable)error;

brustolin marked this conversation as resolved.
Show resolved Hide resolved
/**
* The DSN tells the SDK where to send the events to. If this value is not provided, the SDK will
* not send any events.
Expand Down
236 changes: 230 additions & 6 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ - (instancetype)init
return self;
}

- (_Nullable instancetype)initWithDsn:(NSString *)dsn
didFailWithError:(NSError *_Nullable *_Nullable)error
- (_Nullable instancetype)initWithDict:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error
{
if (self = [self init]) {
self.parsedDsn = [[SentryDsn alloc] initWithString:dsn didFailWithError:error];
self.dsn = dsn;

if (error != nil && *error != nil) {
if (![self validateOptions:options didFailWithError:error]) {
[SentryLog
logWithMessage:[NSString stringWithFormat:@"Failed to initialize: %@", *error]
andLevel:kSentryLevelError];
return nil;
}
}
Expand Down Expand Up @@ -215,6 +215,230 @@ - (void)setDsn:(NSString *)dsn
}
}

/**
* Populates all `SentryOptions` values from `options` dict using fallbacks/defaults if needed.
*/
- (BOOL)validateOptions:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error
{
NSPredicate *isNSString = [NSPredicate predicateWithBlock:^BOOL(
id object, NSDictionary *bindings) { return [object isKindOfClass:[NSString class]]; }];

[self setBool:options[@"debug"] block:^(BOOL value) { self->_debug = value; }];

if ([options[@"diagnosticLevel"] isKindOfClass:[NSString class]]) {
for (SentryLevel level = 0; level <= kSentryLevelFatal; level++) {
if ([nameForSentryLevel(level) isEqualToString:options[@"diagnosticLevel"]]) {
self.diagnosticLevel = level;
break;
}
}
}

NSString *dsn = @"";
if (nil != options[@"dsn"] && [options[@"dsn"] isKindOfClass:[NSString class]]) {
dsn = options[@"dsn"];
}

self.parsedDsn = [[SentryDsn alloc] initWithString:dsn didFailWithError:error];

if ([options[@"release"] isKindOfClass:[NSString class]]) {
self.releaseName = options[@"release"];
}

if ([options[@"environment"] isKindOfClass:[NSString class]]) {
self.environment = options[@"environment"];
}

if ([options[@"dist"] isKindOfClass:[NSString class]]) {
self.dist = options[@"dist"];
}

[self setBool:options[@"enabled"] block:^(BOOL value) { self->_enabled = value; }];

if ([options[@"shutdownTimeInterval"] isKindOfClass:[NSNumber class]]) {
self.shutdownTimeInterval = [options[@"shutdownTimeInterval"] doubleValue];
}

[self setBool:options[@"enableCrashHandler"]
block:^(BOOL value) { self->_enableCrashHandler = value; }];

if ([options[@"maxBreadcrumbs"] isKindOfClass:[NSNumber class]]) {
self.maxBreadcrumbs = [options[@"maxBreadcrumbs"] unsignedIntValue];
}

[self setBool:options[@"enableNetworkBreadcrumbs"]
block:^(BOOL value) { self->_enableNetworkBreadcrumbs = value; }];

if ([options[@"maxCacheItems"] isKindOfClass:[NSNumber class]]) {
self.maxCacheItems = [options[@"maxCacheItems"] unsignedIntValue];
}

if ([self isBlock:options[@"beforeSend"]]) {
self.beforeSend = options[@"beforeSend"];
}

if ([self isBlock:options[@"beforeBreadcrumb"]]) {
self.beforeBreadcrumb = options[@"beforeBreadcrumb"];
}

if ([self isBlock:options[@"onCrashedLastRun"]]) {
self.onCrashedLastRun = options[@"onCrashedLastRun"];
}

if ([options[@"integrations"] isKindOfClass:[NSArray class]]) {
self.integrations = [options[@"integrations"] filteredArrayUsingPredicate:isNSString];
}

if ([options[@"sampleRate"] isKindOfClass:[NSNumber class]]) {
self.sampleRate = options[@"sampleRate"];
}

[self setBool:options[@"enableAutoSessionTracking"]
block:^(BOOL value) { self->_enableAutoSessionTracking = value; }];

[self setBool:options[@"enableWatchdogTerminationTracking"]
block:^(BOOL value) { self->_enableWatchdogTerminationTracking = value; }];

if ([options[@"sessionTrackingIntervalMillis"] isKindOfClass:[NSNumber class]]) {
self.sessionTrackingIntervalMillis =
[options[@"sessionTrackingIntervalMillis"] unsignedIntValue];
}

[self setBool:options[@"attachStacktrace"]
block:^(BOOL value) { self->_attachStacktrace = value; }];

[self setBool:options[@"stitchAsyncCode"]
block:^(BOOL value) { self->_stitchAsyncCode = value; }];

if ([options[@"maxAttachmentSize"] isKindOfClass:[NSNumber class]]) {
self.maxAttachmentSize = [options[@"maxAttachmentSize"] unsignedIntValue];
}

[self setBool:options[@"sendDefaultPii"]
block:^(BOOL value) { self->_sendDefaultPii = value; }];

[self setBool:options[@"enableAutoPerformanceTracing"]
block:^(BOOL value) { self->_enableAutoPerformanceTracing = value; }];

[self setBool:options[@"enableCaptureFailedRequests"]
block:^(BOOL value) { self->_enableCaptureFailedRequests = value; }];

#if SENTRY_HAS_UIKIT
[self setBool:options[@"enableUIViewControllerTracing"]
block:^(BOOL value) { self->_enableUIViewControllerTracing = value; }];

[self setBool:options[@"attachScreenshot"]
block:^(BOOL value) { self->_attachScreenshot = value; }];

[self setBool:options[@"attachViewHierarchy"]
block:^(BOOL value) { self->_attachViewHierarchy = value; }];

[self setBool:options[@"enableUserInteractionTracing"]
block:^(BOOL value) { self->_enableUserInteractionTracing = value; }];

if ([options[@"idleTimeout"] isKindOfClass:[NSNumber class]]) {
self.idleTimeout = [options[@"idleTimeout"] doubleValue];
}

[self setBool:options[@"enablePreWarmedAppStartTracing"]
block:^(BOOL value) { self->_enablePreWarmedAppStartTracing = value; }];
#endif

[self setBool:options[@"enableAppHangTracking"]
block:^(BOOL value) { self->_enableAppHangTracking = value; }];

if ([options[@"appHangTimeoutInterval"] isKindOfClass:[NSNumber class]]) {
self.appHangTimeoutInterval = [options[@"appHangTimeoutInterval"] doubleValue];
}

[self setBool:options[@"enableNetworkTracking"]
block:^(BOOL value) { self->_enableNetworkTracking = value; }];

[self setBool:options[@"enableFileIOTracing"]
block:^(BOOL value) { self->_enableFileIOTracing = value; }];

if ([options[@"tracesSampleRate"] isKindOfClass:[NSNumber class]]) {
self.tracesSampleRate = options[@"tracesSampleRate"];
}

if ([self isBlock:options[@"tracesSampler"]]) {
self.tracesSampler = options[@"tracesSampler"];
}

if ([options[@"inAppIncludes"] isKindOfClass:[NSArray class]]) {
NSArray<NSString *> *inAppIncludes =
[options[@"inAppIncludes"] filteredArrayUsingPredicate:isNSString];
_inAppIncludes = [_inAppIncludes arrayByAddingObjectsFromArray:inAppIncludes];
}

if ([options[@"inAppExcludes"] isKindOfClass:[NSArray class]]) {
_inAppExcludes = [options[@"inAppExcludes"] filteredArrayUsingPredicate:isNSString];
}

if ([options[@"urlSessionDelegate"] conformsToProtocol:@protocol(NSURLSessionDelegate)]) {
self.urlSessionDelegate = options[@"urlSessionDelegate"];
}

[self setBool:options[@"enableSwizzling"]
block:^(BOOL value) { self->_enableSwizzling = value; }];

[self setBool:options[@"enableCoreDataTracing"]
block:^(BOOL value) { self->_enableCoreDataTracing = value; }];

#if SENTRY_TARGET_PROFILING_SUPPORTED
if ([options[@"profilesSampleRate"] isKindOfClass:[NSNumber class]]) {
self.profilesSampleRate = options[@"profilesSampleRate"];
}

if ([self isBlock:options[@"profilesSampler"]]) {
self.profilesSampler = options[@"profilesSampler"];
}

[self setBool:options[@"enableProfiling"]
block:^(BOOL value) { self->_enableProfiling = value; }];
#endif

[self setBool:options[@"sendClientReports"]
block:^(BOOL value) { self->_sendClientReports = value; }];

[self setBool:options[@"enableAutoBreadcrumbTracking"]
block:^(BOOL value) { self->_enableAutoBreadcrumbTracking = value; }];

if ([options[@"tracePropagationTargets"] isKindOfClass:[NSArray class]]) {
self.tracePropagationTargets = options[@"tracePropagationTargets"];
}

if ([options[@"failedRequestStatusCodes"] isKindOfClass:[NSArray class]]) {
self.failedRequestStatusCodes = options[@"failedRequestStatusCodes"];
}

if ([options[@"failedRequestTargets"] isKindOfClass:[NSArray class]]) {
self.failedRequestTargets = options[@"failedRequestTargets"];
}

#if SENTRY_HAS_METRIC_KIT
if (@available(iOS 14.0, macOS 12.0, macCatalyst 14.0, *)) {
[self setBool:options[@"enableMetricKit"]
block:^(BOOL value) { self->_enableMetricKit = value; }];
}
#endif

if (nil != error && nil != *error) {
return NO;
} else {
return YES;
}
}

- (void)setBool:(id)value block:(void (^)(BOOL))block
{
// Entries in the dictionary can be NSNull. Especially, on React-Native, this can happen.
if (value != nil && ![value isEqual:[NSNull null]]) {
block([value boolValue]);
}
}

- (void)addInAppInclude:(NSString *)inAppInclude
{
_inAppIncludes = [self.inAppIncludes arrayByAddingObject:inAppInclude];
Expand Down
13 changes: 13 additions & 0 deletions Sources/Sentry/include/HybridPublic/SentryOptions+HybridSDKs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import "SentryOptions.h"

NS_ASSUME_NONNULL_BEGIN

@interface
SentryOptions (HybridSDKs)

- (_Nullable instancetype)initWithDict:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
17 changes: 9 additions & 8 deletions Tests/SentryTests/Networking/SentryDsnTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import "SentryError.h"
#import "SentryMeta.h"
#import "SentryNSURLRequest.h"
#import "SentryOptions+HybridSDKs.h"
#import <Sentry/Sentry.h>
#import <XCTest/XCTest.h>

Expand All @@ -14,8 +15,8 @@ @implementation SentryDsnTests
- (void)testMissingUsernamePassword
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"https://sentry.io"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"https://sentry.io" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}
Expand Down Expand Up @@ -62,26 +63,26 @@ - (void)testDsnHeaderUsername
- (void)testMissingScheme
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"sentry.io"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"https://sentry.io" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}

- (void)testMissingHost
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"http:///1"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"http:///1" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}

- (void)testUnsupportedProtocol
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"ftp://sentry.io/1"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"ftp://sentry.io/1" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SentryTransportInitializerTests: XCTestCase {
}

func testDefault() throws {
let options = try Options(dsn: SentryTransportInitializerTests.dsnAsString)
let options = try Options(dict: ["dsn": SentryTransportInitializerTests.dsnAsString])

let result = TransportInitializer.initTransport(options, sentryFileManager: fileManager)

Expand Down
4 changes: 3 additions & 1 deletion Tests/SentryTests/SentryClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ class SentryClientTest: XCTestCase {
func getSut(configureOptions: (Options) -> Void = { _ in }) -> SentryClient {
var client: SentryClient!
do {
let options = try Options(dsn: SentryClientTest.dsn)
let options = try Options(dict: [
"dsn": SentryClientTest.dsn
])
configureOptions(options)

client = SentryClient(
Expand Down
Loading