Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

- Adds GraphQL integration ([#5299](https://github.com/getsentry/sentry-react-native/pull/5299))

### Fix

- Sync `user.geo` from `SetUser` to the native layer ([#5302](https://github.com/getsentry/sentry-react-native/pull/5302))

### Dependencies

- Bump Bundler Plugins from v4.4.0 to v4.5.0 ([#5283](https://github.com/getsentry/sentry-react-native/pull/5283))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,90 @@ class RNSentryModuleImplTest {
val regex = Regex(options.ignoredErrors!![0].filterString)
assertTrue(regex.matches("Error*WithStar"))
}

@Test
fun `setUser with geo data creates user with correct geo properties`() {
val userKeys =
JavaOnlyMap.of(
"id",
"123",
"email",
"[email protected]",
"username",
"testuser",
"geo",
JavaOnlyMap.of(
"city",
"San Francisco",
"country_code",
"US",
"region",
"California",
),
)
val userDataKeys = JavaOnlyMap.of("customField", "customValue")

module.setUser(userKeys, userDataKeys)
}

@Test
fun `setUser with partial geo data creates user with available geo properties`() {
val userKeys =
JavaOnlyMap.of(
"id",
"123",
"geo",
JavaOnlyMap.of(
"city",
"New York",
"country_code",
"US",
),
)
val userDataKeys = JavaOnlyMap.of()

module.setUser(userKeys, userDataKeys)
}

@Test
fun `setUser with empty geo data handles empty geo object`() {
val userKeys =
JavaOnlyMap.of(
"id",
"123",
"geo",
JavaOnlyMap.of(),
)
val userDataKeys = JavaOnlyMap.of()

module.setUser(userKeys, userDataKeys)
}

@Test
fun `setUser with null geo data handles null geo gracefully`() {
val userKeys =
JavaOnlyMap.of(
"id",
"123",
"geo",
null,
)
val userDataKeys = JavaOnlyMap.of()

module.setUser(userKeys, userDataKeys)
}

@Test
fun `setUser with invalid geo data handles non-map geo gracefully`() {
val userKeys =
JavaOnlyMap.of(
"id",
"123",
"geo",
"invalid_geo_data",
)
val userDataKeys = JavaOnlyMap.of()

module.setUser(userKeys, userDataKeys)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,63 @@ - (void)testNullValuesUser
XCTAssertTrue([actual isEqualToUser:expected]);
}

- (void)testUserWithGeo
{
SentryUser *expected = [[SentryUser alloc] init];
[expected setUserId:@"123"];
[expected setEmail:@"[email protected]"];
[expected setUsername:@"testuser"];
[expected setData:@{
@"geo" :
@ { @"city" : @"San Francisco", @"country_code" : @"US", @"region" : @"California" }
}];

SentryUser *actual = [RNSentry userFrom:@{
@"id" : @"123",
@"email" : @"[email protected]",
@"username" : @"testuser",
@"geo" :
@ { @"city" : @"San Francisco", @"country_code" : @"US", @"region" : @"California" }
}
otherUserKeys:nil];

XCTAssertTrue([actual isEqualToUser:expected]);
}

- (void)testUserWithPartialGeo
{
SentryUser *expected = [[SentryUser alloc] init];
[expected setUserId:@"123"];
[expected setData:@{ @"geo" : @ { @"city" : @"New York", @"country_code" : @"US" } }];

SentryUser *actual = [RNSentry userFrom:@{
@"id" : @"123",
@"geo" : @ { @"city" : @"New York", @"country_code" : @"US" }
}
otherUserKeys:nil];

XCTAssertTrue([actual isEqualToUser:expected]);
}

- (void)testUserWithEmptyGeo
{
SentryUser *expected = [[SentryUser alloc] init];
[expected setUserId:@"123"];

SentryUser *actual = [RNSentry userFrom:@{ @"id" : @"123", @"geo" : @ {} } otherUserKeys:nil];

XCTAssertTrue([actual isEqualToUser:expected]);
}

- (void)testUserWithInvalidGeo
{
SentryUser *expected = [[SentryUser alloc] init];
[expected setUserId:@"123"];

SentryUser *actual = [RNSentry userFrom:@{ @"id" : @"123", @"geo" : @"invalid_geo_data" }
otherUserKeys:nil];

XCTAssertTrue([actual isEqualToUser:expected]);
}

@end
Binary file modified packages/core/android/libs/replay-stubs.jar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we don't need to change the stub.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import io.sentry.android.core.internal.debugmeta.AssetsDebugMetaLoader;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.android.core.performance.AppStartMetrics;
import io.sentry.protocol.Geo;
import io.sentry.protocol.SdkVersion;
import io.sentry.protocol.SentryId;
import io.sentry.protocol.SentryPackage;
Expand Down Expand Up @@ -722,6 +723,23 @@ public void setUser(final ReadableMap userKeys, final ReadableMap userDataKeys)
if (userKeys.hasKey("ip_address")) {
userInstance.setIpAddress(userKeys.getString("ip_address"));
}

if (userKeys.hasKey("geo")) {
ReadableMap geoMap = userKeys.getMap("geo");
if (geoMap != null) {
Geo geoData = new Geo();
if (geoMap.hasKey("city")) {
geoData.setCity(geoMap.getString("city"));
}
if (geoMap.hasKey("country_code")) {
geoData.setCountryCode(geoMap.getString("country_code"));
}
if (geoMap.hasKey("region")) {
geoData.setRegion(geoMap.getString("region"));
}
userInstance.setGeo(geoData);
}
}
}

if (userDataKeys != null) {
Expand Down
25 changes: 25 additions & 0 deletions packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,31 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys
[userInstance setUsername:username];
}

id geo = [userKeys valueForKey:@"geo"];
if ([geo isKindOfClass:NSDictionary.class]) {
NSDictionary *geoDict = (NSDictionary *)geo;
NSMutableDictionary *geoData = [[NSMutableDictionary alloc] init];

id city = [geoDict valueForKey:@"city"];
if ([city isKindOfClass:NSString.class]) {
[geoData setObject:city forKey:@"city"];
}

id countryCode = [geoDict valueForKey:@"country_code"];
if ([countryCode isKindOfClass:NSString.class]) {
[geoData setObject:countryCode forKey:@"country_code"];
}

id region = [geoDict valueForKey:@"region"];
if ([region isKindOfClass:NSString.class]) {
[geoData setObject:region forKey:@"region"];
}

if ([geoData count] > 0) {
[userInstance setData:@{ @"geo" : geoData }];
}
}

if ([userDataKeys isKindOfClass:NSDictionary.class]) {
[userInstance setData:userDataKeys];
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/js/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,13 @@ export const NATIVE: SentryNativeWrapper = {
let userKeys = null;
let userDataKeys = null;
if (user) {
const { id, ip_address, email, username, ...otherKeys } = user;
// TODO: Update native impl to use geo
const requiredUser: Omit<RequiredKeysUser, 'geo'> = {
const { id, ip_address, email, username, geo, ...otherKeys } = user;
const requiredUser: RequiredKeysUser = {
id,
ip_address,
email,
username,
geo,
};
userKeys = this._serializeObject(requiredUser);
userDataKeys = this._serializeObject(otherKeys);
Expand Down
16 changes: 16 additions & 0 deletions packages/core/test/scopeSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ describe('ScopeSync', () => {
expect(setUserScopeSpy).toHaveBeenCalledExactlyOnceWith({ id: '123' });
});

it('setUser with geo data', () => {
expect(SentryCore.getIsolationScope().setUser).not.toBe(setUserScopeSpy);
const user = {
id: '123',
email: '[email protected]',
geo: {
city: 'San Francisco',
country_code: 'US',
region: 'California',
},
};
SentryCore.setUser(user);
expect(NATIVE.setUser).toHaveBeenCalledExactlyOnceWith(user);
expect(setUserScopeSpy).toHaveBeenCalledExactlyOnceWith(user);
});

it('setTag', () => {
jest.spyOn(NATIVE, 'primitiveProcessor').mockImplementation((value: SentryCore.Primitive) => value as string);
expect(SentryCore.getIsolationScope().setTag).not.toBe(setTagScopeSpy);
Expand Down
81 changes: 81 additions & 0 deletions packages/core/test/wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,87 @@ describe('Tests Native Wrapper', () => {
{},
);
});

test('serializes user with geo data', async () => {
NATIVE.setUser({
id: '123',
email: '[email protected]',
username: 'testuser',
geo: {
city: 'San Francisco',
country_code: 'US',
region: 'California',
},
customField: 'customValue',
});

expect(RNSentry.setUser).toBeCalledWith(
{
id: '123',
email: '[email protected]',
username: 'testuser',
geo: JSON.stringify({
city: 'San Francisco',
country_code: 'US',
region: 'California',
}),
},
{
customField: 'customValue',
},
);
});

test('serializes user with partial geo data', async () => {
NATIVE.setUser({
id: '123',
geo: {
city: 'New York',
country_code: 'US',
},
});

expect(RNSentry.setUser).toBeCalledWith(
{
id: '123',
geo: JSON.stringify({
city: 'New York',
country_code: 'US',
}),
},
{},
);
});

test('serializes user with empty geo data', async () => {
NATIVE.setUser({
id: '123',
geo: {},
});

expect(RNSentry.setUser).toBeCalledWith(
{
id: '123',
geo: '{}',
},
{},
);
});

test('serializes user with undefined geo', async () => {
NATIVE.setUser({
id: '123',
geo: undefined,
});

expect(RNSentry.setUser).toBeCalledWith(
{
id: '123',
geo: undefined,
},
{},
);
});
});

describe('_processLevel', () => {
Expand Down
Loading