diff --git a/.eslintrc.js b/.eslintrc.js index 7067da32f8..ce1327bdc9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,7 +21,7 @@ module.exports = { settings: { 'import/resolver': { node: { - extensions: ['.js', '.jsx', 'json', '.tsx'], + extensions: ['.js', '.jsx', 'json', '.tsx', ".ts"], }, }, }, diff --git a/packages/jaeger-ui/index.html b/packages/jaeger-ui/index.html index afb2efbe36..1f0be049bb 100644 --- a/packages/jaeger-ui/index.html +++ b/packages/jaeger-ui/index.html @@ -39,26 +39,10 @@ window.global = {}; diff --git a/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts b/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts new file mode 100644 index 0000000000..b791699aef --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts @@ -0,0 +1,35 @@ +import { ActionListener } from "./types"; + +export class ActionDispatcher { + private actions: { + [key: string]: ActionListener[]; + }; + + constructor() { + this.actions = {}; + } + + public addActionListener(type: string, listener: ActionListener) { + if (!this.actions[type]) { + this.actions[type] = [listener]; + } else { + this.actions[type].push(listener); + } + } + + public removeActionListener(type: string, listener: ActionListener) { + if (this.actions[type]) { + this.actions[type] = this.actions[type].filter((x) => x !== listener); + } + + if (this.actions[type].length === 0) { + delete this.actions[type]; + } + } + + public dispatch(type: string, data?: unknown): void { + if (this.actions[type]) { + this.actions[type].forEach(fn => fn(data)); + } + } +} \ No newline at end of file diff --git a/packages/jaeger-ui/src/api/digma/actions.ts b/packages/jaeger-ui/src/api/digma/actions.ts new file mode 100644 index 0000000000..46d5f45833 --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/actions.ts @@ -0,0 +1,5 @@ +export const actions = { + GO_TO_SPAN: "GO_TO_SPAN", + GET_SPANS_WITH_RESOLVED_LOCATION: "GET_SPANS_WITH_RESOLVED_LOCATION", + SET_SPANS_WITH_RESOLVED_LOCATION:"SET_SPANS_WITH_RESOLVED_LOCATION" +} \ No newline at end of file diff --git a/packages/jaeger-ui/src/api/digma/dispatcher.ts b/packages/jaeger-ui/src/api/digma/dispatcher.ts new file mode 100644 index 0000000000..2941e86bcc --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/dispatcher.ts @@ -0,0 +1,3 @@ +import { ActionDispatcher } from "./ActionDispatcher"; + +export const dispatcher = new ActionDispatcher(); \ No newline at end of file diff --git a/packages/jaeger-ui/src/api/digma/index.ts b/packages/jaeger-ui/src/api/digma/index.ts new file mode 100644 index 0000000000..e392c711e4 --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/index.ts @@ -0,0 +1,58 @@ +import { isObject } from "../../utils/ts/typeGuards/isObject"; +import { ActionDispatcher } from "./ActionDispatcher"; +import { updateState } from "./state"; +import { DigmaMessageEvent, IDigmaOutgoingMessageData } from "./types"; + +const isDigmaMessageEvent = (e: MessageEvent): e is DigmaMessageEvent => + isObject(e.data) && e.data.type === "digma"; + +export const initializeDigmaMessageListener = ( + dispatcher: ActionDispatcher +) => { + window.addEventListener("message", e => { + if (isDigmaMessageEvent(e)) { + console.info("Digma message received: ", e); + + updateState(e.data.action, e.data.payload); + + dispatcher.dispatch(e.data.action, e.data.payload); + } + }); +}; + +export const sendMessage = ( + message: IDigmaOutgoingMessageData +): string | undefined => { + console.info("Message to send:", message); + + updateState(message.action, message.payload); + + if (window.sendMessageToVSCode) { + window.sendMessageToVSCode(message); + console.info("Message has been sent to VS Code: ", message); + } + + if (window.cefQuery) { + return window.cefQuery({ + request: JSON.stringify(message), + onSuccess (response) { + console.info("cefQuery has been successfully sent: %s", response); + }, + onFailure (errorCode, errorMessage) { + console.error( + "Failed to send cefQuery: %d, %s", + errorCode, + errorMessage + ); + } + }); + } + + return undefined; +}; + +export const cancelMessage = (messageId: string) => { + if (window.cefQueryCancel) { + window.cefQueryCancel(messageId); + } +}; diff --git a/packages/jaeger-ui/src/api/digma/state.ts b/packages/jaeger-ui/src/api/digma/state.ts new file mode 100644 index 0000000000..8f94e5b03d --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/state.ts @@ -0,0 +1,24 @@ +import { actions } from "./actions"; +import { SetSpansWithResolvedLocationsData } from "./types"; + +export const state: { + pendingOperationsCount: number, + spansWithResolvedLocation: SetSpansWithResolvedLocationsData +} = { + pendingOperationsCount: 0, + spansWithResolvedLocation: {} +}; + +export const updateState = (action: string, payload: any) => { + switch(action) { + case (actions.GET_SPANS_WITH_RESOLVED_LOCATION): + state.pendingOperationsCount++; + break; + case (actions.SET_SPANS_WITH_RESOLVED_LOCATION): + state.spansWithResolvedLocation = payload; + state.pendingOperationsCount--; + break; + default: + } +} + diff --git a/packages/jaeger-ui/src/api/digma/types.ts b/packages/jaeger-ui/src/api/digma/types.ts new file mode 100644 index 0000000000..4f411b8a7b --- /dev/null +++ b/packages/jaeger-ui/src/api/digma/types.ts @@ -0,0 +1,20 @@ +export type ActionListener = (data: unknown) => void; + +export interface IDigmaIncomingMessageData { + type: "digma"; + action: string; + payload?: unknown; +} + +export interface IDigmaOutgoingMessageData { + action: string; + payload?: Record; +} + +export type DigmaMessageEvent = MessageEvent; + +interface ISpanInfo { + importance?: number +} + +export type SetSpansWithResolvedLocationsData = Record \ No newline at end of file diff --git a/packages/jaeger-ui/src/api/jaeger.js b/packages/jaeger-ui/src/api/jaeger.js index eb42982308..254f3fee9f 100644 --- a/packages/jaeger-ui/src/api/jaeger.js +++ b/packages/jaeger-ui/src/api/jaeger.js @@ -17,6 +17,7 @@ import moment from 'moment'; import queryString from 'query-string'; import prefixUrl from '../utils/prefix-url'; +import { isString } from '../utils/ts/typeGuards/isString'; // export for tests export function getMessageFromError(errData, status) { @@ -76,7 +77,7 @@ function getJSON(url, options = {}) { }); } -export const DEFAULT_API_ROOT = window.VS_CODE_SETTINGS.apiBaseUrl ? `${window.VS_CODE_SETTINGS.apiBaseUrl}/api/` : prefixUrl('/api/'); +export const DEFAULT_API_ROOT = isString(window.apiBaseUrl) ? `${window.apiBaseUrl}/api/` : prefixUrl('/api/'); export const ANALYTICS_ROOT = prefixUrl('/analytics/'); export const DEFAULT_DEPENDENCY_LOOKBACK = moment.duration(1, 'weeks').asMilliseconds(); diff --git a/packages/jaeger-ui/src/components/App/index.jsx b/packages/jaeger-ui/src/components/App/index.jsx index 9fecf249c3..a0731862a7 100644 --- a/packages/jaeger-ui/src/components/App/index.jsx +++ b/packages/jaeger-ui/src/components/App/index.jsx @@ -38,6 +38,7 @@ import JaegerAPI, { DEFAULT_API_ROOT } from '../../api/jaeger'; import configureStore from '../../utils/configure-store'; import processScripts from '../../utils/config/process-scripts'; import prefixUrl from '../../utils/prefix-url'; +import { isString } from '../../utils/ts/typeGuards/isString'; import '../common/vars.css'; import '../common/utils.css'; @@ -54,10 +55,10 @@ export default class JaegerUIApp extends Component { } render() { - // Navigate to URL provided by VS Code - if (window.VS_CODE_SETTINGS.startPath) { - const urlToNavigate = window.VS_CODE_SETTINGS.startPath; - window.VS_CODE_SETTINGS.startPath = ""; + // Navigate to URL provided on app start + if (isString(window.initialRoutePath) && window.initialRoutePath) { + const urlToNavigate = window.initialRoutePath; + window.initialRoutePath = ""; history.push(urlToNavigate); } diff --git a/packages/jaeger-ui/src/components/Monitor/EmptyState/index.tsx b/packages/jaeger-ui/src/components/Monitor/EmptyState/index.tsx index adc6af1087..781a5c1b8a 100644 --- a/packages/jaeger-ui/src/components/Monitor/EmptyState/index.tsx +++ b/packages/jaeger-ui/src/components/Monitor/EmptyState/index.tsx @@ -17,8 +17,11 @@ import { Row, Col, Button, Alert } from 'antd'; import './index.css'; import { MonitorEmptyStateConfig } from '../../../types/config'; import { getConfigValue } from '../../../utils/config/get-config'; +import { getStaticAssetPath } from '../../../utils/getStaticAssetPath'; import monitorImg from './media/monitor.png'; +const monitorImgUrl = getStaticAssetPath(monitorImg); + export default class MonitorATMEmptyState extends React.PureComponent { config: MonitorEmptyStateConfig; @@ -29,8 +32,6 @@ export default class MonitorATMEmptyState extends React.PureComponent { } render() { - const monitorImgUrl = window.VS_CODE_SETTINGS.staticPath ? new URL(monitorImg, window.VS_CODE_SETTINGS.staticPath).href : monitorImg; - return ( diff --git a/packages/jaeger-ui/src/components/SearchTracePage/index.jsx b/packages/jaeger-ui/src/components/SearchTracePage/index.jsx index e5cad4838d..9b8ab0f0c0 100644 --- a/packages/jaeger-ui/src/components/SearchTracePage/index.jsx +++ b/packages/jaeger-ui/src/components/SearchTracePage/index.jsx @@ -34,6 +34,7 @@ import { actions as traceDiffActions } from '../TraceDiff/duck'; import { fetchedState } from '../../constants'; import { sortTraces } from '../../model/search'; import { stripEmbeddedState } from '../../utils/embedded-url'; +import { getStaticAssetPath } from '../../utils/getStaticAssetPath'; import FileLoader from './FileLoader'; import './index.css'; @@ -41,8 +42,10 @@ import JaegerLogo from '../../img/jaeger-logo.svg'; const TabPane = Tabs.TabPane; +const logoUrl = getStaticAssetPath(JaegerLogo); + // Sanitize query params to filter out ones provided by VS Code -const sanitizeQueryParams = (params) => { +const sanitizeQueryParams = params => { const VS_CODE_PARAMS = [ "id", "origin", @@ -55,7 +58,7 @@ const sanitizeQueryParams = (params) => { const filteredParams = {}; - Object.keys(params).forEach((key) => { + Object.keys(params).forEach(key => { if (!VS_CODE_PARAMS.includes(key)) { filteredParams[key] = params[key] } @@ -121,7 +124,6 @@ export class SearchTracePageImpl extends Component { const hasTraceResults = traceResults && traceResults.length > 0; const showErrors = errors && !loadingTraces; const showLogo = isHomepage && !hasTraceResults && !loadingTraces && !errors; - const logoUrl = window.VS_CODE_SETTINGS.staticPath ? new URL(JaegerLogo, window.VS_CODE_SETTINGS.staticPath).href : JaegerLogo; return ( {!embedded && ( diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx index da4f93d23a..a9a4bc569c 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx @@ -23,17 +23,17 @@ import { formatDuration, ViewedBoundsFunctionType } from './utils'; import SpanTreeOffset from './SpanTreeOffset'; import SpanBar from './SpanBar'; import Ticks from './Ticks'; - +import { dispatcher } from '../../../api/digma/dispatcher'; +import { actions } from '../../../api/digma/actions'; +import { state as globalState } from '../../../api/digma/state'; +import { getStaticAssetPath } from '../../../utils/getStaticAssetPath'; import { TNil } from '../../../types'; import { Span } from '../../../types/trace'; import codeIcon from '../../../img/code.svg'; import './SpanBarRow.css'; - -type SpanInfo = { - importance?: number -} +import { SetSpansWithResolvedLocationsData } from '../../../api/digma/types'; type SpanBarRowProps = { className?: string; @@ -72,6 +72,8 @@ type SpanBarRowState = { importance?: number; } +const codeIconUrl = getStaticAssetPath(codeIcon); + /** * This was originally a stateless function, but changing to a PureComponent * reduced the render time of expanding a span row detail by ~50%. This is @@ -83,7 +85,7 @@ type SpanBarRowState = { export default class SpanBarRow extends React.PureComponent { constructor(props: SpanBarRowProps) { super(props); - const span = window.spansWithResolvedLocation[props.span.spanID]; + const span = globalState.spansWithResolvedLocation[props.span.spanID]; this.state = { hasResolvedLocation: Boolean(span), importance: span && span.importance, @@ -103,23 +105,20 @@ export default class SpanBarRow extends React.PureComponent }}) => { - const message = e.data; - if (message.command === "setSpansWithResolvedLocation") { - const span = message.data[this.props.span.spanID]; + updateSpanInfo = (data: unknown) => { + const span = (data as SetSpansWithResolvedLocationsData)[this.props.span.spanID]; this.setState({ hasResolvedLocation: Boolean(span), importance: span && span.importance }); - } } componentDidMount(): void { - window.addEventListener('message', this.updateSpanInfo); + dispatcher.addActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateSpanInfo); } componentWillUnmount(): void { - window.removeEventListener('message', this.updateSpanInfo); + dispatcher.removeActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateSpanInfo); } getImportanceAltText(importance?: number): string { @@ -172,7 +171,6 @@ export default class SpanBarRow extends React.PureComponent❗️ } - {this.state.hasResolvedLocation && } + {this.state.hasResolvedLocation && Code location available} {span.references && span.references.length > 1 && ( ', () => { props.logsToggle.mockReset(); props.logItemToggle.mockReset(); wrapper = shallow(); + jest.spyOn(window, 'sendMessageToDigma').mockImplementation(jest.fn()); }); it('renders without exploding', () => { diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx index 0bb2b5746e..10bf37c8a1 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx @@ -14,6 +14,7 @@ import React from 'react'; import { Divider, Tooltip } from 'antd'; +import { Link as RouterLink } from 'react-router-dom'; import AccordianKeyValues from './AccordianKeyValues'; import AccordianLogs from './AccordianLogs'; @@ -23,12 +24,15 @@ import DetailState from './DetailState'; import { formatDuration } from '../utils'; import CopyIcon from '../../../common/CopyIcon'; import LabeledList from '../../../common/LabeledList'; -import { Link as RouterLink } from 'react-router-dom'; import { TNil } from '../../../../types'; import { KeyValuePair, Link, Log, Span } from '../../../../types/trace'; import './index.css'; +import { actions } from '../../../../api/digma/actions'; +import { dispatcher } from '../../../../api/digma/dispatcher'; +import { state as globalState } from '../../../../api/digma/state'; +import { SetSpansWithResolvedLocationsData } from '../../../../api/digma/types'; type SpanDetailProps = { detailState: DetailState; @@ -44,11 +48,6 @@ type SpanDetailProps = { focusSpan: (uiFind: string) => void; }; -type SpanInfo = { - hasResolvedLocation: boolean, - importance?: number -} - type SpanDetailState = { hasResolvedLocation: boolean; importance?: number @@ -57,30 +56,27 @@ type SpanDetailState = { export default class SpanDetail extends React.Component { constructor(props: SpanDetailProps) { super(props); - const span = window.spansWithResolvedLocation[props.span.spanID]; + const span = globalState.spansWithResolvedLocation[props.span.spanID]; this.state = { hasResolvedLocation: Boolean(span), importance: span && span.importance, } } - updateSpanInfo = (e:{ data: { command: string, data: Record }}) => { - const message = e.data; - if (message.command === "setSpansWithResolvedLocation") { - const span = message.data[this.props.span.spanID]; - this.setState({ - hasResolvedLocation: Boolean(span), - importance: span && span.importance - }); - } - } - componentDidMount(): void { - window.addEventListener('message', this.updateSpanInfo); + dispatcher.addActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateSpanInfo); } componentWillUnmount(): void { - window.removeEventListener('message', this.updateSpanInfo); + dispatcher.removeActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateSpanInfo); + } + + updateSpanInfo = (data: unknown) => { + const span = (data as SetSpansWithResolvedLocationsData)[this.props.span.spanID]; + this.setState({ + hasResolvedLocation: Boolean(span), + importance: span && span.importance + }); } getImportanceAltText(importance?: number): string { @@ -96,13 +92,19 @@ export default class SpanDetail extends React.Component) => { e.preventDefault(); - const tag = this.props.span.tags.find((tag: any) => tag.key === "otel.library.name"); - if (tag && window.sendMessageToVSCode) { - window.sendMessageToVSCode({ - command: "goToSpanLocation", - data: { + const otelLibraryNameTag = this.props.span.tags.find((tag: any) => tag.key === "otel.library.name"); + const functionTag = this.props.span.tags.find((tag: any) => tag.key === "code.function"); + const namespaceTag = this.props.span.tags.find((tag: any) => tag.key === "code.namespace"); + + if (otelLibraryNameTag) { + window.sendMessageToDigma({ + action: actions.GO_TO_SPAN, + payload: { + id: this.props.span.spanID, name: this.props.span.operationName, - instrumentationLibrary: tag && tag.value + instrumentationLibrary: otelLibraryNameTag.value, + ...(functionTag ? {function: functionTag.value} : {}), + ...(namespaceTag ? {namespace: namespaceTag.value} : {}), } }); } @@ -149,7 +151,7 @@ export default class SpanDetail extends React.Component {this.state.hasResolvedLocation ? diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow/TimelineHeaderRow.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow/TimelineHeaderRow.tsx index 5d99585d23..36d5d87f89 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow/TimelineHeaderRow.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow/TimelineHeaderRow.tsx @@ -23,6 +23,9 @@ import { TUpdateViewRangeTimeFunction, IViewRangeTime, ViewRangeTimeUpdate } fro import './TimelineHeaderRow.css'; import LoadingIndicator from '../../../common/LoadingIndicator'; +import { dispatcher } from '../../../../api/digma/dispatcher'; +import { actions } from '../../../../api/digma/actions'; +import { state as globalState } from '../../../../api/digma/state'; type TimelineHeaderRowProps = { duration: number; @@ -46,26 +49,22 @@ export default class SpanDetailRow extends React.PureComponent }}) => { - const message = e.data; - if (message.command === "setSpansWithResolvedLocation") { - this.setState({ - isLoading: false - }); - } - } - componentDidMount(): void { - window.addEventListener('message', this.updateIsLoading); + dispatcher.addActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateIsLoading); } componentWillUnmount(): void { - window.removeEventListener('message', this.updateIsLoading); + dispatcher.removeActionListener(actions.SET_SPANS_WITH_RESOLVED_LOCATION, this.updateIsLoading); + } + + updateIsLoading = () => { + this.setState({ + isLoading: false + }); } render() { @@ -89,7 +88,7 @@ export default class SpanDetailRow extends React.PureComponent

Service & Operation

{this.state.isLoading &&
- + Loading data...
} ', () => { beforeAll(() => { filterSpansSpy.mockReturnValue(new Set()); + jest.spyOn(window, 'sendMessageToDigma').mockImplementation(jest.fn()); }); beforeEach(() => { diff --git a/packages/jaeger-ui/src/components/TracePage/index.tsx b/packages/jaeger-ui/src/components/TracePage/index.tsx index cb07d989a3..bcfe9b2feb 100644 --- a/packages/jaeger-ui/src/components/TracePage/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/index.tsx @@ -59,6 +59,7 @@ import TraceStatistics from './TraceStatistics/index'; import TraceSpanView from './TraceSpanView/index'; import TraceFlamegraph from './TraceFlamegraph/index'; import { TraceGraphConfig } from '../../types/config'; +import { actions } from '../../api/digma/actions'; import './index.css'; @@ -163,6 +164,11 @@ export class TracePageImpl extends React.PureComponent { } componentDidMount() { + const trace = this.props.trace; + if (trace && trace.data) { + this.getSpansWithResolvedLocations(trace.data); + } + this.ensureTraceFetched(); this.updateViewRangeTime(0, 1); /* istanbul ignore if */ @@ -185,29 +191,14 @@ export class TracePageImpl extends React.PureComponent { componentDidUpdate({ id: prevID, trace: prevTrace }: TProps) { const { id, trace } = this.props; - // Get all the trace spans and send it to VS Code extension - // to verify if they have resolved location if ( - window.sendMessageToVSCode && trace && - trace != prevTrace && + trace !== prevTrace && trace.data && trace.state && trace.state === fetchedState.DONE ) { - window.sendMessageToVSCode({ - command: "getTraceSpansLocations", - data: trace.data.spans.map(span => { - const tag = span.tags.find(tag => tag.key === "otel.library.name"); - - return { - id: span.spanID, - name: span.operationName, - instrumentationLibrary: tag && tag.value - }}).filter(span => span.instrumentationLibrary) - }); - - window.pendingOperationsCount++; + this.getSpansWithResolvedLocations(trace.data) } this._scrollManager.setTrace(trace && trace.data); @@ -233,6 +224,28 @@ export class TracePageImpl extends React.PureComponent { }); } + getSpansWithResolvedLocations(trace: Trace) { + // Get all the trace spans and send it Digma IDE plugin + // to verify if they have resolved location + window.sendMessageToDigma({ + action: actions.GET_SPANS_WITH_RESOLVED_LOCATION, + payload: { + spans: trace.spans.map(span => { + const otelLibraryNameTag = span.tags.find(tag => tag.key === "otel.library.name"); + const functionTag = span.tags.find(tag => tag.key === "code.function"); + const namespaceTag = span.tags.find(tag => tag.key === "code.namespace"); + + return { + id: span.spanID, + name: span.operationName, + instrumentationLibrary: otelLibraryNameTag && otelLibraryNameTag.value, + ...(functionTag ? {function: functionTag.value} : {}), + ...(namespaceTag ? {namespace: namespaceTag.value} : {}), + }}).filter(span => span.instrumentationLibrary) + } + }); + } + _adjustViewRange(startChange: number, endChange: number, trackSrc: string) { const [viewStart, viewEnd] = this.state.viewRange.time.current; let start = _clamp(viewStart + startChange, 0, 0.99); diff --git a/packages/jaeger-ui/src/index.jsx b/packages/jaeger-ui/src/index.jsx index 7d2e94fd24..af11e6b8ec 100644 --- a/packages/jaeger-ui/src/index.jsx +++ b/packages/jaeger-ui/src/index.jsx @@ -24,6 +24,9 @@ import { createRoot } from 'react-dom/client'; import JaegerUIApp from './components/App'; import { context as trackingContext } from './utils/tracking'; +import { cancelMessage, initializeDigmaMessageListener, sendMessage } from './api/digma'; +import { dispatcher } from './api/digma/dispatcher'; + // these need to go after the App import /* eslint-disable import/first */ import 'u-basscss/css/flexbox.css'; @@ -37,6 +40,11 @@ const UI_ROOT_ID = 'jaeger-ui-root'; const root = createRoot(document.getElementById(UI_ROOT_ID)); +initializeDigmaMessageListener(dispatcher); + +window.sendMessageToDigma = sendMessage; +window.cancelMessageToDigma = cancelMessage; + if (typeof trackingContext === 'object' && trackingContext !== null) { trackingContext.context(() => { root.render( diff --git a/packages/jaeger-ui/src/reducers/embedded.tsx b/packages/jaeger-ui/src/reducers/embedded.tsx index 45054eb30b..82955df942 100644 --- a/packages/jaeger-ui/src/reducers/embedded.tsx +++ b/packages/jaeger-ui/src/reducers/embedded.tsx @@ -21,8 +21,8 @@ export default function embeddedConfig(state: EmbeddedState | undefined) { if (state === undefined) { let search = _get(window, 'location.search'); - let params = new URLSearchParams(search); - if (window.VS_CODE_SETTINGS.embeddedMode && !params.get("uiEmbed")) { + const params = new URLSearchParams(search); + if (typeof window.embeddedMode === "boolean" && window.embeddedMode && !params.get("uiEmbed")) { params.set("uiEmbed", VERSION_0); search = params.toString(); } diff --git a/packages/jaeger-ui/src/utils/getStaticAssetPath.ts b/packages/jaeger-ui/src/utils/getStaticAssetPath.ts new file mode 100644 index 0000000000..d16c48344b --- /dev/null +++ b/packages/jaeger-ui/src/utils/getStaticAssetPath.ts @@ -0,0 +1,4 @@ +import { isString } from "./ts/typeGuards/isString"; + +export const getStaticAssetPath = (path: string) => + isString(window.staticPath) ? new URL(path, window.staticPath).href : path diff --git a/packages/jaeger-ui/src/utils/ts/typeGuards/isNull.ts b/packages/jaeger-ui/src/utils/ts/typeGuards/isNull.ts new file mode 100644 index 0000000000..b20e3a4d29 --- /dev/null +++ b/packages/jaeger-ui/src/utils/ts/typeGuards/isNull.ts @@ -0,0 +1 @@ +export const isNull = (x: unknown): x is null => x === null; diff --git a/packages/jaeger-ui/src/utils/ts/typeGuards/isObject.ts b/packages/jaeger-ui/src/utils/ts/typeGuards/isObject.ts new file mode 100644 index 0000000000..acdab276c6 --- /dev/null +++ b/packages/jaeger-ui/src/utils/ts/typeGuards/isObject.ts @@ -0,0 +1,4 @@ +import { isNull } from "./isNull"; + +export const isObject = (x: unknown): x is Record => + typeof x === "object" && !isNull(x); diff --git a/packages/jaeger-ui/src/utils/ts/typeGuards/isString.ts b/packages/jaeger-ui/src/utils/ts/typeGuards/isString.ts new file mode 100644 index 0000000000..e032004f9c --- /dev/null +++ b/packages/jaeger-ui/src/utils/ts/typeGuards/isString.ts @@ -0,0 +1 @@ +export const isString = (x: unknown): x is string => typeof x === "string"; diff --git a/packages/jaeger-ui/test/jest-per-test-setup.js b/packages/jaeger-ui/test/jest-per-test-setup.js index d6c587d48d..4895275897 100644 --- a/packages/jaeger-ui/test/jest-per-test-setup.js +++ b/packages/jaeger-ui/test/jest-per-test-setup.js @@ -38,6 +38,8 @@ window.getJaegerVersion = () => ({ buildDate: '', }); +window.sendMessageToDigma = () => {}; + global.__APP_ENVIRONMENT__ = 'test'; global.__REACT_APP_GA_DEBUG__ = ''; global.__REACT_APP_VSN_STATE__ = ''; diff --git a/packages/jaeger-ui/tsconfig.json b/packages/jaeger-ui/tsconfig.json index b1dd3c67ac..f7870445fa 100644 --- a/packages/jaeger-ui/tsconfig.json +++ b/packages/jaeger-ui/tsconfig.json @@ -13,5 +13,6 @@ // limitations under the License. { "extends": "../../tsconfig", - "include": ["src/**/*.tsx", "typings"] + "include": ["src", "typings"], + "exclude": ["src/**/*.d.ts"], } diff --git a/packages/jaeger-ui/typings/custom.d.ts b/packages/jaeger-ui/typings/custom.d.ts index 4586b3c5f5..292a3ff98b 100644 --- a/packages/jaeger-ui/typings/custom.d.ts +++ b/packages/jaeger-ui/typings/custom.d.ts @@ -23,15 +23,21 @@ declare interface Window { // For getting ui config getJaegerUiConfig?: () => Record; getJaegerVersion?: () => Record; - sendMessageToVSCode?: (message: { command: string, data: any }) => void; - spansWithResolvedLocation: Record; - pendingOperationsCount: number; - VS_CODE_SETTINGS: { - apiBaseUrl: string; - startPath: string; - staticPath: string; - embeddedMode: boolean; - } + sendMessageToVSCode?: (message) => void; + cefQuery?: (query: { + request: string; + persistent?: boolean; + onSuccess: (response) => void; + onFailure: (error_code, error_message) => void; + }) => string; + cefQueryCancel?: (request_id: string) => void; + sendMessageToDigma: (message) => string | undefined; + cancelMessageToDigma: (request_id: string) => void; + platform?: unknown; + apiBaseUrl?: unknown; + initialRoutePath?: unknown; + embeddedMode?: unknown; + staticPath?: unknown; } declare const __REACT_APP_GA_DEBUG__: string | undefined;