Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to pass custom sdk for fullstory #60

Merged
merged 1 commit into from
Jan 21, 2022
Merged
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
69 changes: 26 additions & 43 deletions src/SentryFullStory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import * as Sentry from '@sentry/browser';
import { Event, EventHint } from '@sentry/types';
import * as FullStory from '@fullstory/browser';
leeandher marked this conversation as resolved.
Show resolved Hide resolved

import * as util from './util';
import { doesFullStoryExist, getOriginalExceptionProperties, getSentryUrl } from './util';
import { FullStoryClient } from './types';

/**
* This integration creates a link from the Sentry Error to the FullStory replay.
Expand All @@ -12,79 +13,61 @@ import * as util from './util';

type Options = {
baseSentryUrl?: string;
client?: FullStoryClient;
};

class SentryFullStory {
public readonly name: string = SentryFullStory.id;
public static id: string = 'SentryFullStory';
sentryOrg: string;
baseSentryUrl: string;
client: FullStoryClient;

constructor(sentryOrg: string, options: Options = {}) {
this.sentryOrg = sentryOrg;
this.client = options.client || FullStory;
this.baseSentryUrl = options.baseSentryUrl || 'https://sentry.io';
}

private static doesFullStoryExist() {
return !!window[window['_fs_namespace']];
}

setupOnce() {
Sentry.addGlobalEventProcessor((event: Event, hint?: EventHint) => {
const self = Sentry.getCurrentHub().getIntegration(SentryFullStory);
// Run the integration ONLY when it was installed on the current Hub AND isn't a transaction
if (self && event.type !== 'transaction' && doesFullStoryExist()) {

const getSentryUrl = (): string => {
// Returns the sentry URL of the error
// If we cannot get the URL, return a string saying we cannot
try {
// No docs on this but the SDK team assures me it works unless you bind another Sentry client
const { dsn } = Sentry.getCurrentHub().getClient()?.getOptions() || {};
if (!dsn) {
console.error('No DSN');
return 'Could not retrieve url';
}
if (!hint) {
console.error('No event hint');
return 'Could not retrieve url';
const getFullStoryUrl = () => {
// getCurrentSessionURL isn't available until after the FullStory script is fully bootstrapped.
// If an error occurs before getCurrentSessionURL is ready, make a note in Sentry and move on.
// More on getCurrentSessionURL here: https://help.fullstory.com/develop-js/getcurrentsessionurl
try {
return this.client.getCurrentSessionURL(true) || 'Current session URL API not ready'
} catch (e) {
const reason = e instanceof Error ? e.message : String(e)
return `Unable to get url: ${reason}`
}
const projectId = util.getProjectIdFromSentryDsn(dsn);
return `${this.baseSentryUrl}/organizations/${this.sentryOrg}/issues/?project=${projectId}&query=${hint.event_id}`;
} catch (err) {
console.error('Error retrieving project ID from DSN', err);
//TODO: Could put link to a help here
return 'Could not retrieve url';
}
};

const getFullStoryUrl = (): string => {
// getCurrentSessionURL isn't available until after the FullStory script is fully bootstrapped.
// If an error occurs before getCurrentSessionURL is ready, make a note in Sentry and move on.
// More on getCurrentSessionURL here: https://help.fullstory.com/develop-js/getcurrentsessionurl
try {
return FullStory.getCurrentSessionURL(true) || 'Current session URL API not ready'
} catch (e) {
const reason = e instanceof Error ? e.message : String(e)
return `Unable to get url: ${reason}`
}
}

const self = Sentry.getCurrentHub().getIntegration(SentryFullStory);
// Run the integration ONLY when it was installed on the current Hub AND isn't a transaction
if (self && event.type !== 'transaction' && SentryFullStory.doesFullStoryExist()) {
event.contexts = {
...event.contexts,
fullStory: {
fullStoryUrl: getFullStoryUrl()
},
};

try {
FullStory.event('Sentry Error', {
sentryUrl: getSentryUrl(),
...util.getOriginalExceptionProperties(hint),
this.client.event('Sentry Error', {
sentryUrl: getSentryUrl({
baseSentryUrl: this.baseSentryUrl,
sentryOrg: this.sentryOrg,
hint,
}),
...getOriginalExceptionProperties(hint),
});
} catch (e) {
console.debug('Unable to report sentry error details to FullStory')
}
}

return event;
});
}
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface FullStoryClient {
event(eventName: string, eventProperties: { [key: string]: any }): void;
getCurrentSessionURL(now?: boolean): string | null;
}
33 changes: 33 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { EventHint } from '@sentry/types';
import * as Sentry from '@sentry/browser';

/**
* Returns true if Fullstory is installed correctly.
*/
export function doesFullStoryExist() {
return !!window[window['_fs_namespace']];
}

/**
* Get the project ID from a Sentry DSN
Expand All @@ -25,3 +33,28 @@ export const getOriginalExceptionProperties = (hint?: EventHint) => {

return {};
};

/**
* Returns the sentry URL of the error. If we cannot get the URL, return a
* string saying we cannot.
*/
export function getSentryUrl(args: { hint?: EventHint, sentryOrg: string, baseSentryUrl: string }) {
try {
// No docs on this but the SDK team assures me it works unless you bind another Sentry client
const { dsn } = Sentry.getCurrentHub().getClient()?.getOptions() || {};
if (!dsn) {
console.error('No sn');
return 'Could not retrieve url';
}
if (!args.hint) {
console.error('No event hint');
return 'Could not retrieve url';
}
const projectId = getProjectIdFromSentryDsn(dsn);
return `${args.baseSentryUrl}/organizations/${args.sentryOrg}/issues/?project=${projectId}&query=${args.hint.event_id}`;
} catch (err) {
console.error('Error retrieving project ID from DSN', err);
//TODO: Could put link to a help here
return 'Could not retrieve url';
}
}