Skip to content

Commit f53c4a5

Browse files
Merge pull request #203 from alanconway/coo-1650-metric-blank
COO-1650: Troubleshooting panel not updated by internal URL changes.
2 parents 6ae2bc2 + 0e1364c commit f53c4a5

4 files changed

Lines changed: 87 additions & 33 deletions

File tree

web/locales/en/plugin__troubleshooting-panel-console-plugin.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
"Advanced search parameters": "Advanced search parameters",
66
"Cancel": "Cancel",
77
"Correlation graph already matches search": "Correlation graph already matches search",
8-
"Correlation graph is focused on the main view.": "Correlation graph is focused on the main view.",
8+
"Correlation graph is already focused on the current view.": "Correlation graph is already focused on the current view.",
99
"Correlation result was empty.": "Correlation result was empty.",
10-
"Current view does not allow correlation.": "Current view does not allow correlation.",
10+
"Current view does not provide a starting point for correlation": "Current view does not provide a starting point for correlation",
1111
"Custom": "Custom",
1212
"Custom duration": "Custom duration",
1313
"Custom time range": "Custom time range",
14+
"Empty Query": "Empty Query",
15+
"Empty Result": "Empty Result",
1416
"End time must be after start time": "End time must be after start time",
1517
"Fix validation errors before searching": "Fix validation errors before searching",
1618
"Focus": "Focus",
17-
"Focus the correlation on the main view.": "Focus the correlation on the main view.",
19+
"Focus the correlation on the current view.": "Focus the correlation on the current view.",
1820
"From": "From",
1921
"Goals": "Goals",
2022
"Include data from this time range": "Include data from this time range",
@@ -24,7 +26,9 @@
2426
"Neighbours": "Neighbours",
2527
"Neighbours or Goal search": "Neighbours or Goal search",
2628
"Netflow Plugin Disabled": "Netflow Plugin Disabled",
29+
"No correlated data was found": "No correlated data was found",
2730
"No Correlated Signals Found": "No Correlated Signals Found",
31+
"No starting point for correlation": "No starting point for correlation",
2832
"Open the Troubleshooting Panel": "Open the Troubleshooting Panel",
2933
"Other duration": "Other duration",
3034
"Quickly diagnose and resolve issues by exploring correlated observability signals for resources.": "Quickly diagnose and resolve issues by exploring correlated observability signals for resources.",

web/src/components/Korrel8rPanel.tsx

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,46 +62,66 @@ export default function Korrel8rPanel() {
6262
return new korrel8r.Constraint({ start: pStart, end: pEnd });
6363
}, [search.period]);
6464

65-
const initialized = React.useRef(false);
65+
// Call korrel8r service to get results for the current search.
6666
React.useEffect(() => {
67-
if (initialized.current) return; // Run once on mount
68-
initialized.current = true;
69-
if (!search?.queryStr && locationQuery) {
70-
dispatch(setSearch({ ...defaultSearch, queryStr: locationQuery.toString() }));
71-
}
72-
}, [search?.queryStr, locationQuery, dispatch]);
73-
74-
React.useEffect(() => {
75-
// Leave stored result in place if there is one.
76-
// Dispatching SetSearch clears result to null, triggering a new fetch.
67+
// If we already have a stored result, do nothing.
68+
// Dispatching SetSearch sets result=null, triggering a new fetch.
7769
if (result) return;
7870

79-
const queryStr = search?.queryStr?.trim();
80-
const start: api.Start = {
81-
queries: queryStr ? [queryStr] : undefined,
82-
constraint: constraint?.toAPI(),
83-
};
8471
let cancelled = false;
8572
const onResult = (newResult: Result) => {
8673
if (!cancelled) dispatch(setResult(newResult));
8774
};
75+
76+
const queryStr = (search?.queryStr ?? '').trim();
77+
78+
if (!queryStr) {
79+
// Default query or empty result
80+
if (locationQuery?.toString()) {
81+
dispatch(setSearch({ ...defaultSearch, queryStr: locationQuery?.toString() }));
82+
} else {
83+
dispatch(
84+
setResult({
85+
title: t('Empty Query'),
86+
message: t('No starting point for correlation'),
87+
}),
88+
);
89+
}
90+
return;
91+
}
92+
93+
const start: api.Start = {
94+
queries: queryStr ? [queryStr] : undefined,
95+
constraint: constraint?.toAPI(),
96+
};
97+
8898
const fetch =
8999
search.searchType === SearchType.Goal
90100
? getGoalsGraph({ start, goals: [search.goal] })
91101
: getNeighborsGraph({ start, depth: search.depth });
92102
fetch
93-
.then((response: api.Graph) => onResult({ graph: new korrel8r.Graph(response) }))
103+
.then((response: api.Graph) => {
104+
if (Array.isArray(response?.nodes) && response.nodes.length > 0) {
105+
onResult({ graph: new korrel8r.Graph(response) });
106+
} else {
107+
onResult({
108+
title: t('Empty Result'),
109+
message: t('No correlated data was found'),
110+
});
111+
}
112+
})
94113
.catch((e: api.ApiError) => {
95114
onResult({
96115
title: e?.body?.error ? t('Korrel8r Error') : t('Request Failed'),
97116
message: e?.body?.error || e.message || 'Unknown Error',
117+
isError: true,
98118
});
99119
});
100120
return () => {
101121
cancelled = true;
102122
fetch.cancel();
103123
};
104-
}, [search, t, result, constraint, dispatch]);
124+
}, [search, t, result, constraint, dispatch, locationQuery]);
105125

106126
const advancedToggleID = 'query-toggle';
107127
const advancedContentID = 'query-content';
@@ -124,9 +144,9 @@ export default function Korrel8rPanel() {
124144
content={
125145
locationQuery
126146
? isFocused
127-
? t('Correlation graph is focused on the main view.')
128-
: t('Focus the correlation on the main view.')
129-
: t('Current view does not allow correlation.')
147+
? t('Correlation graph is already focused on the current view.')
148+
: t('Focus the correlation on the current view.')
149+
: t('Current view does not provide a starting point for correlation')
130150
}
131151
>
132152
<Button

web/src/hooks/useLocationQuery.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,48 @@
1-
import { useLocation } from 'react-router-dom-v5-compat';
1+
import * as React from 'react';
22
import { Query, URIRef } from '../korrel8r/types';
33
import { useDomains } from './useDomains';
44

5+
/** Get a snapshot of the current browser location */
6+
const getLocation = () => ({
7+
pathname: window.location.pathname,
8+
search: window.location.search,
9+
});
10+
11+
/**
12+
* Hook that tracks the browser location, reacting to all navigation changes.
13+
*
14+
* Uses requestAnimationFrame polling to detect URL changes rather than patching
15+
* history.pushState/replaceState, avoiding conflicts with other code that may
16+
* also intercept history methods (e.g. the console's router).
17+
*
18+
* react-router-dom-v5-compat's useLocation() doesn't update when the panel is
19+
* rendered via useModal(), because the modal may not receive router context updates.
20+
*/
21+
const useBrowserLocation = () => {
22+
const [location, setLocation] = React.useState(getLocation);
23+
const lastHref = React.useRef(window.location.href);
24+
25+
React.useEffect(() => {
26+
let rafId: number;
27+
const check = () => {
28+
const href = window.location.href;
29+
if (href !== lastHref.current) {
30+
lastHref.current = href;
31+
setLocation(getLocation());
32+
}
33+
rafId = requestAnimationFrame(check);
34+
};
35+
rafId = requestAnimationFrame(check);
36+
return () => cancelAnimationFrame(rafId);
37+
}, []);
38+
39+
return location;
40+
};
41+
542
/** Returns the Korrel8r query for the current browser location or undefined */
643
export const useLocationQuery = (): Query | undefined => {
744
const domains = useDomains();
8-
const location = useLocation();
45+
const location = useBrowserLocation();
946
try {
1047
const link = new URIRef(location.pathname + location.search);
1148
const q = domains.linkToQuery(link);

web/src/hooks/useQueryParams.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)