Skip to content

Commit 7850677

Browse files
feat: Read sentry.options.json during cocoa init (#4447)
1 parent 7144a64 commit 7850677

File tree

14 files changed

+287
-43
lines changed

14 files changed

+287
-43
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- Rename `navigation.processing` span to more expressive `Navigation dispatch to screen A mounted/navigation cancelled` ([#4423](https://github.com/getsentry/sentry-react-native/pull/4423))
2020
- Add RN SDK package to `sdk.packages` for Cocoa ([#4381](https://github.com/getsentry/sentry-react-native/pull/4381))
2121
- Add experimental version of `startWithConfigureOptions` for Apple platforms ([#4444](https://github.com/getsentry/sentry-react-native/pull/4444))
22+
- Add initialization using `sentry.options.json` for Apple platforms ([#4447](https://github.com/getsentry/sentry-react-native/pull/4447))
2223

2324
### Internal
2425

packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj

+37
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */; };
1313
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */; };
1414
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */; };
15+
339C6C422D3FD3AE00CA72ED /* RNSentryStartFromFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */; };
16+
339C6C482D3FD9A700CA72ED /* invalid.options.json in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C462D3FD91900CA72ED /* invalid.options.json */; };
17+
339C6C492D3FD9A700CA72ED /* invalid.options.txt in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C452D3FD90200CA72ED /* invalid.options.txt */; };
18+
339C6C4B2D3FD9B200CA72ED /* valid.options.json in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C4A2D3FD9AB00CA72ED /* valid.options.json */; };
1519
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };
1620
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
1721
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACF2977037D008F60EA /* RNSentryTests.mm */; };
@@ -41,6 +45,11 @@
4145
33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryOnDrawReporterTests.m; sourceTree = "<group>"; };
4246
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryStartTests.swift; sourceTree = "<group>"; };
4347
339C6C3D2D3FA04D00CA72ED /* RNSentryVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryVersion.h; path = ../ios/RNSentryVersion.h; sourceTree = SOURCE_ROOT; };
48+
339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryStartFromFileTests.swift; sourceTree = "<group>"; };
49+
339C6C442D3FD62D00CA72ED /* RNSentrySDK+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentrySDK+Test.h"; sourceTree = "<group>"; };
50+
339C6C452D3FD90200CA72ED /* invalid.options.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = invalid.options.txt; sourceTree = "<group>"; };
51+
339C6C462D3FD91900CA72ED /* invalid.options.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = invalid.options.json; sourceTree = "<group>"; };
52+
339C6C4A2D3FD9AB00CA72ED /* valid.options.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = valid.options.json; sourceTree = "<group>"; };
4453
33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryFramesTrackerListenerTests.m; sourceTree = "<group>"; };
4554
33AFDFEE2B8D14C200AAB120 /* RNSentryFramesTrackerListenerTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryFramesTrackerListenerTests.h; sourceTree = "<group>"; };
4655
33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryDependencyContainerTests.m; sourceTree = "<group>"; };
@@ -75,6 +84,7 @@
7584
3360896929524163007C7730 = {
7685
isa = PBXGroup;
7786
children = (
87+
339C6C432D3FD41C00CA72ED /* TestAssets */,
7888
33AFE0122B8F319000AAB120 /* RNSentry */,
7989
3360899029524164007C7730 /* RNSentryCocoaTesterTests */,
8090
3360897329524163007C7730 /* Products */,
@@ -94,6 +104,7 @@
94104
3360899029524164007C7730 /* RNSentryCocoaTesterTests */ = {
95105
isa = PBXGroup;
96106
children = (
107+
339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */,
97108
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */,
98109
332D334A2CDCC8EB00547D76 /* RNSentryCocoaTesterTests-Bridging-Header.h */,
99110
332D33492CDCC8E100547D76 /* RNSentryTests.h */,
@@ -120,9 +131,20 @@
120131
path = Replay;
121132
sourceTree = "<group>";
122133
};
134+
339C6C432D3FD41C00CA72ED /* TestAssets */ = {
135+
isa = PBXGroup;
136+
children = (
137+
339C6C4A2D3FD9AB00CA72ED /* valid.options.json */,
138+
339C6C462D3FD91900CA72ED /* invalid.options.json */,
139+
339C6C452D3FD90200CA72ED /* invalid.options.txt */,
140+
);
141+
path = TestAssets;
142+
sourceTree = "<group>";
143+
};
123144
33AFE0122B8F319000AAB120 /* RNSentry */ = {
124145
isa = PBXGroup;
125146
children = (
147+
339C6C442D3FD62D00CA72ED /* RNSentrySDK+Test.h */,
126148
339C6C3D2D3FA04D00CA72ED /* RNSentryVersion.h */,
127149
333B58AF2D36A7FD000F8D04 /* RNSentrySDK.h */,
128150
333B58A92D35BB2D000F8D04 /* RNSentryStart+Test.h */,
@@ -157,6 +179,7 @@
157179
3360898929524164007C7730 /* Sources */,
158180
BB7D14838753E6599863899B /* Frameworks */,
159181
CC7959F3721CB3AD7CB6A047 /* [CP] Copy Pods Resources */,
182+
339C6C472D3FD99900CA72ED /* Resources */,
160183
);
161184
buildRules = (
162185
);
@@ -201,6 +224,19 @@
201224
};
202225
/* End PBXProject section */
203226

227+
/* Begin PBXResourcesBuildPhase section */
228+
339C6C472D3FD99900CA72ED /* Resources */ = {
229+
isa = PBXResourcesBuildPhase;
230+
buildActionMask = 2147483647;
231+
files = (
232+
339C6C482D3FD9A700CA72ED /* invalid.options.json in Resources */,
233+
339C6C4B2D3FD9B200CA72ED /* valid.options.json in Resources */,
234+
339C6C492D3FD9A700CA72ED /* invalid.options.txt in Resources */,
235+
);
236+
runOnlyForDeploymentPostprocessing = 0;
237+
};
238+
/* End PBXResourcesBuildPhase section */
239+
204240
/* Begin PBXShellScriptBuildPhase section */
205241
30F19D4E16BEEFEC68733838 /* [CP] Check Pods Manifest.lock */ = {
206242
isa = PBXShellScriptBuildPhase;
@@ -252,6 +288,7 @@
252288
332D33472CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift in Sources */,
253289
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */,
254290
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */,
291+
339C6C422D3FD3AE00CA72ED /* RNSentryStartFromFileTests.swift in Sources */,
255292
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */,
256293
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */,
257294
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */,

packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryCocoaTesterTests-Bridging-Header.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
#import "RNSentryReplayBreadcrumbConverter.h"
88
#import "RNSentryReplayMask.h"
99
#import "RNSentryReplayUnmask.h"
10-
#import "RNSentrySDK.h"
10+
#import "RNSentrySDK+Test.h"
1111
#import "RNSentryStart.h"
1212
#import "RNSentryVersion.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import XCTest
2+
3+
final class RNSentryStartFromFileTests: XCTestCase {
4+
5+
func testNoThrowOnMissingOptionsFile() {
6+
var wasConfigurationCalled = false
7+
8+
RNSentrySDK.start(getNonExistingOptionsPath(), configureOptions: { _ in
9+
wasConfigurationCalled = true
10+
})
11+
12+
XCTAssertTrue(wasConfigurationCalled)
13+
14+
let actualOptions = PrivateSentrySDKOnly.options
15+
XCTAssertNil(actualOptions.dsn)
16+
XCTAssertNil(actualOptions.parsedDsn)
17+
}
18+
19+
func testNoThrowOnInvalidFileType() {
20+
var wasConfigurationCalled = false
21+
22+
RNSentrySDK.start(getInvalidOptionsTypePath(), configureOptions: { _ in
23+
wasConfigurationCalled = true
24+
})
25+
26+
XCTAssertTrue(wasConfigurationCalled)
27+
28+
let actualOptions = PrivateSentrySDKOnly.options
29+
XCTAssertNil(actualOptions.dsn)
30+
XCTAssertNil(actualOptions.parsedDsn)
31+
}
32+
33+
func testNoThrowOnInvalidOptions() {
34+
var wasConfigurationCalled = false
35+
36+
RNSentrySDK.start(getInvalidOptionsPath(), configureOptions: { _ in
37+
wasConfigurationCalled = true
38+
})
39+
40+
XCTAssertTrue(wasConfigurationCalled)
41+
42+
let actualOptions = PrivateSentrySDKOnly.options
43+
XCTAssertNil(actualOptions.dsn)
44+
XCTAssertNotNil(actualOptions.parsedDsn)
45+
XCTAssertEqual(actualOptions.environment, "environment-from-invalid-file")
46+
}
47+
48+
func testLoadValidOptions() {
49+
var wasConfigurationCalled = false
50+
51+
RNSentrySDK.start(getValidOptionsPath(), configureOptions: { _ in
52+
wasConfigurationCalled = true
53+
})
54+
55+
XCTAssertTrue(wasConfigurationCalled)
56+
57+
let actualOptions = PrivateSentrySDKOnly.options
58+
XCTAssertNil(actualOptions.dsn)
59+
XCTAssertNotNil(actualOptions.parsedDsn)
60+
XCTAssertEqual(actualOptions.environment, "environment-from-valid-file")
61+
}
62+
63+
func testOptionsFromFileInConfigureOptions() {
64+
var wasConfigurationCalled = false
65+
66+
RNSentrySDK.start(getValidOptionsPath()) { options in
67+
wasConfigurationCalled = true
68+
XCTAssertEqual(options.environment, "environment-from-valid-file")
69+
}
70+
71+
XCTAssertTrue(wasConfigurationCalled)
72+
}
73+
74+
func testOptionsOverwrittenInConfigureOptions() {
75+
RNSentrySDK.start(getValidOptionsPath()) { options in
76+
options.environment = "new-environment"
77+
}
78+
79+
let actualOptions = PrivateSentrySDKOnly.options
80+
XCTAssertEqual(actualOptions.environment, "new-environment")
81+
}
82+
83+
func getNonExistingOptionsPath() -> String {
84+
return "/non-existing.options.json"
85+
}
86+
87+
func getInvalidOptionsTypePath() -> String {
88+
guard let path = getTestBundle().path(forResource: "invalid.options", ofType: "txt") else {
89+
fatalError("Could not get invalid type options path")
90+
}
91+
return path
92+
}
93+
94+
func getInvalidOptionsPath() -> String {
95+
guard let path = getTestBundle().path(forResource: "invalid.options", ofType: "json") else {
96+
fatalError("Could not get invalid options path")
97+
}
98+
return path
99+
}
100+
101+
func getValidOptionsPath() -> String {
102+
guard let path = getTestBundle().path(forResource: "valid.options", ofType: "json") else {
103+
fatalError("Could not get invalid options path")
104+
}
105+
return path
106+
}
107+
108+
func getTestBundle() -> Bundle {
109+
let maybeBundle = Bundle.allBundles.first(where: { $0.bundlePath.hasSuffix(".xctest") })
110+
guard let bundle = maybeBundle else {
111+
fatalError("Could not find test bundle")
112+
}
113+
return bundle
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#import "RNSentrySDK.h"
2+
3+
@interface
4+
RNSentrySDK (Test)
5+
6+
+ (void)start:(NSString *)path
7+
configureOptions:(void (^)(SentryOptions *_Nonnull options))configureOptions;
8+
9+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dsn": "https://[email protected]/123456",
3+
"environment": "environment-from-invalid-file",
4+
"invalid-option": 123
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
invalid-options
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"dsn": "https://[email protected]/123456",
3+
"environment": "environment-from-valid-file"
4+
}

packages/core/ios/RNSentrySDK.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,21 @@ SENTRY_NO_INIT
55

66
/**
77
* @experimental
8-
* Inits and configures Sentry for React Native applications. Make sure to
9-
* set a valid DSN.
8+
* Inits and configures Sentry for React Native applications using `sentry.options.json`
9+
* configuration file.
10+
*
11+
* @discussion Call this method on the main thread. When calling it from a background thread, the
12+
* SDK starts on the main thread async.
13+
*/
14+
+ (void)start;
15+
16+
/**
17+
* @experimental
18+
* Inits and configures Sentry for React Native applicationsusing `sentry.options.json`
19+
* configuration file and `configureOptions` callback.
20+
*
21+
* The `configureOptions` callback can overwrite the config file options
22+
* and add non-serializable items to the options object.
1023
*
1124
* @discussion Call this method on the main thread. When calling it from a background thread, the
1225
* SDK starts on the main thread async.

packages/core/ios/RNSentrySDK.m

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,65 @@
11
#import "RNSentrySDK.h"
22
#import "RNSentryStart.h"
33

4+
static NSString *SENTRY_OPTIONS_RESOURCE_NAME = @"sentry.options";
5+
static NSString *SENTRY_OPTIONS_RESOURCE_TYPE = @"json";
6+
47
@implementation RNSentrySDK
58

9+
+ (void)start
10+
{
11+
[self startWithConfigureOptions:nil];
12+
}
13+
614
+ (void)startWithConfigureOptions:(void (^)(SentryOptions *options))configureOptions
715
{
8-
SentryOptions *options = [[SentryOptions alloc] init];
16+
NSString *path = [[NSBundle mainBundle] pathForResource:SENTRY_OPTIONS_RESOURCE_NAME
17+
ofType:SENTRY_OPTIONS_RESOURCE_TYPE];
18+
19+
[self start:path configureOptions:configureOptions];
20+
}
21+
22+
+ (void)start:(NSString *)path configureOptions:(void (^)(SentryOptions *options))configureOptions
23+
{
24+
NSError *readError = nil;
25+
NSError *parseError = nil;
26+
NSError *optionsError = nil;
27+
28+
NSData *_Nullable content = nil;
29+
if (path != nil) {
30+
content = [NSData dataWithContentsOfFile:path options:0 error:&readError];
31+
}
32+
33+
NSDictionary *dict = nil;
34+
if (content != nil) {
35+
dict = [NSJSONSerialization JSONObjectWithData:content options:0 error:&parseError];
36+
}
37+
38+
if (readError != nil) {
39+
NSLog(@"[RNSentry] Failed to load options from %@, with error: %@", path,
40+
readError.localizedDescription);
41+
}
42+
43+
if (parseError != nil) {
44+
NSLog(@"[RNSentry] Failed to parse JSON from %@, with error: %@", path,
45+
parseError.localizedDescription);
46+
}
47+
48+
SentryOptions *options = nil;
49+
if (dict != nil) {
50+
options = [RNSentryStart createOptionsWithDictionary:dict error:&optionsError];
51+
}
52+
53+
if (optionsError != nil) {
54+
NSLog(@"[RNSentry] Failed to parse options from %@, with error: %@", path,
55+
optionsError.localizedDescription);
56+
}
57+
58+
if (options == nil) {
59+
// Fallback in case that options file could not be parsed.
60+
options = [[SentryOptions alloc] init];
61+
}
62+
963
[RNSentryStart updateWithReactDefaults:options];
1064
if (configureOptions != nil) {
1165
configureOptions(options);

packages/core/ios/RNSentryStart.m

+4-1
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
5050
}
5151

5252
// Exclude Dev Server and Sentry Dsn request from Breadcrumbs
53-
// TODO: Migrate for manual init
5453
NSString *dsn = [self getURLFromDSN:[mutableOptions valueForKey:@"dsn"]];
54+
// TODO: For Auto Init from JS dev server is resolved automatically, for init from options file
55+
// dev server has to be specified manually
5556
NSString *devServerUrl = [mutableOptions valueForKey:@"devServerUrl"];
5657
sentryOptions.beforeBreadcrumb
5758
= ^SentryBreadcrumb *_Nullable(SentryBreadcrumb *_Nonnull breadcrumb)
@@ -86,6 +87,8 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
8687
sentryOptions.spotlightUrl = spotlightValue;
8788
} else if ([spotlightValue isKindOfClass:[NSNumber class]]) {
8889
sentryOptions.enableSpotlight = [spotlightValue boolValue];
90+
// TODO: For Auto init from JS set automatically for init from options file have to be
91+
// set manually
8992
id defaultSpotlightUrl = [mutableOptions valueForKey:@"defaultSidecarUrl"];
9093
if (defaultSpotlightUrl != nil) {
9194
sentryOptions.spotlightUrl = defaultSpotlightUrl;

packages/core/scripts/sentry-xcode.sh

+19
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,22 @@ fi
5151
if [ -f "$SENTRY_COLLECT_MODULES" ]; then
5252
/bin/sh "$SENTRY_COLLECT_MODULES"
5353
fi
54+
55+
SENTRY_OPTIONS_FILE_ERROR_MESSAGE_POSTFIX="Skipping options file copy. To disable this behavior, set SENTRY_COPY_OPTIONS_FILE=false in your environment variables."
56+
SENTRY_OPTIONS_FILE_NAME="sentry.options.json"
57+
SENTRY_OPTIONS_FILE_DESTINATION_PATH="$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/$SENTRY_OPTIONS_FILE_NAME"
58+
[ -z "$SENTRY_OPTIONS_FILE_PATH" ] && SENTRY_OPTIONS_FILE_PATH="$RN_PROJECT_ROOT/$SENTRY_OPTIONS_FILE_NAME"
59+
[ -z "$SENTRY_COPY_OPTIONS_FILE" ] && SENTRY_COPY_OPTIONS_FILE=true
60+
61+
if [ "$SENTRY_COPY_OPTIONS_FILE" = true ]; then
62+
if [[ -z "$CONFIGURATION_BUILD_DIR" ]]; then
63+
echo "[Sentry] CONFIGURATION_BUILD_DIR is not set. $SENTRY_OPTIONS_FILE_ERROR_MESSAGE_POSTFIX" 1>&2
64+
elif [[ -z "$UNLOCALIZED_RESOURCES_FOLDER_PATH" ]]; then
65+
echo "[Sentry] UNLOCALIZED_RESOURCES_FOLDER_PATH is not set. $SENTRY_OPTIONS_FILE_ERROR_MESSAGE_POSTFIX" 1>&2
66+
elif [ ! -f "$SENTRY_OPTIONS_FILE_PATH" ]; then
67+
echo "[Sentry] $SENTRY_OPTIONS_FILE_PATH not found. $SENTRY_OPTIONS_FILE_ERROR_MESSAGE_POSTFIX" 1>&2
68+
else
69+
cp "$SENTRY_OPTIONS_FILE_PATH" "$SENTRY_OPTIONS_FILE_DESTINATION_PATH"
70+
echo "[Sentry] Copied $SENTRY_OPTIONS_FILE_PATH to $SENTRY_OPTIONS_FILE_DESTINATION_PATH"
71+
fi
72+
fi

0 commit comments

Comments
 (0)