-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sveltekit): Add wrapper for client load function
- Loading branch information
1 parent
ac43b2f
commit f5a3b71
Showing
4 changed files
with
123 additions
and
4 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { captureException } from '@sentry/svelte'; | ||
import { addExceptionMechanism, objectify } from '@sentry/utils'; | ||
import type { ServerLoad } from '@sveltejs/kit'; | ||
|
||
/** | ||
* Wrap load function with Sentry | ||
* | ||
* @param origLoad SvelteKit user defined load function | ||
*/ | ||
export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad { | ||
return new Proxy(origLoad, { | ||
apply: async (wrappingTarget, thisArg, args: Parameters<ServerLoad>) => { | ||
try { | ||
return await wrappingTarget.apply(thisArg, args); | ||
} catch (e) { | ||
// In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can | ||
// store a seen flag on it. | ||
const objectifiedErr = objectify(e) as unknown; | ||
|
||
captureException(objectifiedErr, scope => { | ||
scope.addEventProcessor(event => { | ||
addExceptionMechanism(event, { | ||
type: 'sveltekit', | ||
handled: false, | ||
data: { | ||
function: 'load', | ||
}, | ||
}); | ||
return event; | ||
}); | ||
|
||
return scope; | ||
}); | ||
|
||
throw objectifiedErr; | ||
} | ||
}, | ||
}); | ||
} |
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,80 @@ | ||
import { Scope } from '@sentry/svelte'; | ||
import type { ServerLoad } from '@sveltejs/kit'; | ||
import { vi } from 'vitest'; | ||
|
||
import { wrapLoadWithSentry } from '../../src/client/load'; | ||
|
||
const mockCaptureException = vi.fn(); | ||
let mockScope = new Scope(); | ||
|
||
vi.mock('@sentry/svelte', async () => { | ||
const original = (await vi.importActual('@sentry/svelte')) as any; | ||
return { | ||
...original, | ||
captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { | ||
cb(mockScope); | ||
mockCaptureException(err, cb); | ||
return original.captureException(err, cb); | ||
}, | ||
}; | ||
}); | ||
|
||
const mockAddExceptionMechanism = vi.fn(); | ||
|
||
vi.mock('@sentry/utils', async () => { | ||
const original = (await vi.importActual('@sentry/utils')) as any; | ||
return { | ||
...original, | ||
addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args), | ||
}; | ||
}); | ||
|
||
function getById(_id?: string) { | ||
throw new Error('error'); | ||
} | ||
|
||
describe('wrapLoadWithSentry', () => { | ||
beforeEach(() => { | ||
mockCaptureException.mockClear(); | ||
mockAddExceptionMechanism.mockClear(); | ||
mockScope = new Scope(); | ||
}); | ||
|
||
it('calls captureException', async () => { | ||
async function load({ params }: Parameters<ServerLoad>[0]): Promise<ReturnType<ServerLoad>> { | ||
return { | ||
post: getById(params.id), | ||
}; | ||
} | ||
|
||
const wrappedLoad = wrapLoadWithSentry(load); | ||
const res = wrappedLoad({ params: { id: '1' } } as any); | ||
await expect(res).rejects.toThrow(); | ||
|
||
expect(mockCaptureException).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('adds an exception mechanism', async () => { | ||
const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { | ||
void callback({}, { event_id: 'fake-event-id' }); | ||
return mockScope; | ||
}); | ||
|
||
async function load({ params }: Parameters<ServerLoad>[0]): Promise<ReturnType<ServerLoad>> { | ||
return { | ||
post: getById(params.id), | ||
}; | ||
} | ||
|
||
const wrappedLoad = wrapLoadWithSentry(load); | ||
const res = wrappedLoad({ params: { id: '1' } } as any); | ||
await expect(res).rejects.toThrow(); | ||
|
||
expect(addEventProcessorSpy).toBeCalledTimes(1); | ||
expect(mockAddExceptionMechanism).toBeCalledTimes(1); | ||
expect(mockAddExceptionMechanism).toBeCalledWith( | ||
{}, | ||
{ handled: false, type: 'sveltekit', data: { function: 'load' } }, | ||
); | ||
}); | ||
}); |