diff --git a/.env.example b/.env.example index 76223f204..de248f624 100644 --- a/.env.example +++ b/.env.example @@ -67,8 +67,8 @@ E2E_LOGIN_URL='https://jetstream-e2e-dev-ed.develop.my.salesforce.com' EXAMPLE_USER_OVERRIDE='true' EXAMPLE_USER_PASSWORD='EXAMPLE_123!' -NX_PUBLIC_ROLLBAR_KEY='' NX_PUBLIC_AMPLITUDE_KEY='' +NX_PUBLIC_SENTRY_DSN='' # Credentials for sending emails # If you are not using the example user, then you may need to configure this for MFA @@ -89,8 +89,6 @@ GOOGLE_CLIENT_ID='' GOOGLE_CLIENT_SECRET='' GOOGLE_REDIRECT_URI='http://localhost:3333/oauth/google/callback' -ROLLBAR_SERVER_TOKEN='' - # Algolia API key - used to index docs pages ALGOLIA_APPLICATION_ID='' ALGOLIA_API_KEY='' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af414332f..ec868094e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,8 +12,6 @@ env: CONTENTFUL_SPACE: wuv9tl5d77ll CONTENTFUL_TOKEN: ${{ secrets.CONTENTFUL_TOKEN }} NX_CLOUD_DISTRIBUTED_EXECUTION: false - NX_PUBLIC_AMPLITUDE_KEY: ${{ secrets.NX_PUBLIC_AMPLITUDE_KEY }} - NX_PUBLIC_ROLLBAR_KEY: ${{ secrets.NX_PUBLIC_ROLLBAR_KEY }} NX_PUBLIC_CLIENT_URL: 'http://localhost:3333/app' NX_PUBLIC_SERVER_URL: 'http://localhost:3333' diff --git a/.release-it.json b/.release-it.json index a54c6bc16..b03cedaac 100644 --- a/.release-it.json +++ b/.release-it.json @@ -15,7 +15,7 @@ "releaseName": "Jetstream ${version}" }, "hooks": { - "before:init": ["yarn test:all", "SKIP_ROLLBAR=true yarn build"], - "after:release": "CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) && git checkout release && git reset --hard $CURRENT_BRANCH && git push origin release -f && git checkout main && yarn rollbar:create-deploy" + "before:init": ["yarn test:all", "SKIP_SENTRY=true yarn build"], + "after:release": "CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) && git checkout release && git reset --hard $CURRENT_BRANCH && git push origin release -f && git checkout main" } } diff --git a/apps/api/src/app/utils/response.handlers.ts b/apps/api/src/app/utils/response.handlers.ts index 7eee7f956..89ebfe420 100644 --- a/apps/api/src/app/utils/response.handlers.ts +++ b/apps/api/src/app/utils/response.handlers.ts @@ -1,4 +1,4 @@ -import { ENV, getExceptionLog, logger, prisma, rollbarServer } from '@jetstream/api-config'; +import { ENV, getExceptionLog, logger, prisma, sentryServer } from '@jetstream/api-config'; import { AuthError, createCSRFToken, getCookieConfig } from '@jetstream/auth/server'; import { ERROR_MESSAGES, HTTP } from '@jetstream/shared/constants'; import { Maybe } from '@jetstream/types'; @@ -76,14 +76,17 @@ export function sendJson(res: Response, content?: Respon if (res.headersSent) { res.log.warn('Response headers already sent'); try { - rollbarServer.warn('Response not handled by sendJson, headers already sent', new Error('headers already sent'), { - context: `route#sendJson`, - custom: { + sentryServer.captureException(new Error('headers already sent'), { + tags: { requestId: res.locals.requestId, }, + extra: { + message: 'Response not handled by sendJson, headers already sent', + location: `route#sendJson`, + }, }); } catch (ex) { - res.log.error(getExceptionLog(ex), 'Error sending to Rollbar'); + res.log.error(getExceptionLog(ex), 'Error sending to Sentry'); } return; } @@ -141,20 +144,25 @@ export async function uncaughtErrorHandler(err: any, req: express.Request, res: if (res.headersSent) { responseLogger.warn('Response headers already sent'); try { - rollbarServer.warn('Error not handled by error handler, headers already sent', req, { - context: `route#errorHandler`, - custom: { - ...getExceptionLog(err, true), + sentryServer.captureException(new Error('headers already sent'), { + user: { + id: req.session.user?.id, + email: req.session.user?.email, + name: req.session.user?.name, + }, + tags: { + requestId: res.locals.requestId, + }, + extra: { + message: 'Response not handled by sendJson, headers already sent', + location: `route#sendJson`, url: req.url, params: req.params, query: req.query, - body: req.body, - userId: req.session.user?.id, - requestId: res.locals.requestId, }, }); } catch (ex) { - responseLogger.error(getExceptionLog(ex), 'Error sending to Rollbar'); + responseLogger.error(getExceptionLog(ex), 'Error sending to Sentry'); } return; } @@ -258,20 +266,25 @@ export async function uncaughtErrorHandler(err: any, req: express.Request, res: } try { - rollbarServer.warn('Error not handled by error handler', req, { - context: `route#errorHandler`, - custom: { - ...getExceptionLog(err, true), + sentryServer.captureException(new Error('headers already sent'), { + user: { + id: req.session.user?.id, + email: req.session.user?.email, + name: req.session.user?.name, + }, + tags: { + requestId: res.locals.requestId, + }, + extra: { + message: 'Error not handled by ErrorHandler', + location: `route#errorHandler`, url: req.url, params: req.params, query: req.query, - body: req.body, - userId: req.session.user?.id, - requestId: res.locals.requestId, }, }); } catch (ex) { - responseLogger.error(getExceptionLog(ex), 'Error sending to Rollbar'); + responseLogger.error(getExceptionLog(ex), 'Error sending to Sentry'); } const errorMessage = 'There was an error processing the request'; diff --git a/apps/api/src/app/utils/route.utils.ts b/apps/api/src/app/utils/route.utils.ts index fcf0e680f..791e7d68a 100644 --- a/apps/api/src/app/utils/route.utils.ts +++ b/apps/api/src/app/utils/route.utils.ts @@ -1,4 +1,4 @@ -import { getExceptionLog, logger, rollbarServer } from '@jetstream/api-config'; +import { getExceptionLog, logger, sentryServer } from '@jetstream/api-config'; import { CookieOptions, UserProfileSession } from '@jetstream/auth/types'; import { ApiConnection } from '@jetstream/salesforce-api'; import { NextFunction } from 'express'; @@ -98,18 +98,19 @@ export function createRoute { + if (sentryReportingUri) { + res.setHeader( + 'Report-To', + JSON.stringify({ + group: 'csp-endpoint', + max_age: 10886400, + endpoints: [{ url: sentryReportingUri }], + include_subdomains: true, + }) + ); + } + next(); + }); + app.use(blockBotByUserAgentMiddleware); if (ENV.ENVIRONMENT === 'development') { @@ -329,7 +350,7 @@ if (ENV.NODE_ENV === 'production' && !ENV.CI && cluster.isPrimary) { app.use('/webhook', webhookRoutes); - app.use(raw({ limit: '30mb', type: ['text/csv'] })); + app.use(raw({ limit: '30mb', type: ['text/csv', 'text/plain'] })); app.use(raw({ limit: '30mb', type: ['application/zip'] })); app.use(json({ limit: '20mb', type: ['json', 'application/csp-report'] })); app.use(urlencoded({ extended: true })); @@ -408,23 +429,25 @@ if (ENV.NODE_ENV === 'production' && !ENV.CI && cluster.isPrimary) { logger.error(getExceptionLog(error), '[SERVER][ERROR]'); }); - process.on('SIGTERM', () => { - logger.info('SIGTERM received, shutting down gracefully'); - server.close(() => { - logger.info('Server closed'); + if (ENV.ENVIRONMENT !== 'development') { + process.on('SIGTERM', () => { + logger.info('SIGTERM received, shutting down gracefully'); + server.close(() => { + logger.info('Server closed'); - pgPool.end().then(() => { - logger.info('DB pool closed'); - process.exit(0); + pgPool.end().then(() => { + logger.info('DB pool closed'); + process.exit(0); + }); }); - }); - // Force close after 30s - setTimeout(() => { - logger.error('Could not close connections in time, forcefully shutting down'); - process.exit(1); - }, 30_000); - }); + // Force close after 30s + setTimeout(() => { + logger.error('Could not close connections in time, forcefully shutting down'); + process.exit(1); + }, 30_000); + }); + } } /** diff --git a/apps/cron-tasks/src/config/env-config.ts b/apps/cron-tasks/src/config/env-config.ts index d4cbca478..0f1a81d9d 100644 --- a/apps/cron-tasks/src/config/env-config.ts +++ b/apps/cron-tasks/src/config/env-config.ts @@ -16,7 +16,6 @@ export const ENV = { NODE_ENV: process.env.NODE_ENV, ENVIRONMENT: process.env.ENVIRONMENT || 'production', GIT_VERSION: VERSION, - ROLLBAR_SERVER_TOKEN: process.env.ROLLBAR_SERVER_TOKEN, // FIXME: there was a typo in env variables, using both temporarily as a safe fallback JETSTREAM_POSTGRES_DBURI: process.env.JETSTREAM_POSTGRES_DBURI || process.env.JESTREAM_POSTGRES_DBURI, PRISMA_DEBUG: process.env.PRISMA_DEBUG && process.env.PRISMA_DEBUG.toLocaleLowerCase().startsWith('t'), diff --git a/apps/geo-ip-api/src/route.utils.ts b/apps/geo-ip-api/src/route.utils.ts index 0339b88ea..398994834 100644 --- a/apps/geo-ip-api/src/route.utils.ts +++ b/apps/geo-ip-api/src/route.utils.ts @@ -1,4 +1,4 @@ -import { getExceptionLog, rollbarServer } from '@jetstream/api-config'; +import { getExceptionLog, sentryServer } from '@jetstream/api-config'; import type { Request as ExpressRequest, Response as ExpressResponse } from 'express'; import { NextFunction } from 'express'; import type pino from 'pino'; @@ -48,16 +48,14 @@ export function createRoute
}> - + } /> }> diff --git a/apps/jetstream-web-extension/webpack.config.js b/apps/jetstream-web-extension/webpack.config.js index 612669f07..496e0ae45 100644 --- a/apps/jetstream-web-extension/webpack.config.js +++ b/apps/jetstream-web-extension/webpack.config.js @@ -46,16 +46,28 @@ module.exports = composePlugins(withNx(), withReact(), (config, { configuration }, }, }; + config.plugins = config.plugins || []; + config.plugins.push( + // @ts-expect-error this is valid, not sure why it is complaining + new webpack.NormalModuleReplacementPlugin(/@sentry\/react/, '@jetstream/mocks/sentry') + ); + // Omit Sentry from the build - it is huge and there is a lot of work to get it going with the web extension config.plugins.push( // @ts-expect-error this is valid, not sure why it is complaining new webpack.EnvironmentPlugin({ NX_PUBLIC_AMPLITUDE_KEY: '', - NX_PUBLIC_ROLLBAR_KEY: '', + NX_PUBLIC_SENTRY_DSN: '', }), new webpack.DefinePlugin({ 'globalThis.__IS_BROWSER_EXTENSION__': true, 'import.meta.env.NX_PUBLIC_AMPLITUDE_KEY': 'null', + 'import.meta.env.NX_PUBLIC_SENTRY_DSN': 'null', + __SENTRY_DEBUG__: false, + __SENTRY_TRACING__: false, + __RRWEB_EXCLUDE_IFRAME__: true, + __RRWEB_EXCLUDE_SHADOW_DOM__: true, + __SENTRY_EXCLUDE_REPLAY_WORKER__: true, }), createHtmlPagePlugin('app', 'app'), createHtmlPagePlugin('popup', 'popup'), diff --git a/apps/jetstream-worker/src/app/config/env-config.ts b/apps/jetstream-worker/src/app/config/env-config.ts index c57eb6581..3c6866700 100644 --- a/apps/jetstream-worker/src/app/config/env-config.ts +++ b/apps/jetstream-worker/src/app/config/env-config.ts @@ -18,7 +18,6 @@ export const ENV = { ENVIRONMENT: process.env.ENVIRONMENT || 'production', PORT: process.env.port || 3344, GIT_VERSION: VERSION, - ROLLBAR_SERVER_TOKEN: process.env.ROLLBAR_SERVER_TOKEN, // JETSTREAM JETSTREAM_SERVER_DOMAIN: process.env.JETSTREAM_SERVER_DOMAIN, JETSTREAM_SERVER_URL: process.env.JETSTREAM_SERVER_URL, diff --git a/apps/jetstream/src/app/app.tsx b/apps/jetstream/src/app/app.tsx index 559e389c6..60eb3d22a 100644 --- a/apps/jetstream/src/app/app.tsx +++ b/apps/jetstream/src/app/app.tsx @@ -1,11 +1,10 @@ import { Announcement } from '@jetstream/types'; import { AppToast, ConfirmationServiceProvider } from '@jetstream/ui'; -import { AppLoading, DownloadFileStream, ErrorBoundaryFallback, HeaderNavbar } from '@jetstream/ui-core'; +import { AppLoading, DownloadFileStream, ErrorBoundary, HeaderNavbar } from '@jetstream/ui-core'; import { OverlayProvider } from '@react-aria/overlays'; import { Suspense, useState } from 'react'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; -import { ErrorBoundary } from 'react-error-boundary'; import ModalContainer from 'react-modal-promise'; import { RecoilRoot } from 'recoil'; import RecoilNexus from 'recoil-nexus'; @@ -42,7 +41,7 @@ export const App = () => {
}> - + diff --git a/apps/jetstream/src/app/components/billing/Billing.tsx b/apps/jetstream/src/app/components/billing/Billing.tsx index cf2e48032..25fa9cf17 100644 --- a/apps/jetstream/src/app/components/billing/Billing.tsx +++ b/apps/jetstream/src/app/components/billing/Billing.tsx @@ -2,7 +2,7 @@ import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS, TITLES } from '@jetstream/shared/constants'; import { getSubscriptions } from '@jetstream/shared/data'; import { APP_ROUTES } from '@jetstream/shared/ui-router'; -import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; +import { useSentry, useTitle } from '@jetstream/shared/ui-utils'; import { StripeUserFacingCustomer } from '@jetstream/types'; import { AutoFullHeightContainer, @@ -28,7 +28,7 @@ const HEIGHT_BUFFER = 170; export const Billing = () => { useTitle(TITLES.SETTINGS); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const setUserProfile = useSetRecoilState(fromAppState.userProfileState); const [loading, setLoading] = useState(false); const [loadingError, setLoadingError] = useState(false); @@ -62,8 +62,8 @@ export const Billing = () => { setSubscriptionStatus({ hasCanceledSubscriptions, hasFutureDatedCancellation, hasActiveSubscriptions }); setCustomerWithSubscriptions(customer); } catch (ex) { - logger.error('Settings: Error fetching user', { stack: ex.stack, message: ex.message }); - rollbar.error('Settings: Error fetching user', { stack: ex.stack, message: ex.message }); + logger.error('Settings: Error fetching user', ex); + sentry.trackError('Settings: Error fetching user', ex, 'Billing'); setLoadingError(true); } finally { setLoading(false); diff --git a/apps/jetstream/src/app/components/core/AppInitializer.tsx b/apps/jetstream/src/app/components/core/AppInitializer.tsx index 6018baa09..d222aaedf 100644 --- a/apps/jetstream/src/app/components/core/AppInitializer.tsx +++ b/apps/jetstream/src/app/components/core/AppInitializer.tsx @@ -2,7 +2,7 @@ import { logger } from '@jetstream/shared/client-logger'; import { HTTP } from '@jetstream/shared/constants'; import { checkHeartbeat, disconnectSocket, initSocket, registerMiddleware } from '@jetstream/shared/data'; -import { useObservable, useRollbar } from '@jetstream/shared/ui-utils'; +import { useObservable, useSentry } from '@jetstream/shared/ui-utils'; import { Announcement, SalesforceOrgUi } from '@jetstream/types'; import { useAmplitude } from '@jetstream/ui-core'; import { fromAppState } from '@jetstream/ui/app-state'; @@ -82,12 +82,10 @@ APP VERSION ${version} announcements && onAnnouncements && onAnnouncements(announcements); }, [announcements, onAnnouncements]); - useRollbar({ - accessToken: environment.rollbarClientAccessToken, - environment: appCookie.environment, - userProfile: userProfile, - version, - }); + const sentry = useSentry({ appCookie, userProfile, version }); + useEffect(() => { + sentry.trackError('Settings: Error unlinking account', new Error('Some Error'), 'ProfileLinkedAccounts'); + }, [sentry]); useAmplitude(); useEffect(() => { diff --git a/apps/jetstream/src/app/components/core/LazyLoad.tsx b/apps/jetstream/src/app/components/core/LazyLoad.tsx index c73e51b3e..cef5249da 100644 --- a/apps/jetstream/src/app/components/core/LazyLoad.tsx +++ b/apps/jetstream/src/app/components/core/LazyLoad.tsx @@ -23,7 +23,7 @@ * SOFTWARE. */ -import { logErrorToRollbar } from '@jetstream/shared/ui-utils'; +import { logErrorToSentry } from '@jetstream/shared/ui-utils'; import { ComponentType, createElement, forwardRef, lazy } from 'react'; export type PreloadableComponent> = T & { @@ -60,10 +60,7 @@ export default function lazyWithPreload>( }) .catch((ex) => { console.error('Failed to preload component', ex); - logErrorToRollbar('Preload route failed', { - message: ex.message, - stack: ex.stack, - }); + logErrorToSentry('Preload route failed', ex, 'lazyWithPreload'); }); } diff --git a/apps/jetstream/src/app/components/core/monaco-loader.ts b/apps/jetstream/src/app/components/core/monaco-loader.ts index 5cdb6ddd7..4057335ed 100644 --- a/apps/jetstream/src/app/components/core/monaco-loader.ts +++ b/apps/jetstream/src/app/components/core/monaco-loader.ts @@ -1,5 +1,5 @@ import { logger } from '@jetstream/shared/client-logger'; -import { logErrorToRollbar } from '@jetstream/shared/ui-utils'; +import { logErrorToSentry } from '@jetstream/shared/ui-utils'; import { loader } from '@monaco-editor/react'; // Load as static resource instead of bundled in application @@ -15,9 +15,5 @@ loader }) .catch((ex) => { logger.error('[ERROR] Failed to load monaco editor', ex); - logErrorToRollbar('Failed to load monaco editor', { - message: ex?.message, - stack: ex?.stack, - exception: ex, - }); + logErrorToSentry('Failed to load monaco editor', ex, 'monaco-loader'); }); diff --git a/apps/jetstream/src/app/components/profile/Profile.tsx b/apps/jetstream/src/app/components/profile/Profile.tsx index 93ccf0249..81df6f342 100644 --- a/apps/jetstream/src/app/components/profile/Profile.tsx +++ b/apps/jetstream/src/app/components/profile/Profile.tsx @@ -10,7 +10,7 @@ import { updateUserProfile, } from '@jetstream/shared/data'; import { APP_ROUTES } from '@jetstream/shared/ui-router'; -import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; +import { useSentry, useTitle } from '@jetstream/shared/ui-utils'; import { AutoFullHeightContainer, Grid, @@ -38,7 +38,7 @@ export const Profile = () => { useTitle(TITLES.SETTINGS); const isMounted = useRef(true); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const [loading, setLoading] = useState(false); const [loadingError, setLoadingError] = useState(false); const setUserProfile = useSetRecoilState(userProfileState); @@ -61,7 +61,7 @@ export const Profile = () => { setLoadingError(false); setFullUserProfile(await getFullUserProfile()); } catch (ex) { - rollbar.error('Settings: Error fetching user', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error fetching user', ex, 'Profile'); setLoadingError(true); } finally { setLoading(false); @@ -96,7 +96,7 @@ export const Profile = () => { message: 'There was a problem updating your user. Try again or file a support ticket for assistance.', type: 'error', }); - rollbar.error('Settings: Error updating user', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error updating user', ex, 'Profile'); } finally { setLoading(false); setEditMode(false); @@ -126,7 +126,7 @@ export const Profile = () => { message: ex.message || 'There was a problem resetting your password. Try again or file a support ticket for assistance.', type: 'error', }); - rollbar.error('Settings: Error setting password', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error setting password', ex, 'Profile'); } } @@ -143,7 +143,7 @@ export const Profile = () => { message: ex.message || 'There was a problem resetting your password. Try again or file a support ticket for assistance.', type: 'error', }); - rollbar.error('Settings: Error resetting password', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error resetting password', ex, 'Profile'); } } @@ -156,7 +156,7 @@ export const Profile = () => { message: 'There was a problem removing your password. Try again or file a support ticket for assistance.', type: 'error', }); - rollbar.error('Settings: Error removing password', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error removing password', ex, 'Profile'); } } diff --git a/apps/jetstream/src/app/components/profile/ProfileLinkedAccounts.tsx b/apps/jetstream/src/app/components/profile/ProfileLinkedAccounts.tsx index cce391f7c..a615dbf09 100644 --- a/apps/jetstream/src/app/components/profile/ProfileLinkedAccounts.tsx +++ b/apps/jetstream/src/app/components/profile/ProfileLinkedAccounts.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { UserProfileIdentity, UserProfileUiWithIdentities } from '@jetstream/auth/types'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; -import { useCsrfToken, useRollbar } from '@jetstream/shared/ui-utils'; +import { useCsrfToken, useSentry } from '@jetstream/shared/ui-utils'; import { fireToast, Grid } from '@jetstream/ui'; import { useAmplitude } from '@jetstream/ui-core'; import { FunctionComponent } from 'react'; @@ -20,7 +20,7 @@ const searchParams = new URLSearchParams({ export const ProfileLinkedAccounts: FunctionComponent = ({ fullUserProfile, onUserProfilesChange }) => { const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const { unlinkAccount, loading: linkAccountLoading, providers } = useLinkAccount(); const { csrfToken } = useCsrfToken(); @@ -34,7 +34,7 @@ export const ProfileLinkedAccounts: FunctionComponent { useTitle(TITLES.SETTINGS); const isMounted = useRef(true); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const [loading, setLoading] = useState(false); const [loadingError, setLoadingError] = useState(false); const setUserProfile = useSetRecoilState(userProfileState); @@ -58,7 +58,7 @@ export const Settings = () => { setLoadingError(false); setFullUserProfile(await getFullUserProfile()); } catch (ex) { - rollbar.error('Settings: Error fetching user', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error fetching user', ex, 'Settings'); setLoadingError(true); } finally { setLoading(false); @@ -94,7 +94,7 @@ export const Settings = () => { message: 'There was a problem updating your user. Try again or file a support ticket for assistance.', type: 'error', }); - rollbar.error('Settings: Error updating user', { stack: ex.stack, message: ex.message }); + sentry.trackError('Settings: Error updating user', ex, 'Settings'); } finally { setLoading(false); } diff --git a/apps/jetstream/src/environments/environment.prod.ts b/apps/jetstream/src/environments/environment.prod.ts index 79723bec3..7cfc07aff 100644 --- a/apps/jetstream/src/environments/environment.prod.ts +++ b/apps/jetstream/src/environments/environment.prod.ts @@ -1,11 +1,13 @@ +import { UI_ENV } from '@jetstream/ui/env'; + export const environment = { name: 'Jetstream', production: true, - rollbarClientAccessToken: import.meta.env.NX_PUBLIC_ROLLBAR_KEY, - amplitudeToken: import.meta.env.NX_PUBLIC_AMPLITUDE_KEY, - STRIPE_PUBLIC_KEY: import.meta.env.NX_PUBLIC_STRIPE_PUBLIC_KEY, - BILLING_ENABLED: import.meta.env.NX_PUBLIC_BILLING_ENABLED === 'true', - STRIPE_PRO_ANNUAL_PRICE_ID: import.meta.env.NX_PUBLIC_STRIPE_PRO_ANNUAL_PRICE_ID, - STRIPE_PRO_MONTHLY_PRICE_ID: import.meta.env.NX_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID, + sentryDsn: UI_ENV.sentryDsn, + amplitudeToken: UI_ENV.amplitudeToken, + STRIPE_PUBLIC_KEY: UI_ENV.STRIPE_PUBLIC_KEY, + BILLING_ENABLED: UI_ENV.BILLING_ENABLED, + STRIPE_PRO_ANNUAL_PRICE_ID: UI_ENV.STRIPE_PRO_ANNUAL_PRICE_ID, + STRIPE_PRO_MONTHLY_PRICE_ID: UI_ENV.STRIPE_PRO_MONTHLY_PRICE_ID, isWebExtension: false, }; diff --git a/apps/jetstream/src/environments/environment.ts b/apps/jetstream/src/environments/environment.ts index 53e3d8bd6..ed6c0c71c 100644 --- a/apps/jetstream/src/environments/environment.ts +++ b/apps/jetstream/src/environments/environment.ts @@ -1,14 +1,15 @@ +import { UI_ENV } from '@jetstream/ui/env'; // This file can be replaced during build by using the `fileReplacements` array. // When building for production, this file is replaced with `environment.prod.ts`. export const environment = { name: 'JetstreamDev', production: false, - rollbarClientAccessToken: import.meta.env.NX_PUBLIC_ROLLBAR_KEY, - amplitudeToken: import.meta.env.NX_PUBLIC_AMPLITUDE_KEY, - STRIPE_PUBLIC_KEY: import.meta.env.NX_PUBLIC_STRIPE_PUBLIC_KEY, - BILLING_ENABLED: import.meta.env.NX_PUBLIC_BILLING_ENABLED === 'true', - STRIPE_PRO_ANNUAL_PRICE_ID: import.meta.env.NX_PUBLIC_STRIPE_PRO_ANNUAL_PRICE_ID, - STRIPE_PRO_MONTHLY_PRICE_ID: import.meta.env.NX_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID, + sentryDsn: UI_ENV.sentryDsn, + amplitudeToken: UI_ENV.amplitudeToken, + STRIPE_PUBLIC_KEY: UI_ENV.STRIPE_PUBLIC_KEY, + BILLING_ENABLED: UI_ENV.BILLING_ENABLED, + STRIPE_PRO_ANNUAL_PRICE_ID: UI_ENV.STRIPE_PRO_ANNUAL_PRICE_ID, + STRIPE_PRO_MONTHLY_PRICE_ID: UI_ENV.STRIPE_PRO_MONTHLY_PRICE_ID, isWebExtension: false, }; diff --git a/apps/landing/pages/subprocessors/index.tsx b/apps/landing/pages/subprocessors/index.tsx index 0b92b1206..3aa3ceee8 100644 --- a/apps/landing/pages/subprocessors/index.tsx +++ b/apps/landing/pages/subprocessors/index.tsx @@ -2,6 +2,8 @@ import Layout from '../../components/layouts/Layout'; const webSubProcessors = [ { name: 'Amplitude', function: 'Telemetry', location: 'United States', optional: 'No' }, + { name: 'BetterStack', function: 'Server Logging', location: 'Europe', optional: 'No' }, + { name: 'Backblaze', function: 'Log storage', location: 'United States', optional: 'No' }, { name: 'Cloudflare', function: 'Infrastructure', location: 'United States', optional: 'No' }, { name: 'Cloudinary', @@ -17,7 +19,7 @@ const webSubProcessors = [ }, { name: 'Mailgun', function: 'Email', location: 'United States', optional: 'No' }, { name: 'Render', function: 'Infrastructure', location: 'United States', optional: 'No' }, - { name: 'Rollbar', function: 'Automated bug tracking', location: 'United States', optional: 'No' }, + { name: 'Sentry', function: 'Automated bug tracking', location: 'United States', optional: 'No' }, { name: 'Salesforce.com', function: 'Application core', location: 'United States', optional: 'No' }, ]; diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml index e47d41ed9..35eae9a2f 100644 --- a/docker-compose.e2e.yml +++ b/docker-compose.e2e.yml @@ -22,7 +22,7 @@ services: SFDC_CONSUMER_SECRET: '${SFDC_CONSUMER_SECRET}' SFDC_CONSUMER_KEY: '${SFDC_CONSUMER_KEY}' SFDC_CALLBACK_URL: '${SFDC_CALLBACK_URL}' - NX_PUBLIC_ROLLBAR_KEY: '${NX_PUBLIC_ROLLBAR_KEY}' + NX_PUBLIC_SENTRY_KEY: '${NX_PUBLIC_SENTRY_KEY}' NX_PUBLIC_AMPLITUDE_KEY: '${NX_PUBLIC_AMPLITUDE_KEY}' ports: - '3333:3333' diff --git a/jest.preset.js b/jest.preset.js index c5dac7008..6b4dc0402 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -11,6 +11,9 @@ const config = { resolver: '@nx/jest/plugins/resolver', moduleFileExtensions: ['ts', 'js', 'html'], coverageReporters: ['html'], + moduleNameMapper: { + '.*ui-env': './libs/shared/ui-env/src/lib/__tests__/ui-env.mock.ts', + }, globals: { ...nxPreset.globals, __IS_BROWSER_EXTENSION__: false, diff --git a/libs/api-config/src/index.ts b/libs/api-config/src/index.ts index bc23a65b0..768de0129 100644 --- a/libs/api-config/src/index.ts +++ b/libs/api-config/src/index.ts @@ -1,6 +1,6 @@ // Ensure modules get auto-loaded import './lib/api-db-config'; -import './lib/api-rollbar-config'; +import './lib/api-sentry-config'; import './lib/api-telemetry'; import './lib/env-config'; @@ -8,7 +8,7 @@ import './lib/env-config'; export * from './lib/api-db-config'; export * from './lib/api-logger'; export * from './lib/api-rate-limit.config'; -export * from './lib/api-rollbar-config'; +export * from './lib/api-sentry-config'; export * from './lib/api-telemetry'; export * from './lib/email.config'; export * from './lib/env-config'; diff --git a/libs/api-config/src/lib/api-rollbar-config.ts b/libs/api-config/src/lib/api-rollbar-config.ts deleted file mode 100644 index 573ef2afb..000000000 --- a/libs/api-config/src/lib/api-rollbar-config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Rollbar from 'rollbar'; -import { ENV } from './env-config'; - -export const rollbarServer = new Rollbar({ - codeVersion: ENV.VERSION || '', - code_version: ENV.VERSION || '', - accessToken: ENV.ROLLBAR_SERVER_TOKEN || '', - environment: ENV.ENVIRONMENT, - captureUncaught: true, - captureUnhandledRejections: true, - enabled: !!ENV.ROLLBAR_SERVER_TOKEN && ENV.ENVIRONMENT !== 'development', - nodeSourceMaps: true, -}); diff --git a/libs/api-config/src/lib/api-sentry-config.ts b/libs/api-config/src/lib/api-sentry-config.ts new file mode 100644 index 000000000..5466938d9 --- /dev/null +++ b/libs/api-config/src/lib/api-sentry-config.ts @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/node'; +import { ENV } from './env-config'; + +Sentry.init({ + dsn: ENV.SENTRY_DSN, + release: ENV.VERSION, + environment: ENV.ENVIRONMENT, +}); + +export const sentryServer = Sentry; diff --git a/libs/api-config/src/lib/env-config.ts b/libs/api-config/src/lib/env-config.ts index c869519d4..e94ad4e33 100644 --- a/libs/api-config/src/lib/env-config.ts +++ b/libs/api-config/src/lib/env-config.ts @@ -114,7 +114,6 @@ const envSchema = z.object({ IP_API_KEY: z.string().optional().describe('API Key used to get location information from IP address'), IP_API_SERVICE: z.enum(['IP-API', 'LOCAL']).optional().describe('API Key used to get location information from IP address'), VERSION: z.string().optional(), - ROLLBAR_SERVER_TOKEN: z.string().optional(), // Legacy Auth0 - Used to allow JIT password migration AUTH0_CLIENT_ID: z.string().nullish(), @@ -222,6 +221,9 @@ const envSchema = z.object({ STRIPE_PRO_ANNUAL_PRICE_ID: z.string().optional(), STRIPE_PRO_MONTHLY_PRICE_ID: z.string().optional(), STRIPE_BILLING_PORTAL_LINK: z.string().optional(), + SENTRY_CSP_REPORT_URI: z.string().optional(), + SENTRY_TUNNEL_URI: z.string().optional(), + SENTRY_DSN: z.string().optional(), }); const parseResults = envSchema.safeParse({ @@ -234,6 +236,7 @@ const parseResults = envSchema.safeParse({ STRIPE_PRO_ANNUAL_PRICE_ID: process.env.NX_PUBLIC_STRIPE_PRO_ANNUAL_PRICE_ID, STRIPE_PRO_MONTHLY_PRICE_ID: process.env.NX_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID, STRIPE_BILLING_PORTAL_LINK: process.env.NX_PUBLIC_STRIPE_BILLING_PORTAL_LINK, + SENTRY_DSN: process.env.NX_PUBLIC_SENTRY_DSN, VERSION, }); diff --git a/libs/connected/connected-ui/src/lib/useDescribeMetadata.tsx b/libs/connected/connected-ui/src/lib/useDescribeMetadata.tsx index 06b86ea99..b1c90a9bd 100644 --- a/libs/connected/connected-ui/src/lib/useDescribeMetadata.tsx +++ b/libs/connected/connected-ui/src/lib/useDescribeMetadata.tsx @@ -1,6 +1,6 @@ import { logger } from '@jetstream/shared/client-logger'; import { clearCacheForOrg, describeMetadata as describeMetadataApi } from '@jetstream/shared/data'; -import { useNonInitialEffect, useRollbar } from '@jetstream/shared/ui-utils'; +import { useNonInitialEffect } from '@jetstream/shared/ui-utils'; import { orderValues } from '@jetstream/shared/utils'; import { DescribeMetadataResult, MetadataObject, SalesforceOrgUi } from '@jetstream/types'; import { formatRelative } from 'date-fns/formatRelative'; @@ -16,7 +16,6 @@ export function useDescribeMetadata( loadOnInit = true ) { const isMounted = useRef(true); - const rollbar = useRollbar(); // map of each item or child item to parent item const [metadataItemMap, setMetadataItemMap] = useState>(initialMetadataItemMap || {}); const [metadataItems, setMetadataItems] = useState(initialItems); diff --git a/libs/connected/connected-ui/src/lib/useListMetadata.tsx b/libs/connected/connected-ui/src/lib/useListMetadata.tsx index c304f8856..1bb235a7a 100644 --- a/libs/connected/connected-ui/src/lib/useListMetadata.tsx +++ b/libs/connected/connected-ui/src/lib/useListMetadata.tsx @@ -1,6 +1,6 @@ import { logger } from '@jetstream/shared/client-logger'; import { listMetadata as listMetadataApi, queryAll, queryWithCache } from '@jetstream/shared/data'; -import { useRollbar } from '@jetstream/shared/ui-utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; import { groupByFlat, orderObjectsBy, splitArrayToMaxSize } from '@jetstream/shared/utils'; import { ListMetadataQuery, ListMetadataResult, SalesforceOrgUi } from '@jetstream/types'; import { formatRelative } from 'date-fns/formatRelative'; @@ -215,7 +215,7 @@ async function fetchListMetadataForItemsInFolder( */ export function useListMetadata(selectedOrg: SalesforceOrgUi) { const isMounted = useRef(true); - const rollbar = useRollbar(); + const sentry = useSentry(); const [listMetadataItems, setListMetadataItems] = useState>(); const [loading, setLoading] = useState(true); const [hasError, setHasError] = useState(false); @@ -340,7 +340,7 @@ export function useListMetadata(selectedOrg: SalesforceOrgUi) { setInitialLoadFinished(true); } catch (ex) { logger.error(ex); - rollbar.error('List Metadata Failed', { message: ex.message, stack: ex.stack }); + sentry.trackError('List Metadata Failed', ex, 'useListMetadata'); if (!isMounted.current) { return; } diff --git a/libs/features/anon-apex/src/AnonymousApex.tsx b/libs/features/anon-apex/src/AnonymousApex.tsx index d6f831371..d60764f50 100644 --- a/libs/features/anon-apex/src/AnonymousApex.tsx +++ b/libs/features/anon-apex/src/AnonymousApex.tsx @@ -3,8 +3,8 @@ import { useSetTraceFlag } from '@jetstream/connected-ui'; import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS, INDEXED_DB, LOG_LEVELS, TITLES } from '@jetstream/shared/constants'; import { anonymousApex } from '@jetstream/shared/data'; -import { useBrowserNotifications, useDebounce, useNonInitialEffect, useRollbar, useTitle } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useBrowserNotifications, useDebounce, useNonInitialEffect, useSentry, useTitle } from '@jetstream/shared/ui-utils'; +import { getErrorMessage } from '@jetstream/shared/utils'; import { SplitWrapper as Split } from '@jetstream/splitjs'; import { ApexHistoryItem, ListItem, SalesforceOrgUi } from '@jetstream/types'; import { @@ -55,7 +55,7 @@ export const AnonymousApex: FunctionComponent = () => { const apexRef = useRef(); const logRef = useRef(); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const { serverUrl } = useRecoilValue(applicationCookieState); const skipFrontDoorAuth = useRecoilValue(selectSkipFrontdoorAuth); const selectedOrg = useRecoilValue(selectedOrgState); @@ -175,7 +175,7 @@ export const AnonymousApex: FunctionComponent = () => { }) .catch((ex) => { logger.warn('[ERROR] Could not save history', ex); - rollbar.error('Error saving apex history', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error saving apex history', ex, 'AnonymousApex'); }); } trackEvent(ANALYTICS_KEYS.apex_Submitted, { success: result.success }); diff --git a/libs/features/automation-control/src/AutomationControlSelection.tsx b/libs/features/automation-control/src/AutomationControlSelection.tsx index b6e83f0cc..8c51bc0be 100644 --- a/libs/features/automation-control/src/AutomationControlSelection.tsx +++ b/libs/features/automation-control/src/AutomationControlSelection.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import { TITLES } from '@jetstream/shared/constants'; import { APP_ROUTES } from '@jetstream/shared/ui-router'; -import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; +import { useTitle } from '@jetstream/shared/ui-utils'; import { SplitWrapper as Split } from '@jetstream/splitjs'; import { DescribeGlobalSObjectResult, ListItem, SalesforceOrgUi } from '@jetstream/types'; import { @@ -45,7 +45,6 @@ export interface AutomationControlSelectionProps {} export const AutomationControlSelection: FunctionComponent = () => { useTitle(TITLES.AUTOMATION_CONTROL); - const rollbar = useRollbar(); const selectedOrg = useRecoilValue(selectedOrgState); diff --git a/libs/features/automation-control/src/automation-control-data-utils.tsx b/libs/features/automation-control/src/automation-control-data-utils.tsx index cbdd0d6ee..892073c17 100644 --- a/libs/features/automation-control/src/automation-control-data-utils.tsx +++ b/libs/features/automation-control/src/automation-control-data-utils.tsx @@ -10,7 +10,7 @@ import { import { getOrgType, getToolingRecords, - logErrorToRollbar, + logErrorToSentry, pollMetadataResultsUntilDone, pollRetrieveMetadataResultsUntilDone, } from '@jetstream/shared/ui-utils'; @@ -393,7 +393,7 @@ export async function getProcessBuildersMetadata( } } catch (ex) { logger.warn('Error processing flow metadata', ex); - logErrorToRollbar( + logErrorToSentry( getErrorMessage(ex), { ...getErrorMessageAndStackObj(ex), diff --git a/libs/features/automation-control/src/useAutomationControlData.ts b/libs/features/automation-control/src/useAutomationControlData.ts index 8f9341978..10c031d09 100644 --- a/libs/features/automation-control/src/useAutomationControlData.ts +++ b/libs/features/automation-control/src/useAutomationControlData.ts @@ -1,7 +1,7 @@ import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; -import { useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; +import { getErrorMessage } from '@jetstream/shared/utils'; import { SalesforceOrgUi } from '@jetstream/types'; import { useAmplitude } from '@jetstream/ui-core'; import { useCallback, useEffect, useReducer, useRef } from 'react'; @@ -591,7 +591,7 @@ export function useAutomationControlData({ }) { const isMounted = useRef(true); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const [{ loading, hasError, errorMessage, data, rows, visibleRows, dirtyCount }, dispatch] = useReducer(reducer, { loading: false, @@ -668,13 +668,15 @@ export function useAutomationControlData({ dispatch({ type: 'FETCH_SUCCESS', payload: item }); } else { dispatch({ type: 'FETCH_ERROR', payload: item }); - rollbar.error('Automation Control Fetch Error', { item }); + sentry.trackError('Automation Control Fetch Error', new Error('Automation Control Fetch Error'), 'useAutomationControlData', { + item, + }); } }, error: (err) => { dispatch({ type: 'ERROR', payload: { errorMessage: err.message } }); logger.error('[AUTOMATION][FETCH][ERROR]', err); - rollbar.error('Automation Control Fatal Error', getErrorMessageAndStackObj(err)); + sentry.trackError('Automation Control Fatal Error', err, 'useAutomationControlData'); }, complete: () => { dispatch({ type: 'FETCH_FINISH' }); @@ -687,7 +689,7 @@ export function useAutomationControlData({ } catch (ex) { dispatch({ type: 'ERROR', payload: { errorMessage: getErrorMessage(ex) } }); } - }, [selectedAutomationTypes, selectedOrg, defaultApiVersion, selectedSObjects, rollbar]); + }, [selectedAutomationTypes, selectedOrg, defaultApiVersion, selectedSObjects, sentry]); const refreshProcessBuilders = useCallback(async () => { dispatch({ type: 'FETCH_REFRESH', payload: { selectedTypes: ['FlowProcessBuilder'] } }); diff --git a/libs/features/create-object-and-fields/src/CreateNewGlobalPicklistModal.tsx b/libs/features/create-object-and-fields/src/CreateNewGlobalPicklistModal.tsx index 2134de1d0..107320d2f 100644 --- a/libs/features/create-object-and-fields/src/CreateNewGlobalPicklistModal.tsx +++ b/libs/features/create-object-and-fields/src/CreateNewGlobalPicklistModal.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; -import { useRollbar } from '@jetstream/shared/ui-utils'; -import { REGEX, getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; +import { REGEX, getErrorMessage } from '@jetstream/shared/utils'; import { GlobalValueSetRequest, SalesforceOrgUi } from '@jetstream/types'; import { Checkbox, Grid, GridCol, Input, Modal, ScopedNotification, Spinner, Textarea } from '@jetstream/ui'; import { createGlobalPicklist, generateApiNameFromLabel, useAmplitude } from '@jetstream/ui-core'; @@ -55,7 +55,7 @@ export interface CreateNewGlobalPicklistModalProps { export const CreateNewGlobalPicklistModal: FunctionComponent = ({ selectedOrg, onCreated }) => { const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const [{ defaultApiVersion }] = useRecoilState(applicationCookieState); const [isOpen, setIsOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(null); @@ -85,8 +85,7 @@ export const CreateNewGlobalPicklistModal: FunctionComponent('NOT_STARTED'); @@ -95,18 +94,18 @@ export default function useCreateObject({ apiVersion, serverUrl, selectedOrg }: } setStatus('SUCCESS'); } catch (ex) { - rollbar.error('Deploy object permission records Fatal Error', getErrorMessageAndStackObj(ex)); + sentry.trackError('Deploy object permission records Fatal Error', ex, 'useCreateObject'); setStatus('FAILED'); } finally { setLoading(false); setDeployed(true); } } catch (ex) { - rollbar.error('Deploy object Fatal Error', getErrorMessageAndStackObj(ex)); + sentry.trackError('Deploy object Fatal Error', ex, 'useCreateObject'); setStatus('FAILED'); } }, - [apiVersion, doDeployMetadata, rollbar, selectedOrg] + [apiVersion, doDeployMetadata, sentry, selectedOrg] ); return { diff --git a/libs/features/create-records/src/lib/CreateRecords.tsx b/libs/features/create-records/src/lib/CreateRecords.tsx index 8a6bba7b2..1e0bc9c17 100644 --- a/libs/features/create-records/src/lib/CreateRecords.tsx +++ b/libs/features/create-records/src/lib/CreateRecords.tsx @@ -4,8 +4,8 @@ import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; import { describeSObject, sobjectOperation } from '@jetstream/shared/data'; import { APP_ROUTES } from '@jetstream/shared/ui-router'; -import { filterCreateSobjects, isErrorResponse, useNonInitialEffect, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { filterCreateSobjects, isErrorResponse, useNonInitialEffect, useSentry } from '@jetstream/shared/ui-utils'; +import { getErrorMessage } from '@jetstream/shared/utils'; import { SplitWrapper as Split } from '@jetstream/splitjs'; import { DescribeGlobalSObjectResult, @@ -40,7 +40,7 @@ const HEIGHT_BUFFER = 160; export const CreateRecords = () => { const isMounted = useRef(true); - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const { defaultApiVersion } = useRecoilValue(applicationCookieState); @@ -177,7 +177,7 @@ export const CreateRecords = () => { } catch (ex) { if (isMounted.current) { logger.error('Error fetching metadata', ex); - rollbar.error('Error fetching record metadata', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error fetching record metadata', ex, 'CreateRecords'); setFormErrors({ hasErrors: true, fieldErrors: {}, diff --git a/libs/features/deploy/src/delete-metadata/DeleteMetadataConfigModal.tsx b/libs/features/deploy/src/delete-metadata/DeleteMetadataConfigModal.tsx index 06a4c3567..20bfc78cb 100644 --- a/libs/features/deploy/src/delete-metadata/DeleteMetadataConfigModal.tsx +++ b/libs/features/deploy/src/delete-metadata/DeleteMetadataConfigModal.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/react'; -import { getOrgType, useNonInitialEffect, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { getOrgType, useNonInitialEffect, useSentry } from '@jetstream/shared/ui-utils'; import { DeployOptions, ListMetadataResult, SalesforceOrgUi } from '@jetstream/types'; import { Grid, GridCol, Icon, Modal } from '@jetstream/ui'; import { OrgLabelBadge } from '@jetstream/ui-core'; @@ -29,7 +28,7 @@ export const DeleteMetadataConfigModal: FunctionComponent { - const rollbar = useRollbar(); + const sentry = useSentry(); const modalBodyRef = useRef(null); const [file, setFile] = useState(); const [isConfigValid, setIsConfigValid] = useState(true); @@ -96,7 +95,7 @@ export const DeleteMetadataConfigModal: FunctionComponent { const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const modalRef = useRef(null); const { serverUrl, google_apiKey, google_appId, google_clientId } = useRecoilValue(fromAppState.applicationCookieState); const { hasGoogleDriveAccess, googleShowUpgradeToPro } = useRecoilValue(googleDriveAccessState); @@ -95,7 +94,7 @@ export const DeployMetadataHistoryModal = ({ className }: DeployMetadataHistoryM } catch (ex) { logger.warn('Failed to get deploy history', ex); setError('There was an error retrieving your history.'); - rollbar.error('Failed to get deploy history', getErrorMessageAndStackObj(ex)); + sentry.trackError('Failed to get deploy history', ex, 'DeployMetadataHistoryModal'); } finally { setIsLoading(false); } @@ -122,7 +121,7 @@ export const DeployMetadataHistoryModal = ({ className }: DeployMetadataHistoryM trackEvent(ANALYTICS_KEYS.deploy_history_download_package, { type: item.type, status: item.status }); } catch (ex) { logger.warn('Failed to download deploy history item', ex); - rollbar.error('Failed to download deploy history item', getErrorMessageAndStackObj(ex)); + sentry.trackError('Failed to download deploy history item', ex, 'DeployMetadataHistoryModal'); fireToast({ message: 'There was an error downloading your file.', type: 'error', @@ -145,7 +144,7 @@ export const DeployMetadataHistoryModal = ({ className }: DeployMetadataHistoryM trackEvent(ANALYTICS_KEYS.deploy_history_view_details, { type: item.type, status: item.status }); } catch (ex) { logger.warn('Failed to view history', ex); - rollbar.error('Failed to view history', getErrorMessageAndStackObj(ex)); + sentry.trackError('Failed to view history', ex, 'DeployMetadataHistoryModal'); fireToast({ message: 'Oops. There was a problem opening your history', type: 'error', diff --git a/libs/features/deploy/src/deploy-metadata-package/DeployMetadataPackageConfigModal.tsx b/libs/features/deploy/src/deploy-metadata-package/DeployMetadataPackageConfigModal.tsx index e1501477f..df4c45aa7 100644 --- a/libs/features/deploy/src/deploy-metadata-package/DeployMetadataPackageConfigModal.tsx +++ b/libs/features/deploy/src/deploy-metadata-package/DeployMetadataPackageConfigModal.tsx @@ -1,8 +1,7 @@ import { css } from '@emotion/react'; import { logger } from '@jetstream/shared/client-logger'; import { INPUT_ACCEPT_FILETYPES } from '@jetstream/shared/constants'; -import { getOrgType, useNonInitialEffect, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { getOrgType, useNonInitialEffect, useSentry } from '@jetstream/shared/ui-utils'; import { DeployOptions, InputReadFileContent, Maybe, SalesforceOrgUi } from '@jetstream/types'; import { FileSelector, Grid, GridCol, Modal } from '@jetstream/ui'; import { OrgLabelBadge, OrgsCombobox } from '@jetstream/ui-core'; @@ -42,7 +41,7 @@ export const DeployMetadataPackageConfigModal: FunctionComponent { - const rollbar = useRollbar(); + const sentry = useSentry(); const orgs = useRecoilValue(salesforceOrgsState); const [destinationOrg, setDestinationOrg] = useState(initiallySelectedOrg); const [file, setFile] = useState>(initialFile); @@ -106,7 +105,7 @@ export const DeployMetadataPackageConfigModal: FunctionComponent { isMounted.current = true; @@ -220,10 +220,10 @@ export function useViewOrCompareMetadata({ selectedMetadata }: { selectedMetadat } catch (ex) { notifyUser('There was an error obtaining metadata', { body: getErrorMessage(ex), tag: 'view-or-compare' }); dispatch({ type: 'FETCH_ERROR', payload: { which, error: getErrorMessage(ex) } }); - rollbar.error('Error fetching/comparing metadata', { whichOrg: which, ...getErrorMessageAndStackObj(ex) }); + sentry.trackError('Error fetching/comparing metadata', ex, 'useViewOrCompareMetadata', { whichOrg: which }); } }, - [notifyUser, rollbar, selectedMetadata] + [notifyUser, sentry, selectedMetadata] ); return { diff --git a/libs/features/load-records/src/components/LoadRecordsDataPreview.tsx b/libs/features/load-records/src/components/LoadRecordsDataPreview.tsx index 2e8b177e8..712d89195 100644 --- a/libs/features/load-records/src/components/LoadRecordsDataPreview.tsx +++ b/libs/features/load-records/src/components/LoadRecordsDataPreview.tsx @@ -5,12 +5,11 @@ import { formatNumber } from '@jetstream/shared/ui-utils'; import { REGEX, groupByFlat } from '@jetstream/shared/utils'; import { DescribeGlobalSObjectResult, InsertUpdateUpsertDelete, Maybe, SalesforceOrgUi } from '@jetstream/types'; import { Alert, AutoFullHeightContainer, DataTable, Grid, GridCol, RowWithKey, Spinner, getColumnsForGenericTable } from '@jetstream/ui'; -import { ErrorBoundaryFallback, fromLoadRecordsState } from '@jetstream/ui-core'; +import { ErrorBoundary, fromLoadRecordsState } from '@jetstream/ui-core'; import { applicationCookieState, selectSkipFrontdoorAuth } from '@jetstream/ui/app-state'; import isNil from 'lodash/isNil'; import { FunctionComponent, useEffect, useRef, useState } from 'react'; import { Column } from 'react-data-grid'; -import { ErrorBoundary } from 'react-error-boundary'; import { useRecoilState, useRecoilValue } from 'recoil'; const MAX_RECORD_FOR_PREVIEW = 100_000; @@ -193,7 +192,7 @@ export const LoadRecordsDataPreview: FunctionComponent

File Preview

- + ({ success: 0, failure: 0 }); const [preparedData, setPreparedData] = useState(); const [prepareDataProgress, setPrepareDataProgress] = useState(0); @@ -155,10 +148,9 @@ export const LoadRecordsBatchApiResults = ({ body: `❌ Pre-processing records failed.`, tag: 'load-records', }); - rollbar.error('Error preparing batch api data', { - message: 'Pre-processing failed', - queryErrors: preparedData?.queryErrors, - errors: preparedDataResponse.errors?.flatMap((error) => error.errors) || [], + sentry.trackMessage('Error preparing batch api data', 'LoadRecordsBatchApiResults', { + queryErrors: preparedData?.queryErrors?.slice(0, 5) || [], + errors: preparedDataResponse.errors?.flatMap((error) => error.errors)?.slice(0, 5) || [], }); } else { setStatus(STATUSES.PROCESSING); @@ -177,7 +169,7 @@ export const LoadRecordsBatchApiResults = ({ body: `❌ ${getErrorMessage(ex)}`, tag: 'load-records', }); - rollbar.error('Error preparing batch api data', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error preparing batch api data', ex, 'LoadRecordsBatchApiResults'); return; } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -230,7 +222,7 @@ export const LoadRecordsBatchApiResults = ({ body: `❌ ${getErrorMessage(ex)}`, tag: 'load-records', }); - rollbar.error('Error loading batches', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error loading batches', ex, 'LoadRecordsBatchApiResults'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/libs/features/load-records/src/components/load-results/LoadRecordsBulkApiResults.tsx b/libs/features/load-records/src/components/load-results/LoadRecordsBulkApiResults.tsx index 959106b8d..20c883947 100644 --- a/libs/features/load-records/src/components/load-results/LoadRecordsBulkApiResults.tsx +++ b/libs/features/load-records/src/components/load-results/LoadRecordsBulkApiResults.tsx @@ -2,11 +2,10 @@ import { css } from '@emotion/react'; import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; import { bulkApiAbortJob, bulkApiGetJob, bulkApiGetRecords, bulkApiGetRecordsFromAllBatches } from '@jetstream/shared/data'; -import { checkIfBulkApiJobIsDone, convertDateToLocale, useBrowserNotifications, useRollbar } from '@jetstream/shared/ui-utils'; +import { checkIfBulkApiJobIsDone, convertDateToLocale, useBrowserNotifications, useSentry } from '@jetstream/shared/ui-utils'; import { decodeHtmlEntity, getErrorMessage, - getErrorMessageAndStackObj, getSuccessOrFailureChar, pluralizeFromNumber, splitArrayToMaxSize, @@ -111,7 +110,7 @@ export const LoadRecordsBulkApiResults = ({ const isMounted = useRef(true); const isAborted = useRef(false); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const { serverUrl, google_apiKey, google_appId, google_clientId } = useRecoilValue(applicationCookieState); const { hasGoogleDriveAccess, googleShowUpgradeToPro } = useRecoilValue(googleDriveAccessState); const skipFrontDoorAuth = useRecoilValue(selectSkipFrontdoorAuth); @@ -270,7 +269,9 @@ export const LoadRecordsBulkApiResults = ({ body: `❌ Pre-processing records failed.`, tag: 'load-records', }); - rollbar.error('Error preparing bulk api data', { queryErrors: preparedDataResponse?.queryErrors }); + sentry.trackError('Error preparing bulk api data', new Error('Error preparing bulk api data'), 'LoadRecordsBulkApiResults', { + queryErrors: preparedDataResponse?.queryErrors, + }); } else { setStatus(STATUSES.UPLOADING); setPreparedData(preparedDataResponse); @@ -287,7 +288,7 @@ export const LoadRecordsBulkApiResults = ({ body: `❌ ${getErrorMessage(ex)}`, tag: 'load-records', }); - rollbar.error('Error preparing bulk api data', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error preparing bulk api data', ex, 'LoadRecordsBulkApiResults'); return; } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -341,7 +342,7 @@ export const LoadRecordsBulkApiResults = ({ tag: 'load-records', }); } - rollbar.error('Error loading batches', { + sentry.trackError('Error loading batches', new Error('Error loading batches'), 'LoadRecordsBulkApiResults', { message: loadError.message, stack: loadError.stack, specificErrors: loadError.additionalErrors.map((error) => ({ diff --git a/libs/features/load-records/src/steps/PerformLoadCustomMetadata.tsx b/libs/features/load-records/src/steps/PerformLoadCustomMetadata.tsx index 7c5bdf17d..9fa3d632f 100644 --- a/libs/features/load-records/src/steps/PerformLoadCustomMetadata.tsx +++ b/libs/features/load-records/src/steps/PerformLoadCustomMetadata.tsx @@ -1,6 +1,6 @@ import { ANALYTICS_KEYS, DATE_FORMATS, TITLES } from '@jetstream/shared/constants'; -import { formatNumber, useNonInitialEffect, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj, groupByFlat } from '@jetstream/shared/utils'; +import { formatNumber, useNonInitialEffect, useSentry } from '@jetstream/shared/ui-utils'; +import { groupByFlat } from '@jetstream/shared/utils'; import { DeployMessage, DownloadModalData, @@ -59,7 +59,7 @@ export const PerformLoadCustomMetadata = ({ inputFileData, onIsLoading, }: PerformLoadCustomMetadataProps) => { - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const { serverUrl, defaultApiVersion, google_apiKey, google_appId, google_clientId } = useRecoilValue(applicationCookieState); const { hasGoogleDriveAccess, googleShowUpgradeToPro } = useRecoilValue(googleDriveAccessState); @@ -124,7 +124,7 @@ export const PerformLoadCustomMetadata = ({ document.title = `Loading Records | ${TITLES.BAR_JETSTREAM}`; } catch (ex) { setDeployMetadataError('There was a problem preparing the Custom Metadata for deployment. Please check your file and try again.'); - rollbar.error('Problem preparing custom metadata', getErrorMessageAndStackObj(ex)); + sentry.trackError('Problem preparing custom metadata', ex, 'PerformLoadCustomMetadata'); } } } diff --git a/libs/features/manage-permissions/src/usePermissionRecords.tsx b/libs/features/manage-permissions/src/usePermissionRecords.tsx index d36203252..cf6e02c81 100644 --- a/libs/features/manage-permissions/src/usePermissionRecords.tsx +++ b/libs/features/manage-permissions/src/usePermissionRecords.tsx @@ -1,7 +1,7 @@ import { logger } from '@jetstream/shared/client-logger'; import { queryAll, queryAllUsingOffset } from '@jetstream/shared/data'; -import { useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj, groupByFlat } from '@jetstream/shared/utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; +import { getErrorMessage, groupByFlat } from '@jetstream/shared/utils'; import { EntityParticlePermissionsRecord, FieldPermissionDefinitionMap, @@ -25,7 +25,7 @@ import { export function usePermissionRecords(selectedOrg: SalesforceOrgUi, sobjects: string[], profilePermSetIds: string[], permSetIds: string[]) { const isMounted = useRef(true); - const rollbar = useRollbar(); + const sentry = useSentry(); const [loading, setLoading] = useState(true); const [hasError, setHasError] = useState(false); @@ -96,7 +96,7 @@ export function usePermissionRecords(selectedOrg: SalesforceOrgUi, sobjects: str } } catch (ex) { logger.warn('[useProfilesAndPermSets][ERROR]', getErrorMessage(ex)); - rollbar.error('[useProfilesAndPermSets][ERROR]', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error fetching permission metadata', ex, 'usePermissionRecords'); if (isMounted.current) { setHasError(true); } diff --git a/libs/features/organizations/src/lib/OrganizationModal.tsx b/libs/features/organizations/src/lib/OrganizationModal.tsx index 867c5a1ef..e58833559 100644 --- a/libs/features/organizations/src/lib/OrganizationModal.tsx +++ b/libs/features/organizations/src/lib/OrganizationModal.tsx @@ -1,5 +1,5 @@ -import { useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; +import { getErrorMessage } from '@jetstream/shared/utils'; import { JetstreamOrganizationCreateUpdatePayload, JetstreamOrganizationWithOrgs, Maybe } from '@jetstream/types'; import { fireToast, Grid, Input, Modal, Textarea } from '@jetstream/ui'; import { ConfirmPageChange } from '@jetstream/ui-core'; @@ -13,7 +13,7 @@ interface OrganizationModalProps { export function OrganizationModal({ organization, onSubmit, onClose }: OrganizationModalProps) { const isMounted = useRef(true); - const rollbar = useRollbar(); + const sentry = useSentry(); const [loading, setLoading] = useState(false); const [updatedOrg, setUpdatedOrg] = useState(() => ({ name: organization?.name || '', @@ -34,7 +34,7 @@ export function OrganizationModal({ organization, onSubmit, onClose }: Organizat onClose(); } catch (ex) { fireToast({ message: `There was an error creating the organization. ${getErrorMessage(ex)}`, type: 'error' }); - rollbar.error('OrganizationModal: Error creating organization', getErrorMessageAndStackObj(ex)); + sentry.trackError('OrganizationModal: Error creating organization', ex, 'OrganizationModal'); } finally { setLoading(false); } diff --git a/libs/features/organizations/src/lib/OrganizationPage.tsx b/libs/features/organizations/src/lib/OrganizationPage.tsx index b9447371f..53e4bbf21 100644 --- a/libs/features/organizations/src/lib/OrganizationPage.tsx +++ b/libs/features/organizations/src/lib/OrganizationPage.tsx @@ -8,8 +8,7 @@ import { updateJetstreamOrganization, } from '@jetstream/shared/data'; import { APP_ROUTES } from '@jetstream/shared/ui-router'; -import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useSentry, useTitle } from '@jetstream/shared/ui-utils'; import { JetstreamOrganization, JetstreamOrganizationCreateUpdatePayload, JetstreamOrganizationWithOrgs, Maybe } from '@jetstream/types'; import { AutoFullHeightContainer, @@ -33,7 +32,7 @@ import { OrganizationModal } from './OrganizationModal'; export function Organizations() { useTitle(TITLES.ORGANIZATIONS); - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const selectedOrg = useRecoilValue(fromAppState.selectedOrgStateWithoutPlaceholder); const setSelectedOrgId = useSetRecoilState(fromAppState.selectedOrgIdState); @@ -112,21 +111,11 @@ export function Organizations() { message: `Oops! There was a problem moving the Salesforce Org to the Organization. Please try again.`, type: 'error', }); - rollbar.error('Organizations: Error moving org to organization', getErrorMessageAndStackObj(ex)); + sentry.trackError('Organizations: Error moving org to organization', ex, 'Organizations'); logger.error('Organizations: Error moving org to organization', ex); } }, - [ - allOrgs, - organizations, - rollbar, - selectedOrg?.uniqueId, - setOrganizations, - setOrganizationsFromDb, - setOrgs, - setSelectedOrgId, - trackEvent, - ] + [allOrgs, organizations, sentry, selectedOrg?.uniqueId, setOrganizations, setOrganizationsFromDb, setOrgs, setSelectedOrgId, trackEvent] ); const handleCreateOrUpdate = async (organization: JetstreamOrganizationCreateUpdatePayload) => { diff --git a/libs/features/platform-event-monitor/src/usePlatformEvent.ts b/libs/features/platform-event-monitor/src/usePlatformEvent.ts index 65fbeed01..ce1e64de1 100644 --- a/libs/features/platform-event-monitor/src/usePlatformEvent.ts +++ b/libs/features/platform-event-monitor/src/usePlatformEvent.ts @@ -1,8 +1,8 @@ import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; import { clearCacheForOrg, describeGlobal, sobjectOperation } from '@jetstream/shared/data'; -import { useDebounce, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessage, getErrorMessageAndStackObj, orderValues } from '@jetstream/shared/utils'; +import { useDebounce, useSentry } from '@jetstream/shared/ui-utils'; +import { getErrorMessage, orderValues } from '@jetstream/shared/utils'; import { Maybe, PlatformEventMessage, PlatformEventMessagePayload, SalesforceOrgUi } from '@jetstream/types'; import { fireToast } from '@jetstream/ui'; import { useAmplitude } from '@jetstream/ui-core'; @@ -23,7 +23,7 @@ export interface PlatformEventDownloadData { export function usePlatformEvent({ selectedOrg }: { selectedOrg: SalesforceOrgUi }) { const isMounted = useRef(true); const cometD = useRef(); - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const [{ serverUrl, defaultApiVersion }] = useRecoilState(applicationCookieState); const [platformEvents, setPlatformEvents] = useState([]); @@ -82,10 +82,10 @@ export function usePlatformEvent({ selectedOrg }: { selectedOrg: SalesforceOrgUi } } catch (ex) { setPlatformEventFetchError(getErrorMessage(ex)); - rollbar.error(`Fetch platform event error`, getErrorMessageAndStackObj(ex)); + sentry.trackError(`Fetch platform event error`, ex, 'usePlatformEvent'); } }, - [rollbar, selectedOrg] + [sentry, selectedOrg] ); useEffect(() => { diff --git a/libs/features/query/src/QueryHistory/QueryHistory.tsx b/libs/features/query/src/QueryHistory/QueryHistory.tsx index b7173e46b..47fafe988 100644 --- a/libs/features/query/src/QueryHistory/QueryHistory.tsx +++ b/libs/features/query/src/QueryHistory/QueryHistory.tsx @@ -26,13 +26,12 @@ import { Spinner, Tooltip, } from '@jetstream/ui'; -import { ErrorBoundaryFallback, fromQueryHistoryState, useAmplitude } from '@jetstream/ui-core'; +import { ErrorBoundary, fromQueryHistoryState, useAmplitude } from '@jetstream/ui-core'; import { dexieDb, queryHistoryDb } from '@jetstream/ui/db'; import classNames from 'classnames'; import { useLiveQuery } from 'dexie-react-hooks'; import uniqBy from 'lodash/uniqBy'; import { createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { useLocation } from 'react-router-dom'; import QueryHistoryEmptyState from './QueryHistoryEmptyState'; import QueryHistoryItemCard from './QueryHistoryItemCard'; @@ -265,7 +264,7 @@ export const QueryHistory = forwardRef(({ className, sel } return ( - + { - const rollbar = useRollbar(); + const sentry = useSentry(); const [loading, setLoading] = useState(false); const [refreshLoading, setRefreshLoading] = useState(false); const [isValid, setIsValid] = useState(false); @@ -240,7 +240,7 @@ export const BulkUpdateFromQueryModal: FunctionComponent { sobjectName = sobjectName?.toLowerCase() || null; - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const [modalOpen, setModalOpen] = useState(false); const [downloadAttachmentUrl, setDownloadAttachmentUrl] = useState(null); @@ -117,7 +117,7 @@ export const QueryResultsAttachmentDownload: FunctionComponent(null); @@ -161,8 +160,8 @@ export const useQueryRestore = ( setErrorMessage(ex.message); } else { logger.warn('[QUERY RESTORE][ERROR]', ex); - setErrorMessage('An unknown error has ocurred while restoring your query'); - rollbar.error('Query Restore Error', { ...getErrorMessageAndStackObj(ex), query: currSoql }); + setErrorMessage('An unknown error has occurred while restoring your query'); + sentry.trackError('Query Restore Error', ex, 'useQueryRestore', { query: currSoql }); } endRestore(true, toolingOverride ?? isTooling); } diff --git a/libs/features/record-type-manager/src/lib/deployment/DeploymentModal.tsx b/libs/features/record-type-manager/src/lib/deployment/DeploymentModal.tsx index 9f04618f3..2885d625a 100644 --- a/libs/features/record-type-manager/src/lib/deployment/DeploymentModal.tsx +++ b/libs/features/record-type-manager/src/lib/deployment/DeploymentModal.tsx @@ -1,8 +1,7 @@ import { css } from '@emotion/react'; import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS, MIME_TYPES } from '@jetstream/shared/constants'; -import { saveFile, useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { saveFile, useSentry } from '@jetstream/shared/ui-utils'; import { ReadMetadataRecordTypeExtended } from '@jetstream/types'; import { Grid, GridCol, Icon, Modal, ScopedNotification, Spinner } from '@jetstream/ui'; import { @@ -27,7 +26,7 @@ interface DeploymentModalProps { } export function DeploymentModal({ modifiedValues, recordTypeMetadataByFullName, viewMode, onClose }: DeploymentModalProps) { - const rollbar = useRollbar(); + const sentry = useSentry(); const { trackEvent } = useAmplitude(); const { defaultApiVersion: apiVersion, serverUrl } = useRecoilValue(applicationCookieState); const selectedOrg = useRecoilValue(selectedOrgState); @@ -73,7 +72,7 @@ export function DeploymentModal({ modifiedValues, recordTypeMetadataByFullName, } catch (ex) { logger.error('Error preparing deployment package', ex); setPrepareError(true); - rollbar.error('Record Type Picklist: Error preparing deployment package', getErrorMessageAndStackObj(ex)); + sentry.trackError('Record Type Picklist: Error preparing deployment package', ex, 'DeploymentModal'); } finally { setInProgress(false); } diff --git a/libs/features/record-type-manager/src/lib/utils/useLoadRecordTypeData.tsx b/libs/features/record-type-manager/src/lib/utils/useLoadRecordTypeData.tsx index c6bb1a3cf..2bcb12d17 100644 --- a/libs/features/record-type-manager/src/lib/utils/useLoadRecordTypeData.tsx +++ b/libs/features/record-type-manager/src/lib/utils/useLoadRecordTypeData.tsx @@ -1,8 +1,8 @@ import { logger } from '@jetstream/shared/client-logger'; import { SFDC_BLANK_PICKLIST_VALUE } from '@jetstream/shared/constants'; import { describeSObject, readMetadata } from '@jetstream/shared/data'; -import { useRollbar } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj, groupByFlat, orderValues, splitArrayToMaxSize } from '@jetstream/shared/utils'; +import { useSentry } from '@jetstream/shared/ui-utils'; +import { groupByFlat, orderValues, splitArrayToMaxSize } from '@jetstream/shared/utils'; import { DescribeSObjectResult, ReadMetadataRecordType, ReadMetadataRecordTypeExtended } from '@jetstream/types'; import { fromRecordTypeManagerState } from '@jetstream/ui-core'; import { selectedOrgState } from '@jetstream/ui/app-state'; @@ -26,7 +26,7 @@ const ignoredPicklistSuffix = ['StateCode', 'CountryCode', 'GeocodeAccuracy', 'S export function useLoadRecordTypeData() { const isMounted = useRef(true); - const rollbar = useRollbar(); + const sentry = useSentry(); const selectedOrg = useRecoilValue(selectedOrgState); const selectedRecordTypes = useRecoilValue(fromRecordTypeManagerState.selectedRecordTypes); @@ -88,13 +88,13 @@ export function useLoadRecordTypeData() { _sobjects[sobject] = await describeSObject(selectedOrg, sobject).then((item) => item.data); } catch (ex) { logger.warn(`Error loading object ${sobject}`, ex); - rollbar.error('Record Type Manager: Error loading object', sobject, getErrorMessageAndStackObj(ex)); + sentry.trackError('Record Type Manager: Error loading object', ex, 'useLoadRecordTypeData', { sobject }); } } setSobjects(_sobjects); return _sobjects; }, - [rollbar, selectedOrg] + [sentry, selectedOrg] ); const loadData = useCallback(async () => { @@ -190,9 +190,9 @@ export function useLoadRecordTypeData() { } catch (ex) { setHasError(true); logger.error('Error loading record types', ex); - rollbar.error('Record Type Manager: Error loading record types', getErrorMessageAndStackObj(ex)); + sentry.trackError('Record Type Manager: Error loading record types', ex, 'useLoadRecordTypeData'); } - }, [loadObjectMetadata, loadRecordTypePicklist, rollbar]); + }, [loadObjectMetadata, loadRecordTypePicklist, sentry]); const resetData = useCallback(async () => { dispatch({ type: 'RESET' }); diff --git a/libs/features/salesforce-api/src/SalesforceApi.tsx b/libs/features/salesforce-api/src/SalesforceApi.tsx index 3373374d5..a31ea125e 100644 --- a/libs/features/salesforce-api/src/SalesforceApi.tsx +++ b/libs/features/salesforce-api/src/SalesforceApi.tsx @@ -2,8 +2,7 @@ import { css } from '@emotion/react'; import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS, TITLES } from '@jetstream/shared/constants'; import { manualRequest } from '@jetstream/shared/data'; -import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; -import { getErrorMessageAndStackObj } from '@jetstream/shared/utils'; +import { useSentry, useTitle } from '@jetstream/shared/ui-utils'; import { SplitWrapper as Split } from '@jetstream/splitjs'; import { ManualRequestPayload, ManualRequestResponse, Maybe, SalesforceApiHistoryRequest, SalesforceOrgUi } from '@jetstream/types'; import { AutoFullHeightContainer } from '@jetstream/ui'; @@ -23,7 +22,7 @@ export const SalesforceApi: FunctionComponent = () => { const isMounted = useRef(true); const [{ defaultApiVersion }] = useRecoilState(applicationCookieState); const { trackEvent } = useAmplitude(); - const rollbar = useRollbar(); + const sentry = useSentry(); const selectedOrg = useRecoilValue(selectedOrgState); const [request, setRequest] = useState>(); const [results, setResults] = useState>(null); @@ -65,7 +64,7 @@ export const SalesforceApi: FunctionComponent = () => { }) .catch((ex) => { logger.warn('[ERROR] Could not save history', ex); - rollbar.error('Error saving apex history', getErrorMessageAndStackObj(ex)); + sentry.trackError('Error saving apex history', ex, 'SalesforceApi'); }); } catch (ex) { setResults({ @@ -79,7 +78,7 @@ export const SalesforceApi: FunctionComponent = () => { setLoading(false); } }, - [selectedOrg, trackEvent, rollbar] + [selectedOrg, trackEvent, sentry] ); return ( diff --git a/libs/features/salesforce-api/src/SalesforceApiExamplesModal.tsx b/libs/features/salesforce-api/src/SalesforceApiExamplesModal.tsx index 348ab89ac..3cfcd601d 100644 --- a/libs/features/salesforce-api/src/SalesforceApiExamplesModal.tsx +++ b/libs/features/salesforce-api/src/SalesforceApiExamplesModal.tsx @@ -4,10 +4,9 @@ import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; import { salesforceApiReq } from '@jetstream/shared/data'; import { SalesforceApiRequest } from '@jetstream/types'; import { AutoFullHeightContainer, ColumnWithFilter, DataTree, Grid, Icon, Modal, setColumnFromType, Spinner } from '@jetstream/ui'; -import { ErrorBoundaryFallback, useAmplitude } from '@jetstream/ui-core'; +import { ErrorBoundary, useAmplitude } from '@jetstream/ui-core'; import groupBy from 'lodash/groupBy'; import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; const groupedRows = ['groupName'] as const; @@ -95,7 +94,7 @@ export const SalesforceApiExamplesModal: FunctionComponent +