-
Notifications
You must be signed in to change notification settings - Fork 699
feat(ui): fetch storage capabilities from backend during Vite dev mode #3544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d9c7aff
10244f8
e143e1a
83038e1
0445460
0ffe98b
bde05c2
6bd9e08
5270fc6
50456de
dfc45b6
b65bfb5
66d16e2
28d9e5e
381a83c
d3defc0
ba7bd29
141747f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,10 @@ import monitorImg from './media/monitor.png'; | |
| const MonitorATMEmptyState: React.FC = () => { | ||
| const config: MonitorEmptyStateConfig = getConfigValue('monitor.emptyState'); | ||
|
|
||
| if (!config) { | ||
| return null; | ||
| } | ||
|
Comment on lines
+14
to
+16
|
||
|
|
||
| return ( | ||
| <Col> | ||
| <Row justify="center"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Copyright (c) 2025 The Jaeger Authors. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // Fields that should be merged with user-supplied config values rather than overwritten. | ||
| export type TMergeField = 'dependencies' | 'search' | 'tracking' | 'monitor'; | ||
| export const mergeFields: readonly TMergeField[] = ['dependencies', 'search', 'tracking', 'monitor']; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,9 +119,9 @@ const defaultConfig: Config = { | |
| useOpenTelemetryTerms: false, | ||
| }; | ||
|
|
||
| // Fields that should be merged with user-supplied config values rather than overwritten. | ||
| type TMergeField = 'dependencies' | 'search' | 'tracking'; | ||
| export const mergeFields: readonly TMergeField[] = ['dependencies', 'search', 'tracking']; | ||
| import { mergeFields } from './config-keys'; | ||
|
||
|
|
||
| export { mergeFields }; | ||
|
|
||
| export default deepFreeze(defaultConfig); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,11 +8,12 @@ import legacy from '@vitejs/plugin-legacy'; | |||||
| import path from 'path'; | ||||||
| import fs from 'fs'; | ||||||
| import { fileURLToPath } from 'url'; | ||||||
| import { mergeFields } from './src/constants/config-keys'; | ||||||
|
|
||||||
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | ||||||
|
|
||||||
| const proxyConfig = { | ||||||
| target: 'http://localhost:16686', | ||||||
| target: 'http://127.0.0.1:16686', | ||||||
|
||||||
| target: 'http://127.0.0.1:16686', | |
| target: 'http://localhost:16686', |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The transformIndexHtml hook runs during vite build as well as dev server. With the new backend fetch + HTML replacements, a local backend running during build would bake UI config/capabilities/version into the built index.html and remove the exact placeholder patterns the query-service expects to search/replace at runtime. Consider restricting this plugin to dev-only (e.g., apply: 'serve' and/or guard on Vite command === 'serve') so production builds keep the injection markers intact.
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variables 'backendUiConfig', 'storageCapabilities', and 'version' are all typed as 'any', which bypasses TypeScript's type safety. Since '@types/node' was added as a dependency in this PR, consider defining proper TypeScript interfaces for the expected backend response structure. This would make the code more maintainable and catch potential type errors at compile time.
Copilot
AI
Feb 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variables backendUiConfig, storageCapabilities, and version are typed as any. Consider using more specific types or at least defining a type interface for the expected backend response structure. This would provide better type safety and make the code more maintainable. For example:
interface BackendConfig {
uiConfig?: Record<string, any>;
storageCapabilities?: { archiveStorage?: boolean; metricsStorage?: boolean };
version?: { gitCommit: string; gitVersion: string; buildDate: string };
}
let backendConfig: BackendConfig | null = null;
Copilot
AI
Feb 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout value (1000ms) is hardcoded in the implementation. This should be extracted as a named constant at the top of the function or module level to make it more maintainable and easier to adjust if needed. Consider defining something like const BACKEND_FETCH_TIMEOUT_MS = 1000;.
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This plugin is included unconditionally in the Vite config, and transformIndexHtml also runs during vite build. With the new backend fetch, production builds can become non-deterministic (if a local backend is running) and will always incur the 1s timeout on failures. Consider gating the backend fetch logic to command === 'serve' / dev mode so builds remain reproducible and fast.
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optional chaining operator 'response?.ok' is unnecessary here. If the fetch call succeeds without throwing, the response object is guaranteed to be defined. The check can be simplified to 'response.ok'. The optional chaining doesn't add safety since any network or timeout errors are already caught in the catch block.
| if (response?.ok) { | |
| if (response.ok) { |
Copilot
AI
Feb 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding validation for the backend response to ensure it has the expected structure before attempting to access properties. For example, if data is null or not an object, accessing data.storageCapabilities could lead to unexpected behavior. Adding a basic check like if (data && typeof data === 'object') would make the code more robust.
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code assumes the backend endpoint '/api/ui/config' exists and returns a JSON response with 'storageCapabilities', 'version', and 'uiConfig' fields. However, this is a frontend-only repository and the backend implementation is not visible in this PR. The PR description mentions this endpoint as a "prerequisite", but there's no verification that it exists or that it returns data in the expected format. If the endpoint doesn't exist yet or returns a different structure, the fetch will fail silently and the UI will fall back to defaults. Consider adding a comment documenting the expected response structure, or better yet, creating a TypeScript interface for the expected response.
Copilot
AI
Feb 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optional chaining response?.ok is unnecessary here because if fetch completes successfully (doesn't throw), it always returns a Response object, never null or undefined. The optional chaining may give a false sense of safety - if the fetch succeeds but returns a non-ok status (e.g., 404, 500), the code silently skips config injection without logging any information about the error status.
Consider changing this to check only response.ok and optionally log the status code when it's not ok: if (!response.ok) { console.log('[jaeger-ui-config] Backend returned status: ' + response.status); }
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transformIndexHtml will run on every page load in dev, and this implementation fetches /api/ui/config each time (plus logs on success). This can add noticeable overhead and noisy logs during development. Consider caching the backend response for a short TTL (e.g., 5–30s) and/or only logging when the source changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AbortError check will never execute because the fetch is already wrapped in .catch(() => null) on line 63. When the timeout triggers controller.abort(), the fetch's catch handler converts it to null, preventing the outer catch block from seeing the AbortError.
The code will incorrectly log "Backend not running or error fetching config" instead of "Backend fetch timed out (1s)" when a timeout occurs.
Fix:
try {
const fetchOptions = { signal: controller.signal };
const response = await fetch('http://127.0.0.1:16686/api/ui/config', fetchOptions);
if (response?.ok) {
const data = await response.json();
// ... rest of the logic
}
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') {
console.log('[jaeger-ui-config] Backend fetch timed out (1s)');
} else {
console.log('[jaeger-ui-config] Backend not running or error fetching config');
}
}Remove the .catch(() => null) to allow proper error propagation.
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backend fetch failures/timeouts are logged inside transformIndexHtml, which runs frequently during dev. If the backend isn’t running, this will repeatedly print the same message on every request/reload. Consider logging once per TTL (or behind a debug flag) to avoid noisy console output.
Copilot
AI
Feb 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a JS config file exists, the code still attempts to inject storageCapabilities and version from the backend, but it doesn't inject the backend's uiConfig. This creates an inconsistency: with JS config, developers lose access to the backend's uiConfig as a baseline, which contradicts one of the key benefits mentioned in the PR description ("automatic backend sync").
Consider whether JS config should also have access to backend uiConfig values, perhaps by making them available as a parameter or global variable that the JS config function can reference if needed.
Copilot
AI
Feb 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error object 'err' is declared but never used in this catch block. The error should either be logged for debugging purposes (especially helpful during development) or the parameter should be removed if intentionally unused. Consider changing to 'console.error' or adding a comment explaining why the error is deliberately ignored.
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a jaeger-ui.config.js file exists, the code doesn't inject the finalUiConfig into the HTML because it's inside the else block (lines 101-135). This means that even if the backend provides uiConfig, it won't be injected when using the JS config file. The storageCapabilities and version will be injected (lines 137-151), but JAEGER_CONFIG won't be set, potentially breaking the UI. The logic should inject JAEGER_CONFIG regardless of which config path is taken, or document why this is intentional.
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The shallow merge approach here differs from how the get-config.ts handles merging. In get-config.ts (lines 42-47), only fields listed in mergeFields are shallow merged, while other fields are completely overwritten. However, this implementation always does a shallow merge of the entire uiConfig object regardless of field semantics.
This could lead to unexpected behavior where nested properties from backendUiConfig inadvertently bleed through when they should be completely replaced. Consider applying the same mergeFields logic here as used in get-config.ts to maintain consistency.
Copilot
AI
Feb 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error object 'err' is declared but never used in this catch block. Similar to the issue above, the error should either be logged for debugging or the parameter should be removed. This is especially important here since JSON parsing errors would be helpful to see during development.
Copilot
AI
Feb 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSON.stringify calls on lines 129, 138, and 146 inject configuration data into a script tag but don't escape the string </script>. If the backend returns configuration containing </script> in any string value, it could prematurely close the script tag. While the risk is low (requires malicious localhost backend), consider using .replace(/<\/script>/gi, '<\\/script>') on the JSON.stringify output as a defense-in-depth measure. This follows best practices for injecting JSON into HTML script contexts.
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple JSON.stringify calls (lines 131, 140, 148) are used on data fetched from the backend without error handling. If the backend returns objects with circular references or other non-serializable content, these calls will throw and crash the dev server during HTML transformation. Consider wrapping these operations in try-catch blocks or at minimum documenting the assumption that backend data is always JSON-serializable.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,5 @@ | ||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * This file previously contained a standalone OpenTelemetry verification script. | ||||||||||||||||||||||||||||||||||||
| * It has been intentionally left empty to avoid keeping unreferenced tooling | ||||||||||||||||||||||||||||||||||||
| * at the repository root. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2
to
+5
|
||||||||||||||||||||||||||||||||||||
| * This file previously contained a standalone OpenTelemetry verification script. | |
| * It has been intentionally left empty to avoid keeping unreferenced tooling | |
| * at the repository root. | |
| */ | |
| * Deprecated OpenTelemetry verification entrypoint. | |
| * | |
| * This script is kept only for backward compatibility with existing tooling | |
| * or documentation that may still reference `verify_otel.cjs`. | |
| * It no longer performs any verification. | |
| */ | |
| if (require.main === module) { | |
| console.warn( | |
| 'verify_otel.cjs is deprecated and no longer performs any checks. ' + | |
| 'Please remove any references to this script.' | |
| ); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The repo declares
engines.nodeas>=24, but this adds@types/node^25.2.3. If the project is targeting Node 24 in CI/dev, consider aligning the major version of@types/nodewith the supported runtime (or pinning a known-good version) to avoid type drift and incompatibilities.