Skip to content

Commit 44560d6

Browse files
authored
cherry pick previous restore-health-check commits (#2577)
* cherry pick previous restore-health-check commits * updating shadcn dep
1 parent c9d6864 commit 44560d6

File tree

15 files changed

+1730
-61
lines changed

15 files changed

+1730
-61
lines changed

.vscode/mcp.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"type": "stdio",
55
"command": "npx",
66
"args": ["playwright", "run-test-mcp-server", "--headless"]
7+
},
8+
"shadcn": {
9+
"command": "npx",
10+
"args": ["shadcn@3.7.0", "mcp"]
711
}
812
},
913
"inputs": []

extension/components.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "default",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.ts",
8+
"css": "src/styles/globals.css",
9+
"baseColor": "slate"
10+
},
11+
"aliases": {
12+
"@/components": "./src/components",
13+
"@/lib/utils": "./src/lib/utils"
14+
}
15+
}

extension/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"sass-loader": "16.0.5",
8080
"semver": "7.7.1",
8181
"ses": "1.12.0",
82+
"sonner": "2.0.7",
8283
"soroswap-router-sdk": "1.4.6",
8384
"stellar-hd-wallet": "1.0.2",
8485
"stellar-sdk": "npm:@stellar/stellar-sdk@14.4.3",
@@ -105,6 +106,7 @@
105106
"postcss": "8.5.6",
106107
"postcss-loader": "8.2.0",
107108
"process": "0.11.10",
109+
"shadcn": "3.8.4",
108110
"speed-measure-webpack-plugin": "1.5.0",
109111
"stream-browserify": "3.0.0",
110112
"stream-http": "3.2.0",
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { getIsRpcHealthy } from "../account";
2+
import { NETWORK_ID } from "constants/localStorageTypes";
3+
import { DEFAULT_NETWORKS } from "@shared/constants/stellar";
4+
import type { DataStorageAccess } from "background/helpers/dataStorageAccess";
5+
6+
jest.mock("@shared/helpers/stellar", () => {
7+
const actual = jest.requireActual("@shared/helpers/stellar");
8+
return {
9+
...actual,
10+
isCustomNetwork: jest.fn(),
11+
};
12+
});
13+
14+
jest.mock("@sentry/browser", () => ({
15+
captureException: jest.fn(),
16+
}));
17+
18+
describe("getIsRpcHealthy", () => {
19+
const mockFetch = jest.fn();
20+
const mockIsCustomNetwork = jest.requireMock("@shared/helpers/stellar")
21+
.isCustomNetwork as jest.Mock;
22+
const mockCaptureException = jest.requireMock("@sentry/browser")
23+
.captureException as jest.Mock;
24+
25+
const localStore: DataStorageAccess = {
26+
getItem: jest.fn(),
27+
setItem: jest.fn(),
28+
removeItem: jest.fn(),
29+
clear: jest.fn(),
30+
} as unknown as DataStorageAccess;
31+
32+
beforeEach(() => {
33+
jest.resetAllMocks();
34+
(global as any).fetch = mockFetch;
35+
(localStore.getItem as jest.Mock).mockImplementation(async (key) => {
36+
if (key === NETWORK_ID) {
37+
return {
38+
...DEFAULT_NETWORKS[1],
39+
network: "testnet",
40+
networkName: "Testnet",
41+
};
42+
}
43+
return undefined;
44+
});
45+
});
46+
47+
it("returns true without calling fetch for custom networks", async () => {
48+
mockIsCustomNetwork.mockReturnValue(true);
49+
50+
const result = await getIsRpcHealthy(localStore);
51+
52+
expect(result).toBe(true);
53+
expect(mockFetch).not.toHaveBeenCalled();
54+
});
55+
56+
it("returns true when rpc health is healthy", async () => {
57+
mockIsCustomNetwork.mockReturnValue(false);
58+
mockFetch.mockResolvedValue({
59+
ok: true,
60+
json: async () => ({ status: "healthy" }),
61+
});
62+
63+
const result = await getIsRpcHealthy(localStore);
64+
65+
expect(mockFetch).toHaveBeenCalledWith(
66+
"http://localhost:3003/api/v1/rpc-health?network=testnet",
67+
);
68+
expect(result).toBe(true);
69+
expect(mockCaptureException).not.toHaveBeenCalled();
70+
});
71+
72+
it("returns false and captures when rpc health is unhealthy", async () => {
73+
mockIsCustomNetwork.mockReturnValue(false);
74+
mockFetch.mockResolvedValue({
75+
ok: true,
76+
json: async () => ({ status: "unhealthy" }),
77+
});
78+
79+
const result = await getIsRpcHealthy(localStore);
80+
81+
expect(result).toBe(false);
82+
expect(mockCaptureException).toHaveBeenCalledWith(
83+
"Soroban RPC is not healthy - unhealthy",
84+
);
85+
});
86+
});

extension/src/background/helpers/account.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { isCustomNetwork } from "@shared/helpers/stellar";
2121
import { decodeString, encodeObject } from "helpers/urls";
2222
import { isMainnet, isTestnet, isFuturenet } from "helpers/stellar";
2323
import { DataStorageAccess } from "background/helpers/dataStorageAccess";
24-
import { INDEXER_URL } from "@shared/constants/mercury";
24+
import { INDEXER_URL, INDEXER_V2_URL } from "@shared/constants/mercury";
2525
import { captureException } from "@sentry/browser";
2626

2727
export const getKeyIdList = async ({
@@ -297,21 +297,26 @@ export const getIsHideDustEnabled = async ({
297297
return isHideDustEnabled;
298298
};
299299

300-
export const getIsRpcHealthy = async (networkDetails: NetworkDetails) => {
300+
export const getIsRpcHealthy = async (localStore: DataStorageAccess) => {
301301
let rpcHealth = { status: "" };
302+
303+
const networkDetails = await getNetworkDetails({ localStore });
304+
302305
if (isCustomNetwork(networkDetails)) {
303306
// TODO: use server.getHealth method to get accurate result for standalone network
304307
rpcHealth = { status: "healthy" };
305308
} else {
306309
try {
307310
const res = await fetch(
308-
`${INDEXER_URL}/rpc-health?network=${networkDetails.network}`,
311+
`${INDEXER_V2_URL}/rpc-health?network=${networkDetails.network}`,
309312
);
310313

311314
if (!res.ok) {
312315
captureException(`Failed to load rpc health for Soroban`);
316+
rpcHealth = { status: "unhealthy" };
317+
} else {
318+
rpcHealth = await res.json();
313319
}
314-
rpcHealth = await res.json();
315320
} catch (e) {
316321
captureException(
317322
`Failed to load rpc health for Soroban - ${JSON.stringify(e)}`,

extension/src/background/messageListener/handlers/loadBackendSettings.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import {
22
getFeatureFlags,
33
getUserNotification,
4+
getIsRpcHealthy,
45
} from "background/helpers/account";
6+
import { DataStorageAccess } from "background/helpers/dataStorageAccess";
57

6-
export const loadBackendSettings = async () => {
8+
export const loadBackendSettings = async ({
9+
localStore,
10+
}: {
11+
localStore: DataStorageAccess;
12+
}) => {
713
const featureFlags = await getFeatureFlags();
8-
const isRpcHealthy = true;
14+
const isRpcHealthy = await getIsRpcHealthy(localStore);
915
const userNotification = await getUserNotification();
1016

1117
return {

extension/src/background/messageListener/popupMessageListener.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ export const popupMessageListener = (
368368
});
369369
}
370370
case SERVICE_TYPES.LOAD_BACKEND_SETTINGS: {
371-
return loadBackendSettings();
371+
return loadBackendSettings({ localStore });
372372
}
373373
case SERVICE_TYPES.GET_CACHED_ASSET_ICON_LIST: {
374374
return getCachedAssetIconList({

extension/src/popup/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Provider } from "react-redux";
55

66
import { metricsMiddleware } from "helpers/metrics";
77
import { activePublicKeyMiddleware } from "helpers/activePublicKeyMiddleware";
8+
import { Toaster } from "popup/basics/shadcn/Toast";
89

910
import { reducer as auth } from "popup/ducks/accountServices";
1011
import { reducer as settings } from "popup/ducks/settings";
@@ -42,6 +43,7 @@ export const App = () => (
4243
<ErrorBoundary>
4344
<Provider store={store}>
4445
<AccountMismatch />
46+
<Toaster />
4547
<ErrorTracking />
4648
<Router />
4749
</Provider>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as React from "react";
2+
import { Toaster as SonnerToaster } from "sonner";
3+
import { Icon } from "@stellar/design-system";
4+
5+
import "./styles.scss";
6+
7+
/*
8+
Toast component from sonner (https://sonner.emilkowal.ski/)
9+
Used for displaying toast notifications in the application.
10+
*/
11+
12+
function Toaster({ ...props }: React.ComponentProps<typeof SonnerToaster>) {
13+
return (
14+
<SonnerToaster
15+
data-testid="shadcn-toast"
16+
className="Toast"
17+
theme="dark"
18+
position="top-center"
19+
icons={{
20+
info: <Icon.InfoCircle />,
21+
}}
22+
{...props}
23+
/>
24+
);
25+
}
26+
27+
export { Toaster };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.Toast {
2+
li:before {
3+
content: none !important;
4+
}
5+
}

0 commit comments

Comments
 (0)