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..1efe6e002e 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,20 @@ describe('getConfig()', () => { }); }); - describe('`window.getJaegerUiConfig` is a function', () => { + describe('index functions are injected by backend', () => { let embedded; let getJaegerUiConfig; + let capabilities; + let getJaegerStorageCapabilities; beforeEach(() => { getConfig.apply({}, []); embedded = {}; getJaegerUiConfig = jest.fn(() => embedded); window.getJaegerUiConfig = getJaegerUiConfig; + capabilities = defaultConfig.capabilities; + getJaegerStorageCapabilities = jest.fn(() => capabilities); + window.getJaegerStorageCapabilities = getJaegerStorageCapabilities; }); it('returns the default config when the embedded config is `null`', () => { @@ -70,9 +76,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, ...capabilities }); }); describe('overwriting precedence and merging', () => { diff --git a/packages/jaeger-ui/src/utils/config/get-config.tsx b/packages/jaeger-ui/src/utils/config/get-config.tsx index 7d39ac7b93..c80810fa07 100644 --- a/packages/jaeger-ui/src/utils/config/get-config.tsx +++ b/packages/jaeger-ui/src/utils/config/get-config.tsx @@ -26,36 +26,52 @@ let haveWarnedDeprecations = false; * 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 config = {...defaultConfig, storageCapabilities: capabilities}; + + const embedded = getUiConfig(); if (!embedded) { - return { ...defaultConfig }; + return config; } // check for deprecated config values if (Array.isArray(deprecations)) { deprecations.forEach(deprecation => processDeprecation(embedded, deprecation, !haveWarnedDeprecations)); haveWarnedDeprecations = true; } - const rv = { ...defaultConfig, ...embedded }; + const rv = { ...config, ...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) { - rv[key] = { ...defaultConfig[key], ...embedded[key] }; + if (embedded && typeof embedded[key] === 'object' && embedded[key] !== null) { + rv[key] = { ...config[key], ...embedded[key] }; } } return rv; }); +function getUiConfig() { + 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 }; + } + + return getJaegerUiConfig(); +} + +function getCapabilities() { + const getJaegerCapabilities = window.getJaegerStorageCapabilities; + if (typeof getJaegerCapabilities !== 'function' || !getJaegerCapabilities()) { + return defaultConfig.storageCapabilities; + } + return getJaegerCapabilities(); +} + 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; }