diff --git a/packages/jaeger-ui/index.html b/packages/jaeger-ui/index.html index c54d657cb7..d5e30a6e26 100644 --- a/packages/jaeger-ui/index.html +++ b/packages/jaeger-ui/index.html @@ -33,6 +33,12 @@ const JAEGER_CONFIG = DEFAULT_CONFIG; return JAEGER_CONFIG; } + // Jaeger storage compabilities data is embedded by the query-service via search-replace. + function getJaegerStorageCapabilities() { + const DEFAULT_STORAGE_CAPABILITIES = { "archiveStorage": false }; + const JAEGER_STORAGE_CAPABILITIES = DEFAULT_STORAGE_CAPABILITIES; + return JAEGER_STORAGE_CAPABILITIES; + } // Jaeger version data is embedded by the query-service via search/replace. function getJaegerVersion() { const DEFAULT_VERSION = {'gitCommit':'', 'gitVersion':'', 'buildDate':''}; diff --git a/packages/jaeger-ui/src/components/TracePage/index.test.js b/packages/jaeger-ui/src/components/TracePage/index.test.js index bed1d620b7..de4c1a3da4 100644 --- a/packages/jaeger-ui/src/components/TracePage/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/index.test.js @@ -400,8 +400,12 @@ describe('', () => { it('is true when not embedded and archive is enabled', () => { [{ timeline: {} }, undefined].forEach(embedded => { [true, false].forEach(archiveEnabled => { - wrapper.setProps({ embedded, archiveEnabled }); - expect(wrapper.find(TracePageHeader).prop('showArchiveButton')).toBe(!embedded && archiveEnabled); + [{ archiveStorage: false }, { archiveStorage: true }].forEach(storageCapabilities => { + wrapper.setProps({ embedded, archiveEnabled, storageCapabilities }); + expect(wrapper.find(TracePageHeader).prop('showArchiveButton')).toBe( + !embedded && archiveEnabled && storageCapabilities.archiveStorage + ); + }); }); }); }); diff --git a/packages/jaeger-ui/src/components/TracePage/index.tsx b/packages/jaeger-ui/src/components/TracePage/index.tsx index eb15b2882b..1a887e713d 100644 --- a/packages/jaeger-ui/src/components/TracePage/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/index.tsx @@ -57,7 +57,7 @@ import updateUiFind from '../../utils/update-ui-find'; import TraceStatistics from './TraceStatistics/index'; import TraceSpanView from './TraceSpanView/index'; import TraceFlamegraph from './TraceFlamegraph/index'; -import { TraceGraphConfig } from '../../types/config'; +import { StorageCapabilities, TraceGraphConfig } from '../../types/config'; import './index.css'; import memoizedTraceCriticalPath from './CriticalPath/index'; @@ -78,6 +78,7 @@ type TOwnProps = { type TReduxProps = { archiveEnabled: boolean; + storageCapabilities: StorageCapabilities | TNil; archiveTraceState: TraceArchive | TNil; criticalPathEnabled: boolean; embedded: null | EmbeddedState; @@ -326,6 +327,7 @@ export class TracePageImpl extends React.PureComponent { render() { const { archiveEnabled, + storageCapabilities, archiveTraceState, criticalPathEnabled, embedded, @@ -359,6 +361,7 @@ export class TracePageImpl extends React.PureComponent { } const isEmbedded = Boolean(embedded); + const hasArchiveStorage = Boolean(storageCapabilities?.archiveStorage); const headerProps = { focusUiFindMatches: this.focusUiFindMatches, slimView, @@ -380,7 +383,7 @@ export class TracePageImpl extends React.PureComponent { ref: this._searchBar, resultCount: findCount, disableJsonView, - showArchiveButton: !isEmbedded && archiveEnabled, + showArchiveButton: !isEmbedded && archiveEnabled && hasArchiveStorage, showShortcutsHelp: !isEmbedded, showStandaloneLink: isEmbedded, showViewOptions: !isEmbedded, @@ -445,6 +448,7 @@ export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxP const trace = id ? traces[id] : null; const archiveTraceState = id ? archive[id] : null; const archiveEnabled = Boolean(config.archiveEnabled); + const storageCapabilities = config.storageCapabilities; const { disableJsonView, criticalPathEnabled } = config; const { state: locationState } = router.location; const searchUrl = (locationState && locationState.fromSearch) || null; @@ -453,6 +457,7 @@ export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxP return { ...extractUiFindFromState(state), archiveEnabled, + storageCapabilities, archiveTraceState, criticalPathEnabled, embedded, diff --git a/packages/jaeger-ui/src/constants/default-config.tsx b/packages/jaeger-ui/src/constants/default-config.tsx index 9f1bc4bff7..9bf2ce2155 100644 --- a/packages/jaeger-ui/src/constants/default-config.tsx +++ b/packages/jaeger-ui/src/constants/default-config.tsx @@ -21,7 +21,7 @@ import { version } from '../../package.json'; import { Config } from '../types/config'; const defaultConfig: Config = { - archiveEnabled: false, + archiveEnabled: true, criticalPathEnabled: true, dependencies: { dagMaxNumServices: FALLBACK_DAG_MAX_NUM_SERVICES, @@ -77,6 +77,9 @@ const defaultConfig: Config = { }, maxLimit: 1500, }, + storageCapabilities: { + archiveStorage: false, + }, tracking: { gaID: null, trackErrors: true, diff --git a/packages/jaeger-ui/src/types/config.tsx b/packages/jaeger-ui/src/types/config.tsx index e6938f2a00..e27b43b35e 100644 --- a/packages/jaeger-ui/src/types/config.tsx +++ b/packages/jaeger-ui/src/types/config.tsx @@ -82,6 +82,11 @@ export type TraceGraphConfig = { layoutManagerMemory?: number; }; +export type StorageCapabilities = { + // archiveStorage indicates whether the query service supports archive storage. + archiveStorage?: boolean; +}; + // Default values are provided in packages/jaeger-ui/src/constants/default-config.tsx export type Config = { // @@ -130,6 +135,9 @@ export type Config = { // TODO when is it useful? scripts?: readonly TScript[]; + // storage capabilities given by the query service. + storageCapabilities?: StorageCapabilities; + // topTagPrefixes defines a set of prefixes for span tag names that are considered // "important" and cause the matching tags to appear higher in the list of tags. // For example, topTagPrefixes=['http.'] would cause all span tags that begin with diff --git a/packages/jaeger-ui/src/utils/config/get-config.test.js b/packages/jaeger-ui/src/utils/config/get-config.test.js index d2456036df..52ab1d867a 100644 --- a/packages/jaeger-ui/src/utils/config/get-config.test.js +++ b/packages/jaeger-ui/src/utils/config/get-config.test.js @@ -37,9 +37,10 @@ describe('getConfig()', () => { console.warn = oldWarn; }); - describe('`window.getJaegerUiConfig` is not a function', () => { + describe('index functions are not yet injected by backend', () => { beforeAll(() => { window.getJaegerUiConfig = undefined; + window.getJaegerStorageCapabilities = undefined; }); it('warns once', () => { @@ -54,15 +55,16 @@ describe('getConfig()', () => { }); }); - describe('`window.getJaegerUiConfig` is a function', () => { + describe('index functions are injected by backend', () => { let embedded; - let getJaegerUiConfig; + let capabilities; beforeEach(() => { getConfig.apply({}, []); embedded = {}; - getJaegerUiConfig = jest.fn(() => embedded); - window.getJaegerUiConfig = getJaegerUiConfig; + window.getJaegerUiConfig = jest.fn(() => embedded); + capabilities = defaultConfig.storageCapabilities; + window.getJaegerStorageCapabilities = jest.fn(() => capabilities); }); it('returns the default config when the embedded config is `null`', () => { @@ -70,9 +72,10 @@ describe('getConfig()', () => { expect(getConfig()).toEqual(defaultConfig); }); - it('merges the defaultConfig with the embedded config ', () => { + it('merges the defaultConfig with the embedded config and storage capabilities', () => { embedded = { novel: 'prop' }; - expect(getConfig()).toEqual({ ...defaultConfig, ...embedded }); + capabilities = { archiveStorage: true }; + expect(getConfig()).toEqual({ ...defaultConfig, ...embedded, storageCapabilities: capabilities }); }); describe('overwriting precedence and merging', () => { @@ -84,7 +87,7 @@ describe('getConfig()', () => { keys.forEach(key => { embedded[key] = key; }); - expect(getConfig()).toEqual({ ...defaultConfig, ...embedded }); + expect(getConfig()).toEqual({ ...defaultConfig, ...embedded, storageCapabilities: capabilities }); }); }); @@ -94,7 +97,7 @@ describe('getConfig()', () => { mergeFields.forEach((k, i) => { embedded[k] = i ? true : null; }); - expect(getConfig()).toEqual({ ...defaultConfig, ...embedded }); + expect(getConfig()).toEqual({ ...defaultConfig, ...embedded, storageCapabilities: capabilities }); }); it('merges object values', () => { @@ -106,7 +109,7 @@ describe('getConfig()', () => { embedded[key] = { a: true, b: false }; const expected = { ...defaultConfig, ...embedded }; expected[key] = { ...defaultConfig[key], ...embedded[key] }; - expect(getConfig()).toEqual(expected); + expect(getConfig()).toEqual({ ...expected, storageCapabilities: capabilities }); }); }); }); diff --git a/packages/jaeger-ui/src/utils/config/get-config.tsx b/packages/jaeger-ui/src/utils/config/get-config.tsx index 7d39ac7b93..9c19117c19 100644 --- a/packages/jaeger-ui/src/utils/config/get-config.tsx +++ b/packages/jaeger-ui/src/utils/config/get-config.tsx @@ -18,44 +18,47 @@ import memoizeOne from 'memoize-one'; import processDeprecation from './process-deprecation'; import defaultConfig, { deprecations, mergeFields } from '../../constants/default-config'; -let haveWarnedFactoryFn = false; -let haveWarnedDeprecations = false; - /** * Merge the embedded config from the query service (if present) with the * default config from `../../constants/default-config`. */ const getConfig = memoizeOne(function getConfig() { - const getJaegerUiConfig = window.getJaegerUiConfig; - if (typeof getJaegerUiConfig !== 'function') { - if (!haveWarnedFactoryFn) { - // eslint-disable-next-line no-console - console.warn('Embedded config not available'); - haveWarnedFactoryFn = true; - } - return { ...defaultConfig }; - } - const embedded = getJaegerUiConfig(); + const capabilities = getCapabilities(); + + const embedded = getUiConfig(); if (!embedded) { - return { ...defaultConfig }; + return { ...defaultConfig, storageCapabilities: capabilities }; } // check for deprecated config values if (Array.isArray(deprecations)) { - deprecations.forEach(deprecation => processDeprecation(embedded, deprecation, !haveWarnedDeprecations)); - haveWarnedDeprecations = true; + deprecations.forEach(deprecation => processDeprecation(embedded, deprecation, true)); } const rv = { ...defaultConfig, ...embedded }; // mergeFields config values should be merged instead of fully replaced - const keys = mergeFields; - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (typeof embedded[key] === 'object' && embedded[key] !== null) { + mergeFields.forEach(key => { + if (embedded && typeof embedded[key] === 'object' && embedded[key] !== null) { rv[key] = { ...defaultConfig[key], ...embedded[key] }; } - } - return rv; + }); + return { ...rv, storageCapabilities: capabilities }; }); +function getUiConfig() { + const getter = window.getJaegerUiConfig; + if (typeof getter !== 'function') { + // eslint-disable-next-line no-console + console.warn('Embedded config not available'); + return { ...defaultConfig }; + } + return getter(); +} + +function getCapabilities() { + const getter = window.getJaegerStorageCapabilities; + const capabilities = typeof getter === 'function' ? getter() : null; + return capabilities ?? defaultConfig.storageCapabilities; +} + export default getConfig; export function getConfigValue(path: string) { diff --git a/packages/jaeger-ui/test/jest-per-test-setup.js b/packages/jaeger-ui/test/jest-per-test-setup.js index 2a411af60b..30793654eb 100644 --- a/packages/jaeger-ui/test/jest-per-test-setup.js +++ b/packages/jaeger-ui/test/jest-per-test-setup.js @@ -32,6 +32,7 @@ expect.addSnapshotSerializer(createSerializer({ mode: 'deep' })); // Calls to get-config.tsx and get-version.tsx warn if these globals are not functions. // This file is executed before each test file, so they may be overridden safely. window.getJaegerUiConfig = () => ({}); +window.getJaegerStorageCapabilities = () => ({}); window.getJaegerVersion = () => ({ gitCommit: '', gitVersion: '', diff --git a/packages/jaeger-ui/typings/custom.d.ts b/packages/jaeger-ui/typings/custom.d.ts index cc78cbc49e..b12c879ab4 100644 --- a/packages/jaeger-ui/typings/custom.d.ts +++ b/packages/jaeger-ui/typings/custom.d.ts @@ -22,6 +22,7 @@ declare interface Window { __webpack_public_path__: string; // eslint-disable-line camelcase // For getting ui config getJaegerUiConfig?: () => Record; + getJaegerStorageCapabilities?: () => Record; getJaegerVersion?: () => Record; }