Skip to content

Commit b61d04a

Browse files
authored
Merge pull request #10151 from Brian-McM/bm-pick-ronans-stuff
[PICK release-v3.30] whisker calico-cloud link | runtime config
2 parents 296463b + 6f2e0b6 commit b61d04a

File tree

15 files changed

+151
-27
lines changed

15 files changed

+151
-27
lines changed

whisker/.env

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
APP_API_URL="http://localhost:3002"
1+
APP_API_URL="http://localhost:3002"
2+
APP_CONFIG_PATH="/config.json"

whisker/.env.production

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
APP_API_URL="/whisker-backend"
2+
APP_CONFIG_PATH="/config/config.json"

whisker/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Run `yarn`
1212

1313
Run `yarn start`
1414

15-
Go to `http://localhost:3000`
15+
Go to `http://localhost:3000` using chrome with disabled web security
1616

1717
## Build
1818

whisker/public/config.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"config": {
3+
"cluster_id": "",
4+
"cluster_type": "",
5+
"calico_version": "",
6+
"notifications": ""
7+
}
8+
}

whisker/rsbuild.config.ts

+9-19
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
1-
import { defineConfig, loadEnv, rspack } from '@rsbuild/core';
1+
import { defineConfig, loadEnv } from '@rsbuild/core';
22
import { pluginReact } from '@rsbuild/plugin-react';
33
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
44

55
const { publicVars } = loadEnv({ prefixes: ['APP_'] });
66

