diff --git a/lib/Models/ErrorServiceProviders/ErrorService.ts b/lib/Models/ErrorServiceProviders/ErrorService.ts index cff77c15d1..3f9e6e82c0 100644 --- a/lib/Models/ErrorServiceProviders/ErrorService.ts +++ b/lib/Models/ErrorServiceProviders/ErrorService.ts @@ -1,5 +1,5 @@ import TerriaError from "../../Core/TerriaError"; -import SentryErrorServiceProvider from "./SentryErrorServiceProvider"; +import { ConfigParameters } from "../Terria"; export interface ErrorServiceOptions { provider: string; configuration: any; @@ -8,27 +8,7 @@ export interface ErrorServiceOptions { /** * The interface that all valid error service providers must implement. */ -export type ErrorServiceProvider = { - error(error: string | Error | TerriaError): void; -}; - -/** - * Asynchronously loads and returns an error service provider instance for the given options. - */ -export async function initializeErrorServiceProvider( - options?: ErrorServiceOptions -): Promise { - const provider = options?.provider; - const configuration = options?.configuration; - - if (provider === "rollbar") { - const rollbarModule = await import("./RollbarErrorServiceProvider"); - const rollbarProvider = new rollbarModule.default(configuration); - return rollbarProvider; - } else if (provider === "sentry") { - const sentryProvider = new SentryErrorServiceProvider(); - return sentryProvider; - } else { - throw new Error(`Unknown error service provider: ${provider}`); - } +export interface ErrorServiceProvider { + init: (config: ConfigParameters) => void; + error: (error: string | Error | TerriaError) => void; } diff --git a/lib/Models/ErrorServiceProviders/RollbarErrorServiceProvider.ts b/lib/Models/ErrorServiceProviders/RollbarErrorServiceProvider.ts deleted file mode 100644 index c2831b6a57..0000000000 --- a/lib/Models/ErrorServiceProviders/RollbarErrorServiceProvider.ts +++ /dev/null @@ -1,39 +0,0 @@ -import Rollbar from "rollbar"; -import TerriaError, { TerriaErrorSeverity } from "../../Core/TerriaError"; -import { ErrorServiceProvider } from "./ErrorService"; - -export default class RollbarErrorServiceProvider - implements ErrorServiceProvider -{ - readonly rollbar: Rollbar; - - /** - * @param configuration Configuration for the Rollbar instance. See https://docs.rollbar.com/docs/rollbarjs-configuration-reference#context-1 - * - * Caveat: Rollbar API requests are blocked by some privacy extensions for browsers. - */ - constructor(configuration: any) { - this.rollbar = new Rollbar({ - captureUncaught: true, - captureUnhandledRejections: true, - enabled: true, - ...configuration - }); - } - - error(error: string | Error | TerriaError) { - if (error instanceof TerriaError) { - if ( - (typeof error.severity === "function" - ? error.severity() - : error.severity) === TerriaErrorSeverity.Error - ) { - this.rollbar.error(error.toError()); - } else { - this.rollbar.warning(error.toError()); - } - } else { - this.rollbar.error(error); - } - } -} diff --git a/lib/Models/ErrorServiceProviders/SentryErrorServiceProvider.ts b/lib/Models/ErrorServiceProviders/SentryErrorServiceProvider.ts deleted file mode 100644 index 6958c1c259..0000000000 --- a/lib/Models/ErrorServiceProviders/SentryErrorServiceProvider.ts +++ /dev/null @@ -1,44 +0,0 @@ -import TerriaError from "../../Core/TerriaError"; -import { ErrorServiceProvider } from "./ErrorService"; -import * as Sentry from "@sentry/react"; - -/** - * A stub error service provider that does nothing. - */ -export default class SentryErrorServiceProvider - implements ErrorServiceProvider -{ - constructor() { - console.log("init sentry"); - Sentry.init({ - dsn: "https://87c898683a4f6875b3209c9c81fb9cb7@o4507722649829376.ingest.us.sentry.io/4507722651205632", - integrations: [ - Sentry.browserTracingIntegration(), - Sentry.browserProfilingIntegration(), - Sentry.replayIntegration() - ], - // Performance Monitoring - tracesSampleRate: 1.0, // Capture 100% of the transactions - // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost"], - // Session Replay - replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. - replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. - // Set profilesSampleRate to 1.0 to profile every transaction. - // Since profilesSampleRate is relative to tracesSampleRate, - // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate - // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would - // results in 25% of transactions being profiled (0.5*0.5=0.25) - profilesSampleRate: 1.0 - }); - } - - error(_error: string | Error | TerriaError) { - console.log("sentry error", _error); - if (_error instanceof TerriaError) { - Sentry.captureException(_error.toError()); - } else { - Sentry.captureException(_error); - } - } -} diff --git a/lib/Models/ErrorServiceProviders/StubErrorServiceProvider.ts b/lib/Models/ErrorServiceProviders/StubErrorServiceProvider.ts index 689d828099..6ac53fe3eb 100644 --- a/lib/Models/ErrorServiceProviders/StubErrorServiceProvider.ts +++ b/lib/Models/ErrorServiceProviders/StubErrorServiceProvider.ts @@ -5,5 +5,6 @@ import { ErrorServiceProvider } from "./ErrorService"; * A stub error service provider that does nothing. */ export default class StubErrorServiceProvider implements ErrorServiceProvider { + init() {} error(_error: string | Error | TerriaError) {} } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 85d8880083..c31d61a7ea 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -96,8 +96,7 @@ import updateModelFromJson from "./Definition/updateModelFromJson"; import upsertModelFromJson from "./Definition/upsertModelFromJson"; import { ErrorServiceOptions, - ErrorServiceProvider, - initializeErrorServiceProvider + ErrorServiceProvider } from "./ErrorServiceProviders/ErrorService"; import StubErrorServiceProvider from "./ErrorServiceProviders/StubErrorServiceProvider"; import TerriaFeature from "./Feature/Feature"; @@ -360,6 +359,7 @@ interface StartOptions { }; applicationUrl?: Location; shareDataService?: ShareDataService; + errorService?: ErrorServiceProvider; /** * i18nOptions is explicitly a separate option from `languageConfiguration`, * as `languageConfiguration` can be serialised, but `i18nOptions` may have @@ -679,8 +679,8 @@ export default class Terria { readonly developmentEnv = process?.env?.NODE_ENV === "development"; /** - * An error service instance. The instance can be configured by setting the - * `errorService` config parameter. Here we initialize it to stub provider so + * An error service instance. The instance can be provided via the + * `errorService` startOption. Here we initialize it to stub provider so * that the `terria.errorService` always exists. */ errorService: ErrorServiceProvider = new StubErrorServiceProvider(); @@ -912,20 +912,6 @@ export default class Terria { this.modelIdShareKeysMap.set(id, [shareKey]); } - /** - * Initialize errorService from config parameters. - */ - setupErrorServiceProvider(errorService: ErrorServiceOptions) { - initializeErrorServiceProvider(errorService) - .then((errorService) => { - this.errorService = errorService; - console.log("error initd", errorService); - }) - .catch((e) => { - console.error("Failed to initialize error service", e); - }); - } - setupInitializationUrls(baseUri: uri.URI, config: any) { const initializationUrls: string[] = config?.initializationUrls || []; const initSources: InitSource[] = initializationUrls.map((url) => ({ @@ -1017,10 +1003,6 @@ export default class Terria { if (isJsonObject(config) && isJsonObject(config.parameters)) { this.updateParameters(config.parameters); } - - if (this.configParameters.errorService) { - this.setupErrorServiceProvider(this.configParameters.errorService); - } this.setupInitializationUrls(baseUri, config); }); } catch (error) { @@ -1042,7 +1024,17 @@ export default class Terria { setCustomRequestSchedulerDomainLimits( this.configParameters.customRequestSchedulerLimits ); - + if (options.errorService) { + try { + this.errorService = options.errorService; + this.errorService.init(this.configParameters); + } catch (e) { + console.error( + `Failed to initialize error service: ${this.configParameters.errorService?.provider}`, + e + ); + } + } this.analytics?.start(this.configParameters); this.analytics?.logEvent( Category.launch, diff --git a/package.json b/package.json index 8ad6420820..09da43ea11 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,6 @@ "react-virtual": "~2.3.2", "resolve-url-loader": "^5.0.0", "retry": "^0.12.0", - "rollbar": "^2.24.0", "sass-loader": "^10", "shpjs": "^4.0.4", "simple-statistics": "^7.0.1", diff --git a/test/Models/ErrorServiceSpec.ts b/test/Models/ErrorServiceSpec.ts index eaa5ca9fa7..f745ebf929 100644 --- a/test/Models/ErrorServiceSpec.ts +++ b/test/Models/ErrorServiceSpec.ts @@ -1,25 +1,60 @@ -import { initializeErrorServiceProvider } from "../../lib/Models/ErrorServiceProviders/ErrorService"; -import RollbarErrorServiceProvider from "../../lib/Models/ErrorServiceProviders/RollbarErrorServiceProvider"; +import TerriaError from "../../lib/Core/TerriaError"; +import { ErrorServiceProvider } from "../../lib/Models/ErrorServiceProviders/ErrorService"; +import StubErrorServiceProvider from "../../lib/Models/ErrorServiceProviders/StubErrorServiceProvider"; +import Terria from "../../lib/Models/Terria"; -describe("initializeErrorServiceProvider", function () { - it("can initialize RollbarErrorServiceProvider", async function () { - const errorService = await initializeErrorServiceProvider({ - provider: "rollbar", - configuration: {} +describe("ErrorService", function () { + let mockErrorServiceProvider: ErrorServiceProvider; + let terria: Terria; + + beforeAll(() => { + jasmine.Ajax.install(); + jasmine.Ajax.stubRequest(/.*/).andError({}); + jasmine.Ajax.stubRequest(/.*(serverconfig|proxyabledomains).*/).andReturn({ + responseText: JSON.stringify({ foo: "bar" }) + }); + jasmine.Ajax.stubRequest("test-config.json").andReturn({ + responseText: JSON.stringify({ config: true }) }); - expect(errorService instanceof RollbarErrorServiceProvider).toBe(true); + mockErrorServiceProvider = { + init: () => {}, + error: () => {} + }; }); - it("throws an error when an invalid provider type is given", async function () { - let error; - try { - await initializeErrorServiceProvider({ - provider: "foo", - configuration: undefined - }); - } catch (e: any) { - error = e; - } - expect(error.message).toBe(`Unknown error service provider: foo`); + beforeEach(() => { + terria = new Terria({ + appBaseHref: "/", + baseUrl: "./" + }); + }); + + it("Initializes an error service, passing in config", async function () { + const initSpy = spyOn(mockErrorServiceProvider, "init"); + await terria.start({ + configUrl: "test-config.json", + errorService: mockErrorServiceProvider + }); + expect(initSpy).toHaveBeenCalledTimes(1); + }); + + it("Gets called with error", async function () { + const errorSpy = spyOn(mockErrorServiceProvider, "error").and.callThrough(); + await terria.start({ + configUrl: "test-config.json", + errorService: mockErrorServiceProvider + }); + const error = new TerriaError({ + message: "test error" + }); + terria.raiseErrorToUser(error); + expect(errorSpy).toHaveBeenCalledWith(error); + }); + + it("Falls back to stub provider", () => { + terria.start({ + configUrl: "test-config.json" + }); + expect(terria.errorService).toEqual(jasmine.any(StubErrorServiceProvider)); }); });