-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Centralise frontend error reporting (and suppress unactionable Sentry…
… errors) (#3850) * Centralise frontend network error reporting * Add shim for tests * Revert unnecessary change to `useSearch` * Remove unnecessary errors plugin shim causing jest.mock to fail for feature flag json See note regarding setup-after-env in this section of Jest documentation: https://jestjs.io/docs/jest-object\#jestmockmodulename-factory-options * Fix jest types to match actual jest version * Add unit tests for errors plugin * Skip 429s and rearrange function for clarity * WIP/ PR does not work due to analytics composable * Consider SSR/CSR in test * Remove stray console.error * Add search route network error handling test * Fix bad merge of pnpm lock
- Loading branch information
1 parent
1e15853
commit ee3caaa
Showing
15 changed files
with
500 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import axios from "axios" | ||
import { Plugin, Context } from "@nuxt/types" | ||
|
||
import { ERR_UNKNOWN, ErrorCode, errorCodes } from "~/constants/errors" | ||
import type { FetchingError, RequestKind } from "~/types/fetch-state" | ||
import type { SupportedSearchType } from "~/constants/media" | ||
|
||
const isValidErrorCode = ( | ||
code: string | undefined | null | ||
): code is ErrorCode => { | ||
if (!code) { | ||
return false | ||
} | ||
return (errorCodes as readonly string[]).includes(code) | ||
} | ||
|
||
function isDetailedResponseData(data: unknown): data is { detail: string } { | ||
return !!data && typeof data === "object" && "detail" in data | ||
} | ||
|
||
/** | ||
* Normalize any error occurring during a network call. | ||
* | ||
* @param error - Any error arising during a network call | ||
* @param searchType - The type of search selected when the error occurred | ||
* @param requestKind - The kind of request the error occurred for | ||
* @param details - Any additional details to attach to the error | ||
* @returns Normalized error object | ||
*/ | ||
export function normalizeFetchingError( | ||
error: unknown, | ||
searchType: SupportedSearchType, | ||
requestKind: RequestKind, | ||
details?: Record<string, string> | ||
): FetchingError { | ||
const fetchingError: FetchingError = { | ||
requestKind, | ||
details, | ||
searchType, | ||
code: ERR_UNKNOWN, | ||
} | ||
|
||
if (!axios.isAxiosError(error)) { | ||
fetchingError.message = (error as Error).message | ||
return fetchingError | ||
} | ||
|
||
// Otherwise, it's an AxiosError | ||
if (isValidErrorCode(error.code)) { | ||
fetchingError.code = error.code | ||
} | ||
|
||
if (error.response?.status) { | ||
fetchingError.statusCode = error.response.status | ||
} | ||
|
||
const responseData = error?.response?.data | ||
|
||
// Use the message returned by the API. | ||
if (isDetailedResponseData(responseData)) { | ||
fetchingError.message = responseData.detail as string | ||
} else { | ||
fetchingError.message = error.message | ||
} | ||
|
||
return fetchingError | ||
} | ||
|
||
/** | ||
* Record network errors using the appropriate tool, as needed, | ||
* based on response code, status, and request kind. | ||
* @param error - The error to record | ||
*/ | ||
function recordError( | ||
context: Context, | ||
originalError: unknown, | ||
fetchingError: FetchingError | ||
) { | ||
if (fetchingError.statusCode === 429) { | ||
// These are more readily monitored via the Cloudflare dashboard. | ||
return | ||
} | ||
|
||
if ( | ||
fetchingError.requestKind === "single-result" && | ||
fetchingError.statusCode === 404 | ||
) { | ||
/** | ||
* Do not record 404s for single result requests because: | ||
* 1. Plausible will already record them as resulting in a 404 page view | ||
* 2. The Openverse API 404s on malformed identifiers, so there is no way | ||
* to distinguish between truly not found works and bad requests from | ||
* the client side. | ||
* 3. There isn't much we can do other than monitor for an anomalously high | ||
* number of 404 responses from the frontend server that could indicate a frontend | ||
* implementation or configuration error suddenly causing malformed | ||
* identifiers to be used. Neither Sentry nor Plausible are the right tool | ||
* for that task. If the 404s are caused by an API issue, we'd see that in | ||
* API response code monitoring, where we can more easily trace the cause | ||
*/ | ||
return | ||
} | ||
|
||
if (process.client && fetchingError.code === "ERR_NETWORK") { | ||
/** | ||
* Record network errors in Plausible so that we can evaluate potential | ||
* regional or device configuration issues, for which Sentry is not | ||
* as good a tool. Additionally, the number of these events are trivial | ||
* for Plausible, but do actually affect our Sentry quota enough that it | ||
* is worth diverting them. | ||
*/ | ||
context.$sendCustomEvent("NETWORK_ERROR", { | ||
requestKind: fetchingError.requestKind, | ||
searchType: fetchingError.searchType, | ||
}) | ||
} else { | ||
context.$sentry.captureException(originalError, { | ||
extra: { fetchingError }, | ||
}) | ||
} | ||
} | ||
|
||
function createProcessFetchingError( | ||
context: Context | ||
): typeof normalizeFetchingError { | ||
function processFetchingError( | ||
...[originalError, ...args]: Parameters<typeof normalizeFetchingError> | ||
) { | ||
const fetchingError = normalizeFetchingError(originalError, ...args) | ||
recordError(context, originalError, fetchingError) | ||
return fetchingError | ||
} | ||
|
||
return processFetchingError | ||
} | ||
|
||
declare module "@nuxt/types" { | ||
interface Context { | ||
$processFetchingError: ReturnType<typeof createProcessFetchingError> | ||
} | ||
} | ||
|
||
const plugin: Plugin = async (context, inject) => { | ||
inject("processFetchingError", createProcessFetchingError(context)) | ||
} | ||
|
||
export default plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.