-
-
Notifications
You must be signed in to change notification settings - Fork 338
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(integrations): Add Spotlight integration (#3550)
- Loading branch information
1 parent
58b3261
commit eb53e59
Showing
13 changed files
with
279 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import type { Client, Envelope, EventProcessor, Integration } from '@sentry/types'; | ||
import { logger, serializeEnvelope } from '@sentry/utils'; | ||
|
||
import { makeUtf8TextEncoder } from '../transports/TextEncoder'; | ||
import { ReactNativeLibraries } from '../utils/rnlibraries'; | ||
|
||
type SpotlightReactNativeIntegrationOptions = { | ||
/** | ||
* The URL of the Sidecar instance to connect and forward events to. | ||
* If not set, Spotlight will try to connect to the Sidecar running on localhost:8969. | ||
* | ||
* @default "http://localhost:8969/stream" | ||
*/ | ||
sidecarUrl?: string; | ||
}; | ||
|
||
/** | ||
* Use this integration to send errors and transactions to Spotlight. | ||
* | ||
* Learn more about spotlight at https://spotlightjs.com | ||
*/ | ||
export function Spotlight({ | ||
sidecarUrl = getDefaultSidecarUrl(), | ||
}: SpotlightReactNativeIntegrationOptions = {}): Integration { | ||
logger.info('[Spotlight] Using Sidecar URL', sidecarUrl); | ||
|
||
return { | ||
name: 'Spotlight', | ||
|
||
setupOnce(_: (callback: EventProcessor) => void, getCurrentHub) { | ||
const client = getCurrentHub().getClient(); | ||
if (client) { | ||
setup(client, sidecarUrl); | ||
} else { | ||
logger.warn('[Spotlight] Could not initialize Sidecar integration due to missing Client'); | ||
} | ||
}, | ||
}; | ||
} | ||
|
||
function setup(client: Client, sidecarUrl: string): void { | ||
sendEnvelopesToSidecar(client, sidecarUrl); | ||
} | ||
|
||
function sendEnvelopesToSidecar(client: Client, sidecarUrl: string): void { | ||
if (!client.on) { | ||
return; | ||
} | ||
|
||
client.on('beforeEnvelope', (originalEnvelope: Envelope) => { | ||
// TODO: This is a workaround for spotlight/sidecar not supporting images | ||
const spotlightEnvelope: Envelope = [...originalEnvelope]; | ||
const envelopeItems = [...originalEnvelope[1]].filter( | ||
item => typeof item[0].content_type !== 'string' || !item[0].content_type.startsWith('image'), | ||
); | ||
|
||
spotlightEnvelope[1] = envelopeItems as Envelope[1]; | ||
|
||
fetch(sidecarUrl, { | ||
method: 'POST', | ||
body: serializeEnvelope(spotlightEnvelope, makeUtf8TextEncoder()), | ||
headers: { | ||
'Content-Type': 'application/x-sentry-envelope', | ||
}, | ||
mode: 'cors', | ||
}).catch(err => { | ||
logger.error( | ||
"[Spotlight] Sentry SDK can't connect to Spotlight is it running? See https://spotlightjs.com to download it.", | ||
err, | ||
); | ||
}); | ||
}); | ||
} | ||
|
||
function getDefaultSidecarUrl(): string { | ||
try { | ||
const { url } = ReactNativeLibraries.Devtools?.getDevServer(); | ||
return `http://${getHostnameFromString(url)}:8969/stream`; | ||
} catch (_oO) { | ||
// We can't load devserver URL | ||
} | ||
return 'http://localhost:8969/stream'; | ||
} | ||
|
||
/** | ||
* React Native implementation of the URL class is missing the `hostname` property. | ||
*/ | ||
function getHostnameFromString(urlString: string): string | null { | ||
const regex = /^(?:\w+:)?\/\/([^/:]+)(:\d+)?(.*)$/; | ||
const matches = urlString.match(regex); | ||
|
||
if (matches && matches[1]) { | ||
return matches[1]; | ||
} else { | ||
// Invalid URL format | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import type { Envelope, Hub } from '@sentry/types'; | ||
import fetchMock from 'jest-fetch-mock'; | ||
|
||
import { Spotlight } from '../../src/js/integrations/spotlight'; | ||
|
||
describe('spotlight', () => { | ||
it('should not change the original envelope', () => { | ||
const mockHub = createMockHub(); | ||
|
||
const spotlight = Spotlight(); | ||
spotlight.setupOnce( | ||
() => {}, | ||
() => mockHub as unknown as Hub, | ||
); | ||
|
||
const spotlightBeforeEnvelope = mockHub.getClient().on.mock.calls[0]?.[1] as | ||
| ((envelope: Envelope) => void) | ||
| undefined; | ||
|
||
const originalEnvelopeReference = createMockEnvelope(); | ||
spotlightBeforeEnvelope?.(originalEnvelopeReference); | ||
|
||
expect(spotlightBeforeEnvelope).toBeDefined(); | ||
expect(originalEnvelopeReference).toEqual(createMockEnvelope()); | ||
}); | ||
|
||
it('should remove image attachments from spotlight envelope', () => { | ||
fetchMock.mockOnce(); | ||
const mockHub = createMockHub(); | ||
|
||
const spotlight = Spotlight(); | ||
spotlight.setupOnce( | ||
() => {}, | ||
() => mockHub as unknown as Hub, | ||
); | ||
|
||
const spotlightBeforeEnvelope = mockHub.getClient().on.mock.calls[0]?.[1] as | ||
| ((envelope: Envelope) => void) | ||
| undefined; | ||
|
||
spotlightBeforeEnvelope?.(createMockEnvelope()); | ||
|
||
expect(spotlightBeforeEnvelope).toBeDefined(); | ||
expect(fetchMock.mock.lastCall?.[1]?.body?.toString().includes('image/png')).toBe(false); | ||
}); | ||
}); | ||
|
||
function createMockHub() { | ||
const client = { | ||
on: jest.fn(), | ||
}; | ||
|
||
return { | ||
getClient: jest.fn().mockReturnValue(client), | ||
}; | ||
} | ||
|
||
function createMockEnvelope(): Envelope { | ||
return [ | ||
{ | ||
event_id: 'event_id', | ||
sent_at: 'sent_at', | ||
sdk: { | ||
name: 'sdk_name', | ||
version: 'sdk_version', | ||
}, | ||
}, | ||
[ | ||
[ | ||
{ | ||
type: 'event', | ||
length: 0, | ||
}, | ||
{ | ||
event_id: 'event_id', | ||
}, | ||
], | ||
[ | ||
{ | ||
type: 'attachment', | ||
length: 10, | ||
filename: 'filename', | ||
}, | ||
'attachment', | ||
], | ||
[ | ||
{ | ||
type: 'attachment', | ||
length: 8, | ||
filename: 'filename2', | ||
content_type: 'image/png', | ||
}, | ||
Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]), // PNG header | ||
], | ||
], | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { enableFetchMocks } from 'jest-fetch-mock'; | ||
enableFetchMocks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters