Skip to content

Commit a04eefb

Browse files
authored
chore(base): Add comments and docstrings to initial react-query code MAAASENG-4252 (#5577)
- Added comments and docstrings to helper functions surrounding react-query implementation Resolves [MAAASENG-4252](https://warthogs.atlassian.net/browse/MAAASENG-4252)
1 parent ef18791 commit a04eefb

File tree

5 files changed

+61
-0
lines changed

5 files changed

+61
-0
lines changed

src/app/api/base.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@ type ApiEndpoint = typeof API_ENDPOINTS;
2121
export type ApiEndpointKey = keyof ApiEndpoint;
2222
type ApiUrl = `${typeof SERVICE_API}${ApiEndpoint[ApiEndpointKey]}`;
2323

24+
/**
25+
* Constructs a complete API URL from a given endpoint key.
26+
* @param endpoint The endpoint key
27+
* @returns A full API URL (string)
28+
*/
2429
export const getFullApiUrl = (endpoint: ApiEndpointKey): ApiUrl =>
2530
`${SERVICE_API}${API_ENDPOINTS[endpoint]}`;
2631

32+
/**
33+
* Runs a given fetch request with the appropriate headers and CSRF token.
34+
*
35+
* @param url The URL to fetch
36+
* @param options RequestInit options
37+
* @returns The JSON response from the fetch
38+
*/
2739
export const fetchWithAuth = async (url: string, options: RequestInit = {}) => {
2840
const csrftoken = getCookie("csrftoken");
2941
const headers = {

src/app/api/endpoints.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { fetchWithAuth, getFullApiUrl } from "@/app/api/base";
22
import type { Zone } from "@/app/store/zone/types";
33

4+
/**
5+
* Fetches a list of zones.
6+
*
7+
* @returns The list of zones
8+
*/
49
export const fetchZones = (): Promise<Zone[]> =>
510
fetchWithAuth(getFullApiUrl("zones"));

src/app/api/query-client.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { QueryClient } from "@tanstack/react-query";
22

3+
// Different query keys for different methods.
34
export const queryKeys = {
45
zones: {
56
list: ["zones"],
@@ -10,23 +11,28 @@ type QueryKeys = typeof queryKeys;
1011
type QueryKeyCategories = keyof QueryKeys;
1112
type QueryKeySubcategories<T extends QueryKeyCategories> = keyof QueryKeys[T];
1213

14+
// This basically exists to get us the query key as it appears in react query, i.e. a string in an array.
1315
export type QueryKey =
1416
QueryKeys[QueryKeyCategories][QueryKeySubcategories<QueryKeyCategories>];
1517

1618
// first element of the queryKeys array
19+
// Idk what Peter meant by the above comment, but QueryModel is basically QueryKey but not in an array from what I understand.
1720
export type QueryModel = QueryKey[number];
1821

22+
// 5 minutes feels rather long for default stale time.
1923
export const defaultQueryOptions = {
2024
staleTime: 5 * 60 * 1000, // 5 minutes
2125
cacheTime: 15 * 60 * 1000, // 15 minutes
2226
refetchOnWindowFocus: true,
2327
} as const;
2428

29+
// 0 is far too quick lol but at least we're not using this.
2530
export const realTimeQueryOptions = {
2631
staleTime: 0,
2732
cacheTime: 60 * 1000, // 1 minute
2833
} as const;
2934

35+
// This just creates a query client and provides the options specified above.
3036
export const createQueryClient = () =>
3137
new QueryClient({
3238
defaultOptions: {

src/app/api/query/base.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,32 @@ import statusSelectors from "@/app/store/status/selectors";
1111
import type { WebSocketEndpointModel } from "@/websocket-client";
1212
import { WebSocketMessageType } from "@/websocket-client";
1313

14+
/**
15+
* Provides a hook to subscribe to NOTIFY messages from the websocket.
16+
*
17+
* @returns An object with a subscribe function that takes a callback to run when a NOTIFY message is received.
18+
*/
1419
export const useWebSocket = () => {
1520
const websocketClient = useContext(WebSocketContext);
1621

1722
if (!websocketClient) {
1823
throw new Error("useWebSocket must be used within a WebSocketProvider");
1924
}
2025

26+
// Listen for NOTIFY messages and run a callback when received
2127
const subscribe = useCallback(
2228
(callback: (msg: any) => void) => {
2329
if (!websocketClient.rws) return;
2430

2531
const messageHandler = (messageEvent: MessageEvent) => {
2632
const data = JSON.parse(messageEvent.data);
33+
// if we get a NOTIFY, run the provided callback
2734
if (data.type === WebSocketMessageType.NOTIFY) callback(data);
2835
};
36+
// add an event listener for NOTIFY messages
2937
websocketClient.rws.addEventListener("message", messageHandler);
38+
39+
// this is a function to remove that event listener, it gets called in a cleanup effect down below.
3040
return () =>
3141
websocketClient.rws?.removeEventListener("message", messageHandler);
3242
},
@@ -40,6 +50,16 @@ const wsToQueryKeyMapping: Partial<Record<WebSocketEndpointModel, string>> = {
4050
zone: "zones",
4151
// Add more mappings as needed
4252
} as const;
53+
54+
/**
55+
* A function to run a query which invalidates the query cache when a
56+
* websocket message is received, or when the websocket reconnects.
57+
*
58+
* @param queryKey The query key to use
59+
* @param queryFn The query function to run
60+
* @param options Options for useQuery
61+
* @returns The return value of useQuery
62+
*/
4363
export function useWebsocketAwareQuery<
4464
TQueryFnData = unknown,
4565
TError = unknown,
@@ -60,13 +80,17 @@ export function useWebsocketAwareQuery<
6080
const previousConnectedCount = usePrevious(connectedCount);
6181

6282
useEffect(() => {
83+
// connectedCount will change if the websocket reconnects - if this happens, we should invalidate the query
6384
if (connectedCount !== previousConnectedCount) {
6485
queryClient.invalidateQueries({ queryKey });
6586
}
6687
}, [connectedCount, previousConnectedCount, queryClient, queryKey]);
6788

6889
useEffect(() => {
90+
// subscribe returns a function to remove the event listener for NOTIFY messages;
91+
// This function will be used as the cleanup function for the effect.
6992
return subscribe(
93+
// This callback function will be called when a NOTIFY message is received
7094
({ name: model }: { action: string; name: WebSocketEndpointModel }) => {
7195
const mappedKey = wsToQueryKeyMapping[model];
7296
const modelQueryKey = queryKey[0];

src/app/api/query/zones.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,29 @@ const zoneKeys = {
88
list: ["zones"] as const,
99
};
1010

11+
/**
12+
* Fetches a list of zones.
13+
*
14+
* @returns The zones list as a UseQueryResult
15+
*/
1116
export const useZones = () => {
1217
return useWebsocketAwareQuery(zoneKeys.list, fetchZones);
1318
};
1419

20+
/**
21+
* Fetches the number of zones.
22+
* @returns The number of zones as a UseQueryResult
23+
*/
1524
export const useZoneCount = () =>
1625
useWebsocketAwareQuery<Zone[], Zone[], number>(["zones"], fetchZones, {
1726
select: (data) => data?.length ?? 0,
1827
});
1928

29+
/**
30+
* Get a zone by its ID from the list result.
31+
* @param id The zone's ID
32+
* @returns A Zone, or null as a UseQueryResult
33+
*/
2034
export const useZoneById = (id?: ZonePK | null) =>
2135
useWebsocketAwareQuery(zoneKeys.list, fetchZones, {
2236
select: selectById<Zone>(id ?? null),

0 commit comments

Comments
 (0)