77
export default defineConfig({
8-
tools: {
9-
rspack: {
10-
plugins: [
11-
new rspack.CopyRspackPlugin({
12-
patterns: [
13-
{
14-
from: 'public/favicon.ico',
15-
to: 'public/favicon.ico',
16-
},
17-
{
18-
from: 'config/config.json',
19-
to: 'config/config.json',
20-
},
21-
],
22-
}),
23-
],
24-
},
25-
},
268
plugins: [pluginReact(), pluginTypeCheck()],
279
server: {
2810
port: 3000,
@@ -36,4 +18,12 @@ export default defineConfig({
3618
},
3719
define: publicVars,
3820
},
21+
output: {
22+
copy: [
23+
{
24+
from: 'public/favicon.ico',
25+
to: 'public/favicon.ico',
26+
},
27+
],
28+
},
3929
});

whisker/src/App.tsx

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { AppLayout, ChakraProvider } from '@/components';
2+
import {
3+
AppErrorBoundary,
4+
FlowLogsErrorBoundary,
5+
} from '@/components/core/ErrorBoundary';
26
import { FlowLogsContainer } from '@/features/flowLogs/components';
37
import { FlowLogsPage } from '@/pages';
48
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -10,10 +14,7 @@ import {
1014
RouterProvider,
1115
createBrowserRouter,
1216
} from 'react-router-dom';
13-
import {
14-
AppErrorBoundary,
15-
FlowLogsErrorBoundary,
16-
} from '@/components/core/ErrorBoundary';
17+
import AppConfigProvider from '@/context/AppConfig';
1718

1819
const queryClient = new QueryClient({
1920
defaultOptions: {
@@ -59,7 +60,10 @@ const App: React.FC = () => {
5960
return (
6061
<ChakraProvider>
6162
<QueryClientProvider client={queryClient}>
62-
<RouterProvider router={router} />
63+
<AppConfigProvider>
64+
<RouterProvider router={router} />
65+
</AppConfigProvider>
66+
6367
<ReactQueryDevtools initialIsOpen={false} />
6468
</QueryClientProvider>
6569
</ChakraProvider>

whisker/src/__tests__/App.test.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ jest.mock('@/pages', () => ({
55
FlowLogsPage: () => 'Mock FlowLogsPage',
66
}));
77

8+
jest.mock('@/hooks', () => ({
9+
useClusterId: jest.fn().mockReturnValue('fake-cluster-id'),
10+
}));
11+
812
describe('<App />', () => {
913
it('should render the App component', () => {
1014
render(<App />);

whisker/src/api/index.ts

+11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import {
55
UseStreamOptions,
66
UseStreamResult,
77
} from '@/types/api';
8+
import { AppConfig } from '@/types/render';
89
import { createEventSource } from '@/utils';
10+
import { useQuery } from '@tanstack/react-query';
911
import React from 'react';
1012

1113
export const API_URL = process.env.APP_API_URL;
@@ -183,6 +185,15 @@ export const useStream = <S, R>({
183185
};
184186
};
185187

188+
export const useAppConfigQuery = () =>
189+
useQuery<AppConfig>({
190+
queryKey: ['config'],
191+
queryFn: () =>
192+
fetch(process.env.APP_CONFIG_PATH).then((response) =>
193+
response.json(),
194+
),
195+
});
196+
186197
export default {
187198
get,
188199
useStream,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { renderWithRouter, screen } from '@/test-utils/helper';
2+
import AppHeader from '..';
3+
4+
jest.mock('@/hooks', () => ({
5+
useClusterId: jest.fn().mockReturnValue('fake-cluster-id'),
6+
}));
7+
8+
describe('<AppHeader />', () => {
9+
it('should add the whisker id to the calico cloud link', () => {
10+
renderWithRouter(<AppHeader />);
11+
12+
expect(
13+
screen.getByTestId('app-header-calico-cloud-link'),
14+
).toHaveProperty(
15+
'href',
16+
expect.stringContaining(`whisker-id=fake-cluster-id`),
17+
);
18+
});
19+
});

whisker/src/components/layout/AppHeader/index.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { CalicoCatIcon, CalicoWhiskerIcon } from '@/icons';
22
import { Flex, Heading, LinkBox, LinkOverlay, Text } from '@chakra-ui/react';
33
import React from 'react';
44
import { appHeaderStyles } from './styles';
5+
import { useClusterId } from '@/hooks';
56

67
const AppHeader: React.FC = () => {
8+
const clusterId = useClusterId();
9+
710
return (
811
<Flex as='header' sx={appHeaderStyles}>
912
<Flex alignItems='center'>
@@ -18,11 +21,12 @@ const AppHeader: React.FC = () => {
1821
Calico Whisker is a simplified version of the
1922
</Text>
2023
<LinkOverlay
24+
data-testId='app-header-calico-cloud-link'
2125
fontSize='xs'
2226
fontWeight='bold'
2327
color='tigeraGoldMedium'
2428
isExternal
25-
href='https://calicocloud.io'
29+
href={`https://calicocloud.io?utm_source=whisker&utm_medium=header-link&utm_campaign=oss-ui&whisker-id=${clusterId}`}
2630
>
2731
Service Graph from Calico Cloud
2832
</LinkOverlay>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { render, screen } from '@/test-utils/helper';
2+
import AppConfigProvider from '..';
3+
import { useAppConfigQuery } from '@/api';
4+
import { useClusterId } from '@/hooks';
5+
6+
jest.mock('@/api', () => ({ useAppConfigQuery: jest.fn() }));
7+
8+
const TestComponent = () => {
9+
const clusterId = useClusterId();
10+
11+
return <div>ClusterId = {clusterId}</div>;
12+
};
13+
14+
describe('<AppConfig />', () => {
15+
it('should pass context to children', () => {
16+
const clusterId = 'my-cluster-id';
17+
jest.mocked(useAppConfigQuery).mockReturnValue({
18+
data: undefined,
19+
} as any);
20+
21+
const { rerender } = render(
22+
<AppConfigProvider>
23+
<TestComponent />
24+
</AppConfigProvider>,
25+
);
26+
27+
expect(screen.getByText(/ClusterId =/));
28+
29+
jest.mocked(useAppConfigQuery).mockReturnValue({
30+
data: {
31+
config: {
32+
cluster_id: clusterId,
33+
cluster_type: '',
34+
calico_version: '',
35+
notifications: '',
36+
},
37+
},
38+
} as any);
39+
40+
rerender(
41+
<AppConfigProvider>
42+
<TestComponent />
43+
</AppConfigProvider>,
44+
);
45+
46+
expect(screen.getByText(`ClusterId = ${clusterId}`));
47+
});
48+
});
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useAppConfigQuery } from '@/api';
2+
import { AppConfig } from '@/types/render';
3+
import React from 'react';
4+
5+
export const AppConfigContext = React.createContext<AppConfig | undefined>(
6+
undefined,
7+
);
8+
9+
export const useAppConfig = () => React.useContext(AppConfigContext);
10+
11+
const AppConfigProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
12+
const { data } = useAppConfigQuery();
13+
14+
return (
15+
<AppConfigContext.Provider value={data}>
16+
{children}
17+
</AppConfigContext.Provider>
18+
);
19+
};
20+
21+
export default AppConfigProvider;

whisker/src/hooks/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { useSelectedListOmniFilters } from './omniFilters';
3+
import { useAppConfig } from '@/context/AppConfig';
34

45
const DEBOUNCE_TIME = 500;
56

@@ -27,4 +28,6 @@ export const useDebouncedCallback = () => {
2728
};
2829
};
2930

31+
export const useClusterId = () => useAppConfig()?.config.cluster_id;
32+
3033
export { useSelectedListOmniFilters };

whisker/src/types/env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
declare namespace NodeJS {
22
interface ProcessEnv {
33
APP_API_URL: string;
4+
APP_CONFIG_PATH: string;
45
}
56
}

whisker/src/types/render.ts

+9
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ export type FlowLog = Omit<ApiFlowLog, 'start_time' | 'end_time'> & {
77
start_time: Date;
88
end_time: Date;
99
};
10+
11+
export type AppConfig = {
12+
config: {
13+
cluster_id: string;
14+
cluster_type: string;
15+
calico_version: string;
16+
notifications: string;
17+
};
18+
};

0 commit comments

Comments
 (0)