Skip to content

Commit

Permalink
Support trace-scoped external links similar to tag links
Browse files Browse the repository at this point in the history
Signed-off-by: Ruben Vargas <ruben.vp8510@gmail.com>
  • Loading branch information
rubenvp8510 committed Nov 12, 2019
1 parent 6e02cb0 commit ee0fd9d
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ import { getTraceName } from '../../../model/trace-viewer';
import { TNil } from '../../../types';
import { Trace } from '../../../types/trace';
import { formatDatetime, formatDuration } from '../../../utils/date';
import { getTraceLinks } from '../../../model/link-patterns';

import './TracePageHeader.css';
import ExternalLinks from '../../common/ExternalLinks';

type TracePageHeaderEmbedProps = {
canCollapse: boolean;
Expand Down Expand Up @@ -136,6 +138,8 @@ export function TracePageHeaderFn(props: TracePageHeaderEmbedProps & { forwarded
return null;
}

const links = getTraceLinks(trace);

const summaryItems =
!hideSummary &&
!slimView &&
Expand All @@ -159,6 +163,7 @@ export function TracePageHeaderFn(props: TracePageHeaderEmbedProps & { forwarded
<IoAndroidArrowBack />
</Link>
)}
{links && links.length > 0 && <ExternalLinks links={links} className="TracePageHeader--back" />}
{canCollapse ? (
<a
className="TracePageHeader--titleLink"
Expand Down
71 changes: 71 additions & 0 deletions packages/jaeger-ui/src/components/common/ExternalLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Dropdown, Icon, Menu } from 'antd';
import * as React from 'react';
import { Link } from '../../types/trace';

type ExternalLinksProps = {
links: Link[];
children?: React.ReactNode;
className?: string;
};

const LinkValue = (props: {
href: string;
title?: string;
children: React.ReactNode;
className?: string;
}) => (
<a
href={props.href}
title={props.title}
target="_blank"
rel="noopener noreferrer"
className={props.className}
>
{props.children} <Icon className="KeyValueTable--linkIcon" type="export" />
</a>
);

// export for testing
export const linkValueList = (links: Link[]) => (
<Menu>
{links.map(({ text, url }, index) => (
// `index` is necessary in the key because url can repeat
// eslint-disable-next-line react/no-array-index-key
<Menu.Item key={`${url}-${index}`}>
<LinkValue href={url}>{text}</LinkValue>
</Menu.Item>
))}
</Menu>
);

export default function ExternalLinks(props: ExternalLinksProps) {
const { links, className } = props;
if (links.length === 1) {
return (
<LinkValue href={links[0].url} title={links[0].text} className={className}>
{props.children}
</LinkValue>
);
}
return (
<Dropdown overlay={linkValueList(links)} placement="bottomRight" trigger={['click']}>
<a className={className}>
{props.children} <Icon className="KeyValueTable--linkIcon is-large" type="profile" />
</a>
</Dropdown>
);
}
50 changes: 49 additions & 1 deletion packages/jaeger-ui/src/model/link-patterns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import _uniq from 'lodash/uniq';
import { getConfigValue } from '../utils/config/get-config';
import { getParent } from './span';
import { TNil } from '../types';
import { Span, Link, KeyValuePair } from '../types/trace';
import { Span, Link, KeyValuePair, Trace } from '../types/trace';

const parameterRegExp = /#\{([^{}]*)\}/g;

Expand Down Expand Up @@ -139,6 +139,35 @@ function callTemplate(template: ProcessedTemplate, data: any) {
return template.template(data);
}

export function computeTraceLinks(linkPatterns: ProcessedLinkPattern[], trace: Trace) {
const result: { url: string; text: string }[] = [];
const validKeys = Object.keys(trace)
// @ts-ignore
.filter(key => typeof trace[key] === 'string' || typeof trace[key] === 'number');

linkPatterns
.filter(pattern => pattern.type('trace'))
.forEach(pattern => {
const parameterValues: Record<string, any> = {};
const allParameters = pattern.parameters.every(parameter => {
if (validKeys.some(name => name === parameter)) {
// @ts-ignore
parameterValues[parameter] = trace[parameter];
return true;
}
return false;
});
if (allParameters) {
result.push({
url: callTemplate(pattern.url, parameterValues),
text: callTemplate(pattern.text, parameterValues),
});
}
});

return result;
}

export function computeLinks(
linkPatterns: ProcessedLinkPattern[],
span: Span,
Expand Down Expand Up @@ -188,6 +217,20 @@ export function computeLinks(
return result;
}

export function createGetTraceLinks(linkPatterns: ProcessedLinkPattern[], cache: WeakMap<Trace, Link[]>) {
return (trace: Trace | undefined) => {
if (!trace || linkPatterns.length === 0) {
return [];
}
let result = cache.get(trace);
if (!result) {
result = computeTraceLinks(linkPatterns, trace);
cache.set(trace, result);
}
return result;
};
}

export function createGetLinks(linkPatterns: ProcessedLinkPattern[], cache: WeakMap<KeyValuePair, Link[]>) {
return (span: Span, items: KeyValuePair[], itemIndex: number) => {
if (linkPatterns.length === 0) {
Expand All @@ -207,3 +250,8 @@ export default createGetLinks(
(getConfigValue('linkPatterns') || []).map(processLinkPattern).filter(Boolean),
new WeakMap()
);

export const getTraceLinks = createGetTraceLinks(
(getConfigValue('linkPatterns') || []).map(processLinkPattern).filter(Boolean),
new WeakMap()
);

0 comments on commit ee0fd9d

Please sign in to comment.