Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/components/UI/Perps/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ const PerpsScreenStack = () => {
component={PayWithModal}
options={{
headerShown: false,
...clearNativeStackNavigatorOptions,
...transparentModalScreenOptions,
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,23 @@ describe('useTopTraders', () => {
expect(result.current.error).toBe('Network error');
});

it('logs the full error object via Logger.error with enriched extras', () => {
it('logs the full error object via Logger.error with feature:social tags', () => {
const err = new Error('Network error');
mockUseQuery.mockReturnValue(makeQueryResult({ error: err }));
renderHook(() => useTopTraders());
expect(Logger.error).toHaveBeenCalledWith(
err,
expect.objectContaining({
message: 'useTopTraders: leaderboard fetch failed',
endpoint: 'leaderboard',
errorCategory: expect.any(String),
tags: expect.objectContaining({
feature: 'social',
surface: 'top_traders',
operation: 'fetch_leaderboard',
endpoint: 'leaderboard',
}),
extras: expect.objectContaining({
message: 'Top traders leaderboard fetch failed at useTopTraders',
endpoint: 'leaderboard',
}),
}),
);
});
Expand Down Expand Up @@ -366,9 +373,15 @@ describe('useTopTraders', () => {
expect(Logger.error).toHaveBeenCalledWith(
err,
expect.objectContaining({
message: 'useTopTraders: refresh failed',
endpoint: 'leaderboard',
errorCategory: expect.any(String),
tags: expect.objectContaining({
feature: 'social',
surface: 'top_traders',
operation: 'refresh',
}),
extras: expect.objectContaining({
message: 'Top traders leaderboard refresh failed at useTopTraders',
endpoint: 'leaderboard',
}),
}),
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { useMemo, useCallback, useEffect } from 'react';
import { useMemo, useCallback } from 'react';
import { useSelector } from 'react-redux';
import { useQuery } from '@metamask/react-data-query';
import type {
LeaderboardResponse,
FetchLeaderboardOptions,
} from '@metamask/social-controllers';
import Logger from '../../../../../../util/Logger';
import {
useFollowToggleMany,
type FollowToggleAnalyticsContext,
} from '../../../../../hooks/useFollowToggle';
import { selectIsUnlocked } from '../../../../../../selectors/keyringController';
import type { TopTrader } from '../types';
import {
addSocialBreadcrumb,
buildSocialErrorExtras,
categoriseSocialError,
extractHttpStatus,
formatSocialQueryErrorMessage,
reportSocialServiceFailure,
useLogSocialQueryError,
} from '../../../../../../util/social/socialServiceTelemetry';

export interface UseTopTradersResult {
Expand Down Expand Up @@ -56,6 +54,20 @@ export const useTopTraders = (
enabled: (options?.enabled ?? true) && isUnlocked,
});

const leaderboardQueryParams = useMemo(
() => ({ limit: options?.limit ?? 0 }),
[options?.limit],
);

useLogSocialQueryError(error, {
surface: 'top_traders',
operation: 'fetch_leaderboard',
extraMessage: 'Top traders leaderboard fetch failed',
source: 'useTopTraders',
endpoint: 'leaderboard',
queryParams: leaderboardQueryParams,
});

const { isFollowing, toggleFollow } = useFollowToggleMany();

const traders: TopTrader[] = useMemo(() => {
Expand All @@ -81,45 +93,27 @@ export const useTopTraders = (
try {
await refetch();
} catch (err) {
Logger.error(
err as Error,
buildSocialErrorExtras({
legacyMessage: 'useTopTraders: refresh failed',
reportSocialServiceFailure(
err,
{
surface: 'top_traders',
operation: 'refresh',
extraMessage: 'Top traders leaderboard refresh failed',
source: 'useTopTraders',
endpoint: 'leaderboard',
error: err,
queryParams: { limit: options?.limit ?? 0 },
}),
queryParams: leaderboardQueryParams,
},
{ breadcrumb: false },
);
throw err;
}
}, [refetch, options?.limit]);

useEffect(() => {
if (error) {
Logger.error(
error as Error,
buildSocialErrorExtras({
legacyMessage: 'useTopTraders: leaderboard fetch failed',
endpoint: 'leaderboard',
error,
queryParams: { limit: options?.limit ?? 0 },
}),
);
addSocialBreadcrumb({
endpoint: 'leaderboard',
errorCategory: categoriseSocialError(error),
httpStatus: extractHttpStatus(error),
queryParams: { limit: options?.limit ?? 0 },
});
}
}, [error, options?.limit]);
}, [refetch, leaderboardQueryParams]);

return {
traders,
isLoading,
isFetching,
error:
error instanceof Error ? error.message : error ? String(error) : null,
error: formatSocialQueryErrorMessage(error),
refresh,
toggleFollow,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,23 @@ describe('useFollowedTraders', () => {
expect(result.current.error).toBe('raw error');
});

it('logs query errors with enriched extras', () => {
it('logs query errors with feature:social tags', () => {
const error = new Error('fetch failed');
mockUseQuery.mockReturnValue(makeQueryResult({ error }));
renderHook(() => useFollowedTraders());
expect(Logger.error).toHaveBeenCalledWith(
error,
expect.objectContaining({
message: 'useFollowedTraders: following fetch failed',
endpoint: 'following',
errorCategory: expect.any(String),
tags: expect.objectContaining({
feature: 'social',
surface: 'followed_traders',
operation: 'fetch_following',
endpoint: 'following',
}),
extras: expect.objectContaining({
message: 'Followed traders fetch failed at useFollowedTraders',
endpoint: 'following',
}),
}),
);
});
Expand Down Expand Up @@ -191,9 +198,16 @@ describe('useFollowedTraders', () => {
expect(Logger.error).toHaveBeenCalledWith(
error,
expect.objectContaining({
message: 'useFollowedTraders: refresh failed',
endpoint: 'following',
errorCategory: expect.any(String),
tags: expect.objectContaining({
feature: 'social',
surface: 'followed_traders',
operation: 'refresh',
endpoint: 'following',
}),
extras: expect.objectContaining({
message: 'Followed traders refresh failed at useFollowedTraders',
endpoint: 'following',
}),
}),
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { useQuery } from '@metamask/react-data-query';
import type { FollowingResponse } from '@metamask/social-controllers';
import Logger from '../../../../../util/Logger';
import {
addSocialBreadcrumb,
buildSocialErrorExtras,
categoriseSocialError,
extractHttpStatus,
formatSocialQueryErrorMessage,
reportSocialServiceFailure,
useLogSocialQueryError,
} from '../../../../../util/social/socialServiceTelemetry';

export interface FollowedTrader {
Expand Down Expand Up @@ -37,6 +35,8 @@ export interface UseFollowedTradersResult {

const EMPTY_TRADERS: FollowedTrader[] = [];

const FOLLOWED_TRADERS_SOURCE = 'useFollowedTraders';

/**
* Fetches the list of traders the current user follows, with their
* full profile summary (name + avatar).
Expand All @@ -58,6 +58,14 @@ export const useFollowedTraders = (
enabled,
});

useLogSocialQueryError(error, {
surface: 'followed_traders',
operation: 'fetch_following',
extraMessage: 'Followed traders fetch failed',
source: FOLLOWED_TRADERS_SOURCE,
endpoint: 'following',
});

const traders = useMemo<FollowedTrader[]>(() => {
if (!data?.following) {
return EMPTY_TRADERS;
Expand All @@ -73,41 +81,25 @@ export const useFollowedTraders = (
try {
await refetch();
} catch (err) {
Logger.error(
err as Error,
buildSocialErrorExtras({
legacyMessage: 'useFollowedTraders: refresh failed',
reportSocialServiceFailure(
err,
{
surface: 'followed_traders',
operation: 'refresh',
extraMessage: 'Followed traders refresh failed',
source: FOLLOWED_TRADERS_SOURCE,
endpoint: 'following',
error: err,
}),
},
{ breadcrumb: false },
);
throw err;
}
}, [refetch]);

useEffect(() => {
if (error) {
Logger.error(
error as Error,
buildSocialErrorExtras({
legacyMessage: 'useFollowedTraders: following fetch failed',
endpoint: 'following',
error,
}),
);
addSocialBreadcrumb({
endpoint: 'following',
errorCategory: categoriseSocialError(error),
httpStatus: extractHttpStatus(error),
});
}
}, [error]);

return {
traders,
isLoading: enabled && isLoading,
error:
error instanceof Error ? error.message : error ? String(error) : null,
error: formatSocialQueryErrorMessage(error),
refresh,
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,16 @@ describe('TopTradersView', () => {

expect(Logger.error).toHaveBeenCalledWith(
refreshError,
'TopTradersView: pull-to-refresh failed',
expect.objectContaining({
tags: expect.objectContaining({
feature: 'social',
surface: 'top_traders',
operation: 'pull_to_refresh',
}),
extras: expect.objectContaining({
message: 'Top traders pull-to-refresh failed at TopTradersView',
}),
}),
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
import { selectSocialLeaderboardEnabled } from '../../../../selectors/featureFlagController/socialLeaderboard';
import { fontStyles } from '../../../../styles/common';
import Logger from '../../../../util/Logger';
import { buildSocialLoggerErrorOptions } from '../../../../util/social/socialServiceTelemetry';
import { useTheme } from '../../../../util/theme';
import { useNotificationStoragePreferences } from '../../Settings/NotificationsSettings/hooks/useNotificationStoragePreferences';
import {
Expand Down Expand Up @@ -266,7 +267,16 @@ const TopTradersView = () => {
);
await Promise.all([refresh(), minDuration]);
} catch (err) {
Logger.error(err as Error, 'TopTradersView: pull-to-refresh failed');
Logger.error(
err as Error,
buildSocialLoggerErrorOptions({
surface: 'top_traders',
operation: 'pull_to_refresh',
extraMessage: 'Top traders pull-to-refresh failed',
source: 'TopTradersView',
error: err,
}),
);
} finally {
setRefreshing(false);
}
Expand Down
Loading
Loading