Commit 813c4dd7 authored by tom's avatar tom

add meta data to sentry errors

parent c9ea7805
...@@ -41,7 +41,7 @@ import type ArrayElement from 'types/utils/ArrayElement'; ...@@ -41,7 +41,7 @@ import type ArrayElement from 'types/utils/ArrayElement';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
export interface ApiResource { export interface ApiResource {
path: string; path: ResourcePath;
endpoint?: string; endpoint?: string;
basePath?: string; basePath?: string;
pathParams?: Array<string>; pathParams?: Array<string>;
...@@ -364,6 +364,11 @@ export const RESOURCES = { ...@@ -364,6 +364,11 @@ export const RESOURCES = {
export type ResourceName = keyof typeof RESOURCES; export type ResourceName = keyof typeof RESOURCES;
type ResourcePathMap = {
[K in ResourceName]: typeof RESOURCES[K]['path']
}
export type ResourcePath = ResourcePathMap[keyof ResourcePathMap]
export type ResourceFiltersKey<R extends ResourceName> = typeof RESOURCES[R] extends {filterFields: Array<unknown>} ? export type ResourceFiltersKey<R extends ResourceName> = typeof RESOURCES[R] extends {filterFields: Array<unknown>} ?
ArrayElement<typeof RESOURCES[R]['filterFields']> : ArrayElement<typeof RESOURCES[R]['filterFields']> :
never; never;
......
...@@ -23,7 +23,9 @@ export default function useApiFetch() { ...@@ -23,7 +23,9 @@ export default function useApiFetch() {
) => { ) => {
const resource: ApiResource = RESOURCES[resourceName]; const resource: ApiResource = RESOURCES[resourceName];
const url = buildUrl(resourceName, pathParams, queryParams); const url = buildUrl(resourceName, pathParams, queryParams);
return fetch<SuccessType, ErrorType>(url, { return fetch<SuccessType, ErrorType>(
url,
{
credentials: 'include', credentials: 'include',
...(resource.endpoint && appConfig.host === 'localhost' ? { ...(resource.endpoint && appConfig.host === 'localhost' ? {
headers: { headers: {
...@@ -31,6 +33,10 @@ export default function useApiFetch() { ...@@ -31,6 +33,10 @@ export default function useApiFetch() {
}, },
} : {}), } : {}),
...fetchParams, ...fetchParams,
}); },
{
resource: resource.path,
},
);
}, [ fetch ]); }, [ fetch ]);
} }
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { CsrfData } from 'types/client/account'; import type { CsrfData } from 'types/client/account';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError, ResourcePath } from 'lib/api/resources';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
export interface Params { export interface Params {
...@@ -15,11 +15,15 @@ export interface Params { ...@@ -15,11 +15,15 @@ export interface Params {
credentials?: RequestCredentials; credentials?: RequestCredentials;
} }
interface Meta {
resource?: ResourcePath;
}
export default function useFetch() { export default function useFetch() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { token } = queryClient.getQueryData<CsrfData>(getResourceKey('csrf')) || {}; const { token } = queryClient.getQueryData<CsrfData>(getResourceKey('csrf')) || {};
return React.useCallback(<Success, Error>(path: string, params?: Params): Promise<Success | ResourceError<Error>> => { return React.useCallback(<Success, Error>(path: string, params?: Params, meta?: Meta): Promise<Success | ResourceError<Error>> => {
const _body = params?.body; const _body = params?.body;
const isFormData = _body instanceof FormData; const isFormData = _body instanceof FormData;
const isBodyAllowed = params?.method && ![ 'GET', 'HEAD' ].includes(params.method); const isBodyAllowed = params?.method && ![ 'GET', 'HEAD' ].includes(params.method);
...@@ -51,7 +55,7 @@ export default function useFetch() { ...@@ -51,7 +55,7 @@ export default function useFetch() {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
}; };
Sentry.captureException(new Error('Client fetch failed'), { extra: error, tags: { source: 'fetch' } }); Sentry.captureException(new Error('Client fetch failed'), { extra: { ...error, ...meta }, tags: { source: 'api_fetch' } });
return response.json().then( return response.json().then(
(jsonError) => Promise.reject({ (jsonError) => Promise.reject({
......
import * as Sentry from '@sentry/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import buildUrl from 'lib/api/buildUrl'; import buildUrl from 'lib/api/buildUrl';
...@@ -14,7 +15,13 @@ export default function useGetCsrfToken() { ...@@ -14,7 +15,13 @@ export default function useGetCsrfToken() {
const url = buildUrl('csrf'); const url = buildUrl('csrf');
const apiResponse = await fetch(url, { credentials: 'include' }); const apiResponse = await fetch(url, { credentials: 'include' });
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
return csrfFromHeader ? { token: csrfFromHeader } : undefined;
if (!csrfFromHeader) {
Sentry.captureException(new Error('Unable to get csrf token'), { tags: { source: 'csrf_token' } });
return;
}
return { token: csrfFromHeader };
} }
return nodeApiFetch('/node-api/csrf'); return nodeApiFetch('/node-api/csrf');
......
...@@ -19,7 +19,7 @@ export default function useRedirectForInvalidAuthToken() { ...@@ -19,7 +19,7 @@ export default function useRedirectForInvalidAuthToken() {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN); const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
if (apiToken) { if (apiToken) {
Sentry.captureException(new Error('Invalid api token'), { tags: { source: 'fetch' } }); Sentry.captureException(new Error('Invalid api token'), { tags: { source: 'invalid_api_token' } });
window.location.assign(loginUrl); window.location.assign(loginUrl);
} }
} }
......
...@@ -26,7 +26,7 @@ const AddressQrCode = ({ hash, className }: Props) => { ...@@ -26,7 +26,7 @@ const AddressQrCode = ({ hash, className }: Props) => {
QRCode.toString(hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => { QRCode.toString(hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => {
if (error) { if (error) {
setError('We were unable to generate QR code.'); setError('We were unable to generate QR code.');
Sentry.captureException(error, { tags: { source: 'QR code' } }); Sentry.captureException(error, { tags: { source: 'qr_code' } });
return; return;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment