Skip to content

Commit 9f3cffd

Browse files
Omnicpiery-yozu
andauthored
Add client parameter definition on Diagnostics App (#357)
Co-authored-by: Ry Anderson <[email protected]>
1 parent edaf0e3 commit 9f3cffd

File tree

5 files changed

+143
-4
lines changed

5 files changed

+143
-4
lines changed

tools/diagnostics-app/src/app/router.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import ViewsLayout from './views/layout';
55
import SQLConsolePage from './views/sql-console';
66
import SyncDiagnosticsPage from './views/sync-diagnostics';
77
import SchemaPage from './views/schema';
8+
import ClientParamsPage from './views/client-params';
89

910
export const LOGIN_ROUTE = '/login';
1011
export const SQL_CONSOLE_ROUTE = '/sql-console';
1112
export const SYNC_DIAGNOSTICS_ROUTE = '/sync-diagnostics';
1213
export const SCHEMA_ROUTE = '/schema';
14+
export const CLIENT_PARAMETERS_ROUTE = '/client-parameters';
1315

1416
/**
1517
* Navigate to this route after authentication
@@ -43,6 +45,10 @@ export const router = createBrowserRouter([
4345
{
4446
path: SCHEMA_ROUTE,
4547
element: <SchemaPage />
48+
},
49+
{
50+
path: CLIENT_PARAMETERS_ROUTE,
51+
element: <ClientParamsPage />
4652
}
4753
]
4854
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { NavigationPage } from '@/components/navigation/NavigationPage';
2+
import { getParams, setParams as setParamsGlobal } from '@/library/powersync/ConnectionManager';
3+
import { Box, Button, Grid, IconButton, styled, TextField } from '@mui/material';
4+
import { FormEvent, useState } from 'react';
5+
import DeleteIcon from '@mui/icons-material/Delete';
6+
import AddIcon from '@mui/icons-material/Add';
7+
8+
const jsonToObjectArray = (json: Object) => {
9+
const entrySet = Object.entries(json);
10+
return entrySet.map(([key, value]) => ({
11+
key,
12+
value
13+
}));
14+
};
15+
16+
function ClientParamsPage() {
17+
const [params, setParams] = useState(jsonToObjectArray(getParams()));
18+
19+
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
20+
e.preventDefault();
21+
e.stopPropagation();
22+
23+
const newParams = params.reduce((curr, item) => ({ ...curr, [`${item.key}`]: item.value }), {});
24+
setParamsGlobal(newParams);
25+
};
26+
27+
const replace = (idx: number, val: any) => setParams((a) => a.map((entity, i) => (i === idx ? val : entity)));
28+
29+
const removeIdx = (idx: number) =>
30+
setParams((a) => a.map((entity, i) => i !== idx && entity).filter((entity) => entity !== false));
31+
32+
const addRow = () => {
33+
setParams((a) => [...a, { key: '', value: '' }]);
34+
};
35+
36+
const changeValue = (idx: number, value: string, currKey: string) => {
37+
replace(idx, { key: currKey, value });
38+
};
39+
40+
const changeKey = (idx: number, key: string, currValue: unknown) => {
41+
replace(idx, { key, value: currValue });
42+
};
43+
44+
return (
45+
<NavigationPage title="Client Parameters">
46+
<S.MainContainer>
47+
<form onSubmit={onSubmit}>
48+
{params.map(({ key, value }, idx) => (
49+
<S.CenteredGrid container>
50+
<S.CenteredGrid item xs={12} md={10}>
51+
<TextField
52+
label="Key"
53+
value={key}
54+
sx={{ margin: '10px' }}
55+
onChange={(v) => changeKey(idx, v.target.value, value)}
56+
/>
57+
<TextField
58+
label="Value"
59+
value={value}
60+
sx={{ margin: '10px' }}
61+
onChange={(v) => changeValue(idx, v.target.value, key)}
62+
/>
63+
64+
<IconButton sx={{ margin: '10px' }} color="error" onClick={() => removeIdx(idx)}>
65+
<DeleteIcon />
66+
</IconButton>
67+
</S.CenteredGrid>
68+
</S.CenteredGrid>
69+
))}
70+
<S.CenteredGrid container>
71+
<IconButton sx={{ margin: '10px' }} onClick={addRow}>
72+
<AddIcon />
73+
</IconButton>
74+
</S.CenteredGrid>
75+
<Button type="submit" sx={{ margin: '10px' }} variant="contained">
76+
Submit
77+
</Button>
78+
</form>
79+
</S.MainContainer>
80+
</NavigationPage>
81+
);
82+
}
83+
84+
namespace S {
85+
export const MainContainer = styled(Box)`
86+
padding: 20px;
87+
`;
88+
89+
export const CenteredGrid = styled(Grid)`
90+
display: flex;
91+
justify-content: center;
92+
align-items: center;
93+
`;
94+
}
95+
96+
export default ClientParamsPage;

tools/diagnostics-app/src/app/views/layout.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import StorageIcon from '@mui/icons-material/Storage';
77
import TableChartIcon from '@mui/icons-material/TableChart';
88
import TerminalIcon from '@mui/icons-material/Terminal';
99
import WifiIcon from '@mui/icons-material/Wifi';
10+
import UserIcon from '@mui/icons-material/VerifiedUser';
1011

1112
import {
1213
AppBar,
@@ -25,7 +26,13 @@ import {
2526
} from '@mui/material';
2627
import React from 'react';
2728

28-
import { LOGIN_ROUTE, SCHEMA_ROUTE, SQL_CONSOLE_ROUTE, SYNC_DIAGNOSTICS_ROUTE } from '@/app/router';
29+
import {
30+
CLIENT_PARAMETERS_ROUTE,
31+
LOGIN_ROUTE,
32+
SCHEMA_ROUTE,
33+
SQL_CONSOLE_ROUTE,
34+
SYNC_DIAGNOSTICS_ROUTE
35+
} from '@/app/router';
2936
import { useNavigationPanel } from '@/components/navigation/NavigationPanelContext';
3037
import { signOut, sync, syncErrorTracker } from '@/library/powersync/ConnectionManager';
3138
import { usePowerSync } from '@powersync/react';
@@ -74,6 +81,11 @@ export default function ViewsLayout({ children }: { children: React.ReactNode })
7481
title: 'SQL Console',
7582
icon: () => <TerminalIcon />
7683
},
84+
{
85+
path: CLIENT_PARAMETERS_ROUTE,
86+
title: 'Client Parameters',
87+
icon: () => <UserIcon />
88+
},
7789
{
7890
path: LOGIN_ROUTE,
7991
title: 'Sign Out',
@@ -208,4 +220,4 @@ namespace S {
208220
object-fit: contain;
209221
padding: 20px;
210222
`;
211-
}
223+
}

tools/diagnostics-app/src/library/powersync/ConnectionManager.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,19 @@ import Logger from 'js-logger';
1010
import { DynamicSchemaManager } from './DynamicSchemaManager';
1111
import { RecordingStorageAdapter } from './RecordingStorageAdapter';
1212
import { TokenConnector } from './TokenConnector';
13+
import { safeParse } from '../safeParse/safeParse';
1314

1415
Logger.useDefaults();
1516
Logger.setLevel(Logger.DEBUG);
1617

18+
export const PARAMS_STORE = 'currentParams';
19+
20+
export const getParams = () => {
21+
const stringifiedParams = localStorage.getItem(PARAMS_STORE);
22+
const params = safeParse(stringifiedParams);
23+
return params;
24+
}
25+
1726
export const schemaManager = new DynamicSchemaManager();
1827

1928
export const db = new PowerSyncDatabase({
@@ -74,7 +83,8 @@ if (connector.hasCredentials()) {
7483
}
7584

7685
export async function connect() {
77-
await sync.connect();
86+
const params = getParams();
87+
await sync.connect({ params });
7888
if (!sync.syncStatus.connected) {
7989
// Disconnect but don't wait for it
8090
sync.disconnect();
@@ -90,7 +100,8 @@ export async function clearData() {
90100
await schemaManager.clear();
91101
await schemaManager.refreshSchema(db.database);
92102
if (connector.hasCredentials()) {
93-
await sync.connect();
103+
const params = getParams();
104+
await sync.connect({ params });
94105
}
95106
}
96107

@@ -103,4 +114,10 @@ export async function signOut() {
103114
await db.disconnectAndClear();
104115
}
105116

117+
export const setParams = (p: object) => {
118+
const stringified = JSON.stringify(p);
119+
localStorage.setItem(PARAMS_STORE, stringified);
120+
connect();
121+
};
122+
106123
(window as any).db = db;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const safeParse = (jsonString?: string | null) => {
2+
try {
3+
if (!jsonString) return {};
4+
return JSON.parse(jsonString);
5+
} catch (error) {
6+
return {};
7+
}
8+
};

0 commit comments

Comments
 (0)