Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Features

- Expose scope-level attributes API (`setAttribute`, `setAttributes`, `removeAttribute`) bridging to native SDKs ([#6009](https://github.com/getsentry/sentry-react-native/pull/6009))
- Warn Expo users at Metro startup when prebuilt native projects are missing Sentry configuration ([#5984](https://github.com/getsentry/sentry-react-native/pull/5984))

### Dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.sentry.ISerializer;
import io.sentry.ScopesAdapter;
import io.sentry.Sentry;
import io.sentry.SentryAttributes;
import io.sentry.SentryDate;
import io.sentry.SentryDateProvider;
import io.sentry.SentryExecutorService;
Expand Down Expand Up @@ -677,23 +678,25 @@ public void setTag(String key, String value) {
}

public void setAttribute(String key, String value) {
// TODO(alwx): This is not implemented in sentry-android yet
/*
* Sentry.configureScope(
* scope -> {
* scope.setAttribute(key, value);
* });
*/
Sentry.configureScope(
scope -> {
scope.setAttribute(key, value);
});
}

public void setAttributes(ReadableMap attributes) {
// TODO(alwx): This is not implemented in sentry-android yet
/*
* Sentry.configureScope(
* scope -> {
* scope.setAttributes(attributes);
* });
*/
Sentry.configureScope(
scope -> {
final Map<String, Object> attributesHashMap = attributes.toHashMap();
scope.setAttributes(SentryAttributes.fromMap(attributesHashMap));
});
}

public void removeAttribute(String key) {
Sentry.configureScope(
scope -> {
scope.removeAttribute(key);
});
}

public void closeNativeSdk(Promise promise) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ public void setAttributes(ReadableMap attributes) {
this.impl.setAttributes(attributes);
}

@Override
public void removeAttribute(String key) {
this.impl.removeAttribute(key);
}

@Override
public void closeNativeSdk(Promise promise) {
this.impl.closeNativeSdk(promise);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ public void setAttributes(ReadableMap attributes) {
this.impl.setAttributes(attributes);
}

@ReactMethod
public void removeAttribute(String key) {
this.impl.removeAttribute(key);
}

@ReactMethod
public void closeNativeSdk(Promise promise) {
this.impl.closeNativeSdk(promise);
Expand Down
20 changes: 12 additions & 8 deletions packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -758,18 +758,22 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys

RCT_EXPORT_METHOD(setAttribute : (NSString *)key value : (NSString *)value)
{
// TODO(alwx): This is not implemented in sentry-cocoa yet
/*[SentrySDKWrapper
configureScope:^(SentryScope *_Nonnull scope) { [scope setAttribute:value forKey:key]; }];*/
[SentrySDKWrapper configureScope:^(
SentryScope *_Nonnull scope) { [scope setAttributeValue:value forKey:key]; }];
}

RCT_EXPORT_METHOD(setAttributes : (NSDictionary *)attributes)
{
// TODO(alwx): This is not implemented in sentry-cocoa yet
/*[SentrySDKWrapper configureScope:^(SentryScope *_Nonnull scope) {
[attributes enumerateKeysAndObjectsUsingBlock:^(
NSString *key, NSString *value, BOOL *stop) { [scope setAttribute:value forKey:key]; }];
}];*/
[SentrySDKWrapper configureScope:^(SentryScope *_Nonnull scope) {
[attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value,
BOOL *stop) { [scope setAttributeValue:value forKey:key]; }];
Comment thread
antonis marked this conversation as resolved.
}];
}

Comment thread
antonis marked this conversation as resolved.
RCT_EXPORT_METHOD(removeAttribute : (NSString *)key)
{
[SentrySDKWrapper
configureScope:^(SentryScope *_Nonnull scope) { [scope removeAttributeForKey:key]; }];
}

RCT_EXPORT_METHOD(crash) { [SentrySDKWrapper crash]; }
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/js/NativeRNSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface Spec extends TurboModule {
setTag(key: string, value: string): void;
setAttribute(key: string, value: string): void;
setAttributes(attributes: UnsafeObject): void;
removeAttribute(key: string): void;
enableNativeFramesTracking(): void;
fetchModules(): Promise<string | undefined | null>;
fetchViewHierarchy(): Promise<number[] | undefined | null>;
Expand Down
38 changes: 19 additions & 19 deletions packages/core/src/js/scopeSync.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Breadcrumb, Scope } from '@sentry/core';

import { debug } from '@sentry/core';
import { logger } from '@sentry/react';

import { DEFAULT_BREADCRUMB_LEVEL } from './breadcrumb';
Expand Down Expand Up @@ -84,30 +83,31 @@ export function enableSyncToNative(scope: Scope): void {
});

fillTyped(scope, 'setAttribute', original => (key: string, value: unknown): Scope => {
debug.warn('This feature is currently not supported.');
// Only sync primitive types
// Native layer still not supported
// if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
// NATIVE.setAttribute(key, value);
// }
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
NATIVE.setAttribute(key, value);
}
return original.call(scope, key, value);
});

fillTyped(scope, 'setAttributes', original => (attributes: Record<string, unknown>): Scope => {
// Native layer not supported
debug.warn('This feature is currently not supported.');
// Filter to only primitive types
// const primitiveAttrs: Record<string, string | number | boolean> = {};
// Object.keys(attributes).forEach(key => {
// const value = attributes[key];
// if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
// primitiveAttrs[key] = value;
// }
// });
//
// if (Object.keys(primitiveAttrs).length > 0) {
// NATIVE.setAttributes(primitiveAttrs);
// }
const primitiveAttrs: Record<string, string | number | boolean> = {};
Object.keys(attributes).forEach(key => {
const value = attributes[key];
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
primitiveAttrs[key] = value;
}
});

if (Object.keys(primitiveAttrs).length > 0) {
NATIVE.setAttributes(primitiveAttrs);
}
return original.call(scope, attributes);
});

fillTyped(scope, 'removeAttribute', original => (key: string): Scope => {
NATIVE.removeAttribute(key);
return original.call(scope, key);
});
}
16 changes: 16 additions & 0 deletions packages/core/src/js/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ interface SentryNativeWrapper {
setTag(key: string, value?: string): void;
setAttribute(key: string, value: string | number | boolean): void;
setAttributes(attributes: Record<string, string | number | boolean>): void;
removeAttribute(key: string): void;

nativeCrash(): void;

Expand Down Expand Up @@ -610,6 +611,21 @@ export const NATIVE: SentryNativeWrapper = {
RNSentry.setAttributes(serializedAttributes);
},

/**
* Removes an attribute from the native scope.
* @param key string
*/
removeAttribute(key: string): void {
if (!this.enableNative) {
return;
}
if (!this._isModuleLoaded(RNSentry)) {
throw this._NativeClientError;
}

RNSentry.removeAttribute(key);
},

/**
* Closes the Native Layer SDK
*/
Expand Down
1 change: 1 addition & 0 deletions packages/core/test/mockWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const NATIVE: MockInterface<NativeType> = {
setTag: jest.fn(),
setAttribute: jest.fn(),
setAttributes: jest.fn(),
removeAttribute: jest.fn(),

nativeCrash: jest.fn(),

Expand Down
16 changes: 10 additions & 6 deletions packages/core/test/scopeSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,9 @@ describe('ScopeSync', () => {
let setExtrasScopeSpy: jest.SpyInstance;
let addBreadcrumbScopeSpy: jest.SpyInstance;
let setContextScopeSpy: jest.SpyInstance;
/*
TODO: Uncomment once Native setattribute is implemented.
let setAttributeScopeSpy: jest.SpyInstance;
let setAttributesScopeSpy: jest.SpyInstance;
*/
let removeAttributeScopeSpy: jest.SpyInstance;

beforeAll(() => {
const testScope = SentryCore.getIsolationScope();
Expand All @@ -135,6 +133,7 @@ describe('ScopeSync', () => {
setContextScopeSpy = jest.spyOn(testScope, 'setContext');
setAttributeScopeSpy = jest.spyOn(testScope, 'setAttribute');
setAttributesScopeSpy = jest.spyOn(testScope, 'setAttributes');
removeAttributeScopeSpy = jest.spyOn(testScope, 'removeAttribute');
});

beforeEach(() => {
Expand Down Expand Up @@ -224,8 +223,6 @@ describe('ScopeSync', () => {
expect(setContextScopeSpy).toHaveBeenCalledExactlyOnceWith('key', { key: 'value' });
});

/*
TODO: uncomment tests once native implementation is done.
it('setAttribute', () => {
expect(SentryCore.getIsolationScope().setAttribute).not.toBe(setAttributeScopeSpy);

Expand Down Expand Up @@ -290,6 +287,13 @@ describe('ScopeSync', () => {
});
expect(NATIVE.setAttributes).not.toHaveBeenCalled();
});
*/

it('removeAttribute', () => {
expect(SentryCore.getIsolationScope().removeAttribute).not.toBe(removeAttributeScopeSpy);

SentryCore.getIsolationScope().removeAttribute('session_id');
expect(NATIVE.removeAttribute).toHaveBeenCalledExactlyOnceWith('session_id');
expect(removeAttributeScopeSpy).toHaveBeenCalledExactlyOnceWith('session_id');
});
});
});
Loading