mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: Fix logs insights deeplink (#59906)
* CloudWatch: Add arns to log insights button * start test * convert to function component * add tests Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>
This commit is contained in:
parent
239888f229
commit
db145774d3
@ -8,7 +8,7 @@ import { CloudWatchAnnotationQueryRunner } from '../query-runner/CloudWatchAnnot
|
||||
import { CloudWatchQuery } from '../types';
|
||||
|
||||
import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchDataSource';
|
||||
import { timeRange } from './timeRange';
|
||||
import { TimeRangeMock } from './timeRange';
|
||||
|
||||
export function setupMockedAnnotationQueryRunner({ variables }: { variables?: CustomVariableModel[] }) {
|
||||
let templateService = new TemplateSrv();
|
||||
@ -25,7 +25,7 @@ export function setupMockedAnnotationQueryRunner({ variables }: { variables?: Cu
|
||||
});
|
||||
|
||||
const request: DataQueryRequest<CloudWatchQuery> = {
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
rangeRaw: { from: '1483228800', to: '1483232400' },
|
||||
targets: [],
|
||||
requestId: '',
|
||||
@ -37,5 +37,5 @@ export function setupMockedAnnotationQueryRunner({ variables }: { variables?: Cu
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
return { runner, fetchMock, templateService, request, timeRange };
|
||||
return { runner, fetchMock, templateService, request, timeRange: TimeRangeMock };
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { CloudWatchMetricsQueryRunner } from '../query-runner/CloudWatchMetricsQ
|
||||
import { CloudWatchJsonData, CloudWatchQuery } from '../types';
|
||||
|
||||
import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchDataSource';
|
||||
import { timeRange } from './timeRange';
|
||||
import { TimeRangeMock } from './timeRange';
|
||||
|
||||
export function setupMockedMetricsQueryRunner({
|
||||
data = {
|
||||
@ -44,7 +44,7 @@ export function setupMockedMetricsQueryRunner({
|
||||
});
|
||||
|
||||
const request: DataQueryRequest<CloudWatchQuery> = {
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
rangeRaw: { from: '1483228800', to: '1483232400' },
|
||||
targets: [],
|
||||
requestId: '',
|
||||
@ -56,5 +56,5 @@ export function setupMockedMetricsQueryRunner({
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
return { runner, fetchMock, templateService, instanceSettings, request, timeRange };
|
||||
return { runner, fetchMock, templateService, instanceSettings, request, timeRange: TimeRangeMock };
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
import { DataQueryRequest } from '@grafana/data';
|
||||
|
||||
import { CloudWatchQuery } from '../types';
|
||||
|
||||
import { TimeRangeMock } from './timeRange';
|
||||
|
||||
export const RequestMock: DataQueryRequest<CloudWatchQuery> = {
|
||||
range: TimeRangeMock,
|
||||
rangeRaw: { from: TimeRangeMock.from, to: TimeRangeMock.to },
|
||||
targets: [],
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
startTime: 0,
|
||||
};
|
@ -91,4 +91,5 @@ export const validLogsQuery: CloudWatchLogsQuery = {
|
||||
id: '',
|
||||
region: 'us-east-2',
|
||||
refId: 'A',
|
||||
expression: `fields @timestamp, @message | sort @timestamp desc | limit 25`,
|
||||
};
|
||||
|
@ -3,4 +3,4 @@ import { dateTime, TimeRange } from '@grafana/data';
|
||||
const start = 1483196400 * 1000;
|
||||
const from = dateTime(start);
|
||||
const to = dateTime(start + 3600 * 1000);
|
||||
export const timeRange: TimeRange = { from, to, raw: { from, to } };
|
||||
export const TimeRangeMock: TimeRange = { from, to, raw: { from, to } };
|
||||
|
@ -0,0 +1,87 @@
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { LoadingState } from '@grafana/data';
|
||||
|
||||
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
|
||||
import { RequestMock } from '../__mocks__/Request';
|
||||
import { validLogsQuery } from '../__mocks__/queries';
|
||||
import { CloudWatchLogsQuery } from '../types';
|
||||
|
||||
import { CloudWatchLink } from './CloudWatchLink';
|
||||
|
||||
describe('CloudWatchLink', () => {
|
||||
it('generates a link with log group names', async () => {
|
||||
const ds = setupMockedDataSource();
|
||||
|
||||
const { rerender } = render(
|
||||
<CloudWatchLink query={validLogsQuery} datasource={ds.datasource} panelData={undefined} />
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('CloudWatch Logs Insights').closest('a')).toHaveAttribute('href', '');
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
rerender(
|
||||
<CloudWatchLink
|
||||
query={validLogsQuery}
|
||||
datasource={ds.datasource}
|
||||
panelData={{
|
||||
timeRange: RequestMock.range,
|
||||
request: RequestMock,
|
||||
state: LoadingState.Done,
|
||||
series: [],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('CloudWatch Logs Insights').closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
"https://us-east-2.console.aws.amazon.com/cloudwatch/home?region=us-east-2#logs-insights:queryDetail=~(end~'2016-12-31T16*3a00*3a00.000Z~start~'2016-12-31T15*3a00*3a00.000Z~timeType~'ABSOLUTE~tz~'UTC~editorString~'fields*20*40timestamp*2c*20*40message*20*7c*20sort*20*40timestamp*20desc*20*7c*20limit*2025~isLiveTail~false~source~(~'group-A~'group-B))"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('generates a link with log group names', async () => {
|
||||
const ds = setupMockedDataSource();
|
||||
const query: CloudWatchLogsQuery = {
|
||||
...validLogsQuery,
|
||||
logGroupNames: undefined,
|
||||
logGroups: [
|
||||
{ value: 'arn:aws:logs:us-east-1:111111111111:log-group:/aws/lambda/test1', text: '/aws/lambda/test1' },
|
||||
{ value: 'arn:aws:logs:us-east-1:111111111111:log-group:/aws/lambda/test2', text: '/aws/lambda/test2' },
|
||||
],
|
||||
};
|
||||
|
||||
const { rerender } = render(<CloudWatchLink query={query} datasource={ds.datasource} panelData={undefined} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('CloudWatch Logs Insights').closest('a')).toHaveAttribute('href', '');
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
rerender(
|
||||
<CloudWatchLink
|
||||
query={query}
|
||||
datasource={ds.datasource}
|
||||
panelData={{
|
||||
timeRange: RequestMock.range,
|
||||
request: RequestMock,
|
||||
state: LoadingState.Done,
|
||||
series: [],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('CloudWatch Logs Insights').closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
"https://us-east-2.console.aws.amazon.com/cloudwatch/home?region=us-east-2#logs-insights:queryDetail=~(end~'2016-12-31T16*3a00*3a00.000Z~start~'2016-12-31T15*3a00*3a00.000Z~timeType~'ABSOLUTE~tz~'UTC~editorString~'fields*20*40timestamp*2c*20*40message*20*7c*20sort*20*40timestamp*20desc*20*7c*20limit*2025~isLiveTail~false~source~(~'arn*3aaws*3alogs*3aus-east-1*3a111111111111*3alog-group*3a*2faws*2flambda*2ftest1~'arn*3aaws*3alogs*3aus-east-1*3a111111111111*3alog-group*3a*2faws*2flambda*2ftest2))"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,9 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { PanelData } from '@grafana/data';
|
||||
import { Icon } from '@grafana/ui';
|
||||
|
||||
import { encodeUrl, AwsUrl } from '../aws_url';
|
||||
import { AwsUrl, encodeUrl } from '../aws_url';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import { CloudWatchLogsQuery } from '../types';
|
||||
|
||||
@ -13,54 +14,39 @@ interface Props {
|
||||
datasource: CloudWatchDatasource;
|
||||
}
|
||||
|
||||
interface State {
|
||||
href: string;
|
||||
}
|
||||
export function CloudWatchLink({ panelData, query, datasource }: Props) {
|
||||
const [href, setHref] = useState('');
|
||||
const prevPanelData = usePrevious<PanelData | undefined>(panelData);
|
||||
|
||||
export default class CloudWatchLink extends Component<Props, State> {
|
||||
state: State = { href: '' };
|
||||
useEffect(() => {
|
||||
if (prevPanelData !== panelData && panelData?.request?.range) {
|
||||
const arns = (query.logGroups ?? [])
|
||||
.filter((group) => group?.value)
|
||||
.map((group) => (group.value ?? '').replace(/:\*$/, '')); // remove `:*` from end of arn
|
||||
const logGroupNames = query.logGroupNames;
|
||||
let sources = arns?.length ? arns : logGroupNames;
|
||||
|
||||
async componentDidUpdate(prevProps: Props) {
|
||||
const { panelData: panelDataNew } = this.props;
|
||||
const { panelData: panelDataOld } = prevProps;
|
||||
const range = panelData?.request?.range;
|
||||
const start = range.from.toISOString();
|
||||
const end = range.to.toISOString();
|
||||
|
||||
if (panelDataOld !== panelDataNew && panelDataNew?.request) {
|
||||
const href = this.getExternalLink();
|
||||
this.setState({ href });
|
||||
const urlProps: AwsUrl = {
|
||||
end,
|
||||
start,
|
||||
timeType: 'ABSOLUTE',
|
||||
tz: 'UTC',
|
||||
editorString: query.expression ?? '',
|
||||
isLiveTail: false,
|
||||
source: sources ?? [],
|
||||
};
|
||||
|
||||
setHref(encodeUrl(urlProps, datasource.api.getActualRegion(query.region)));
|
||||
}
|
||||
}
|
||||
}, [panelData, prevPanelData, datasource, query]);
|
||||
|
||||
getExternalLink(): string {
|
||||
const { query, panelData, datasource } = this.props;
|
||||
|
||||
const range = panelData?.request?.range;
|
||||
|
||||
if (!range) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const start = range.from.toISOString();
|
||||
const end = range.to.toISOString();
|
||||
|
||||
const urlProps: AwsUrl = {
|
||||
end,
|
||||
start,
|
||||
timeType: 'ABSOLUTE',
|
||||
tz: 'UTC',
|
||||
editorString: query.expression ?? '',
|
||||
isLiveTail: false,
|
||||
source: query.logGroupNames ?? [],
|
||||
};
|
||||
|
||||
return encodeUrl(urlProps, datasource.api.getActualRegion(query.region));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { href } = this.state;
|
||||
return (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer">
|
||||
<Icon name="share-alt" /> CloudWatch Logs Insights
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer">
|
||||
<Icon name="share-alt" /> CloudWatch Logs Insights
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { InlineFormLabel } from '@grafana/ui';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import { CloudWatchJsonData, CloudWatchLogsQuery, CloudWatchQuery } from '../types';
|
||||
|
||||
import CloudWatchLink from './CloudWatchLink';
|
||||
import { CloudWatchLink } from './CloudWatchLink';
|
||||
import CloudWatchLogsQueryField from './LogsQueryField';
|
||||
|
||||
type Props = QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData> & {
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from './__mocks__/CloudWatchDataSource';
|
||||
import { setupForLogs } from './__mocks__/logsTestContext';
|
||||
import { validLogsQuery, validMetricSearchBuilderQuery } from './__mocks__/queries';
|
||||
import { timeRange } from './__mocks__/timeRange';
|
||||
import { TimeRangeMock } from './__mocks__/timeRange';
|
||||
import { CloudWatchLogsQuery, CloudWatchMetricsQuery, CloudWatchQuery } from './types';
|
||||
|
||||
describe('datasource', () => {
|
||||
@ -43,7 +43,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
@ -83,7 +83,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
@ -106,7 +106,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
@ -152,7 +152,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
@ -188,7 +188,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
@ -232,7 +232,7 @@ describe('datasource', () => {
|
||||
requestId: '',
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
range: timeRange,
|
||||
range: TimeRangeMock,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: '',
|
||||
|
Loading…
Reference in New Issue
Block a user