mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch Logs: Add selected region to autocomplete requests (#42194)
* Add region to get fields query * Fix and add tests
This commit is contained in:
parent
879cdcd0c7
commit
802ffa3f03
@ -263,7 +263,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
|||||||
};
|
};
|
||||||
|
|
||||||
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
|
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
|
||||||
const { datasource } = this.props;
|
const { datasource, query } = this.props;
|
||||||
const { selectedLogGroups } = this.state;
|
const { selectedLogGroups } = this.state;
|
||||||
|
|
||||||
if (!datasource.languageProvider) {
|
if (!datasource.languageProvider) {
|
||||||
@ -276,7 +276,12 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
|||||||
|
|
||||||
return await cloudwatchLanguageProvider.provideCompletionItems(
|
return await cloudwatchLanguageProvider.provideCompletionItems(
|
||||||
{ text, value, prefix, wrapperClasses, labelKey, editor },
|
{ text, value, prefix, wrapperClasses, labelKey, editor },
|
||||||
{ history, absoluteRange, logGroupNames: selectedLogGroups.map((logGroup) => logGroup.value!) }
|
{
|
||||||
|
history,
|
||||||
|
absoluteRange,
|
||||||
|
logGroupNames: selectedLogGroups.map((logGroup) => logGroup.value!),
|
||||||
|
region: query.region,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,6 +125,34 @@ describe('datasource', () => {
|
|||||||
expect(fetchMock.mock.calls[1][0].data.queries[0].region).toBe('eu-east');
|
expect(fetchMock.mock.calls[1][0].data.queries[0].region).toBe('eu-east');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getLogGroupFields', () => {
|
||||||
|
it('passes region correctly', async () => {
|
||||||
|
const { datasource, fetchMock } = setupMockedDataSource();
|
||||||
|
fetchMock.mockReturnValueOnce(
|
||||||
|
of({
|
||||||
|
data: {
|
||||||
|
results: {
|
||||||
|
A: {
|
||||||
|
frames: [
|
||||||
|
dataFrameToJSON(
|
||||||
|
new MutableDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'key', values: [] },
|
||||||
|
{ name: 'val', values: [] },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await datasource.getLogGroupFields({ region: 'us-west-1', logGroupName: 'test' });
|
||||||
|
expect(fetchMock.mock.calls[0][0].data.queries[0].region).toBe('us-west-1');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupForLogs() {
|
function setupForLogs() {
|
||||||
|
@ -49,6 +49,7 @@ import {
|
|||||||
TSDBResponse,
|
TSDBResponse,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
MetricFindSuggestData,
|
MetricFindSuggestData,
|
||||||
|
CloudWatchLogsRequest,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { CloudWatchLanguageProvider } from './language_provider';
|
import { CloudWatchLanguageProvider } from './language_provider';
|
||||||
import { VariableWithMultiSupport } from 'app/features/variables/types';
|
import { VariableWithMultiSupport } from 'app/features/variables/types';
|
||||||
@ -547,7 +548,7 @@ export class CloudWatchDatasource
|
|||||||
|
|
||||||
makeLogActionRequest(
|
makeLogActionRequest(
|
||||||
subtype: LogAction,
|
subtype: LogAction,
|
||||||
queryParams: Array<GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest>,
|
queryParams: CloudWatchLogsRequest[],
|
||||||
options: {
|
options: {
|
||||||
scopedVars?: ScopedVars;
|
scopedVars?: ScopedVars;
|
||||||
makeReplacements?: boolean;
|
makeReplacements?: boolean;
|
||||||
@ -562,47 +563,43 @@ export class CloudWatchDatasource
|
|||||||
const requestParams = {
|
const requestParams = {
|
||||||
from: range.from.valueOf().toString(),
|
from: range.from.valueOf().toString(),
|
||||||
to: range.to.valueOf().toString(),
|
to: range.to.valueOf().toString(),
|
||||||
queries: queryParams.map(
|
queries: queryParams.map((param: CloudWatchLogsRequest) => ({
|
||||||
(param: GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest) => ({
|
refId: (param as StartQueryRequest).refId || 'A',
|
||||||
refId: (param as StartQueryRequest).refId || 'A',
|
intervalMs: 1, // dummy
|
||||||
intervalMs: 1, // dummy
|
maxDataPoints: 1, // dummy
|
||||||
maxDataPoints: 1, // dummy
|
datasource: this.getRef(),
|
||||||
datasource: this.getRef(),
|
type: 'logAction',
|
||||||
type: 'logAction',
|
subtype: subtype,
|
||||||
subtype: subtype,
|
...param,
|
||||||
...param,
|
})),
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.makeReplacements) {
|
if (options.makeReplacements) {
|
||||||
requestParams.queries.forEach(
|
requestParams.queries.forEach((query: CloudWatchLogsRequest) => {
|
||||||
(query: GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest) => {
|
const fieldsToReplace: Array<
|
||||||
const fieldsToReplace: Array<
|
keyof (GetLogEventsRequest & StartQueryRequest & DescribeLogGroupsRequest & GetLogGroupFieldsRequest)
|
||||||
keyof (GetLogEventsRequest & StartQueryRequest & DescribeLogGroupsRequest & GetLogGroupFieldsRequest)
|
> = ['queryString', 'logGroupNames', 'logGroupName', 'logGroupNamePrefix'];
|
||||||
> = ['queryString', 'logGroupNames', 'logGroupName', 'logGroupNamePrefix'];
|
|
||||||
|
|
||||||
const anyQuery: any = query;
|
const anyQuery: any = query;
|
||||||
for (const fieldName of fieldsToReplace) {
|
for (const fieldName of fieldsToReplace) {
|
||||||
if (query.hasOwnProperty(fieldName)) {
|
if (query.hasOwnProperty(fieldName)) {
|
||||||
if (Array.isArray(anyQuery[fieldName])) {
|
if (Array.isArray(anyQuery[fieldName])) {
|
||||||
anyQuery[fieldName] = anyQuery[fieldName].map((val: string) =>
|
anyQuery[fieldName] = anyQuery[fieldName].map((val: string) =>
|
||||||
this.replace(val, options.scopedVars, true, fieldName)
|
this.replace(val, options.scopedVars, true, fieldName)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
anyQuery[fieldName] = this.replace(anyQuery[fieldName], options.scopedVars, true, fieldName);
|
anyQuery[fieldName] = this.replace(anyQuery[fieldName], options.scopedVars, true, fieldName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: seems to be some sort of bug that we don't really send region with all queries. This means
|
|
||||||
// if you select different than default region in editor you will get results for autocomplete from wrong
|
|
||||||
// region.
|
|
||||||
if (anyQuery.region) {
|
|
||||||
anyQuery.region = this.replace(anyQuery.region, options.scopedVars, true, 'region');
|
|
||||||
anyQuery.region = this.getActualRegion(anyQuery.region);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
// TODO: seems to be some sort of bug that we don't really send region with all queries. This means
|
||||||
|
// if you select different than default region in editor you will get results for autocomplete from wrong
|
||||||
|
// region.
|
||||||
|
if (anyQuery.region) {
|
||||||
|
anyQuery.region = this.replace(anyQuery.region, options.scopedVars, true, 'region');
|
||||||
|
anyQuery.region = this.getActualRegion(anyQuery.region);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultsToDataFrames = (val: any): DataFrame[] => toDataQueryResponse(val).data || [];
|
const resultsToDataFrames = (val: any): DataFrame[] => toDataQueryResponse(val).data || [];
|
||||||
|
@ -127,7 +127,7 @@ function getProvideCompletionItems(query: string): Promise<TypeaheadOutput> {
|
|||||||
{
|
{
|
||||||
value,
|
value,
|
||||||
} as any,
|
} as any,
|
||||||
{ logGroupNames: ['logGroup1'] }
|
{ logGroupNames: ['logGroup1'], region: 'custom' }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ type TypeaheadContext = {
|
|||||||
history?: CloudWatchHistoryItem[];
|
history?: CloudWatchHistoryItem[];
|
||||||
absoluteRange?: AbsoluteTimeRange;
|
absoluteRange?: AbsoluteTimeRange;
|
||||||
logGroupNames?: string[];
|
logGroupNames?: string[];
|
||||||
|
region: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CloudWatchLanguageProvider extends LanguageProvider {
|
export class CloudWatchLanguageProvider extends LanguageProvider {
|
||||||
@ -104,7 +105,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isInsideFunctionParenthesis(curToken)) {
|
if (isInsideFunctionParenthesis(curToken)) {
|
||||||
return await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
return await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAfterKeyword('by', curToken)) {
|
if (isAfterKeyword('by', curToken)) {
|
||||||
@ -133,7 +134,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
private fetchFields = async (logGroups: string[]): Promise<string[]> => {
|
private fetchFields = async (logGroups: string[], region: string): Promise<string[]> => {
|
||||||
if (
|
if (
|
||||||
this.fetchedFieldsCache &&
|
this.fetchedFieldsCache &&
|
||||||
Date.now() - this.fetchedFieldsCache.time < 30 * 1000 &&
|
Date.now() - this.fetchedFieldsCache.time < 30 * 1000 &&
|
||||||
@ -143,7 +144,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
logGroups.map((logGroup) => this.datasource.getLogGroupFields({ logGroupName: logGroup }))
|
logGroups.map((logGroup) => this.datasource.getLogGroupFields({ logGroupName: logGroup, region }))
|
||||||
);
|
);
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
@ -162,7 +163,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private handleKeyword = async (context?: TypeaheadContext): Promise<TypeaheadOutput> => {
|
private handleKeyword = async (context?: TypeaheadContext): Promise<TypeaheadOutput> => {
|
||||||
const suggs = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
const suggs = await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
|
||||||
const functionSuggestions: CompletionItemGroup[] = [
|
const functionSuggestions: CompletionItemGroup[] = [
|
||||||
{
|
{
|
||||||
searchFunctionType: SearchFunctionType.Prefix,
|
searchFunctionType: SearchFunctionType.Prefix,
|
||||||
@ -190,7 +191,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
|
|
||||||
if (queryCommand === 'parse') {
|
if (queryCommand === 'parse') {
|
||||||
if (currentTokenIsFirstArg) {
|
if (currentTokenIsFirstArg) {
|
||||||
return await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
return await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +208,10 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (['display', 'fields'].includes(queryCommand)) {
|
if (['display', 'fields'].includes(queryCommand)) {
|
||||||
const typeaheadOutput = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
const typeaheadOutput = await this.getFieldCompletionItems(
|
||||||
|
context?.logGroupNames ?? [],
|
||||||
|
context?.region || 'default'
|
||||||
|
);
|
||||||
typeaheadOutput.suggestions.push(...this.getFieldAndFilterFunctionCompletionItems().suggestions);
|
typeaheadOutput.suggestions.push(...this.getFieldAndFilterFunctionCompletionItems().suggestions);
|
||||||
|
|
||||||
return typeaheadOutput;
|
return typeaheadOutput;
|
||||||
@ -224,7 +228,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (queryCommand === 'filter' && currentTokenIsFirstArg) {
|
if (queryCommand === 'filter' && currentTokenIsFirstArg) {
|
||||||
const sugg = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
const sugg = await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
|
||||||
const boolFuncs = this.getBoolFuncCompletionItems();
|
const boolFuncs = this.getBoolFuncCompletionItems();
|
||||||
sugg.suggestions.push(...boolFuncs.suggestions);
|
sugg.suggestions.push(...boolFuncs.suggestions);
|
||||||
return sugg;
|
return sugg;
|
||||||
@ -238,7 +242,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
context?: TypeaheadContext
|
context?: TypeaheadContext
|
||||||
): Promise<TypeaheadOutput> {
|
): Promise<TypeaheadOutput> {
|
||||||
if (isFirstArgument) {
|
if (isFirstArgument) {
|
||||||
return await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
return await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
|
||||||
} else if (isTokenType(prevNonWhitespaceToken(curToken), 'field-name')) {
|
} else if (isTokenType(prevNonWhitespaceToken(curToken), 'field-name')) {
|
||||||
// suggest sort options
|
// suggest sort options
|
||||||
return {
|
return {
|
||||||
@ -261,7 +265,10 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleComparison = async (context?: TypeaheadContext) => {
|
private handleComparison = async (context?: TypeaheadContext) => {
|
||||||
const fieldsSuggestions = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
const fieldsSuggestions = await this.getFieldCompletionItems(
|
||||||
|
context?.logGroupNames ?? [],
|
||||||
|
context?.region || 'default'
|
||||||
|
);
|
||||||
const comparisonSuggestions = this.getComparisonCompletionItems();
|
const comparisonSuggestions = this.getComparisonCompletionItems();
|
||||||
fieldsSuggestions.suggestions.push(...comparisonSuggestions.suggestions);
|
fieldsSuggestions.suggestions.push(...comparisonSuggestions.suggestions);
|
||||||
return fieldsSuggestions;
|
return fieldsSuggestions;
|
||||||
@ -313,8 +320,8 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
private getFieldCompletionItems = async (logGroups: string[]): Promise<TypeaheadOutput> => {
|
private getFieldCompletionItems = async (logGroups: string[], region: string): Promise<TypeaheadOutput> => {
|
||||||
const fields = await this.fetchFields(logGroups);
|
const fields = await this.fetchFields(logGroups, region);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
suggestions: [
|
suggestions: [
|
||||||
|
@ -164,7 +164,7 @@ describe('CloudWatchDatasource', () => {
|
|||||||
'container-insights-prometheus-demo',
|
'container-insights-prometheus-demo',
|
||||||
];
|
];
|
||||||
|
|
||||||
const logGroups = await ds.describeLogGroups({});
|
const logGroups = await ds.describeLogGroups({ region: 'default' });
|
||||||
|
|
||||||
expect(logGroups).toEqual(expectedLogGroups);
|
expect(logGroups).toEqual(expectedLogGroups);
|
||||||
});
|
});
|
||||||
|
@ -122,6 +122,12 @@ export interface QueryStatistics {
|
|||||||
|
|
||||||
export type QueryStatus = 'Scheduled' | 'Running' | 'Complete' | 'Failed' | 'Cancelled' | string;
|
export type QueryStatus = 'Scheduled' | 'Running' | 'Complete' | 'Failed' | 'Cancelled' | string;
|
||||||
|
|
||||||
|
export type CloudWatchLogsRequest =
|
||||||
|
| GetLogEventsRequest
|
||||||
|
| StartQueryRequest
|
||||||
|
| DescribeLogGroupsRequest
|
||||||
|
| GetLogGroupFieldsRequest;
|
||||||
|
|
||||||
export interface GetLogEventsRequest {
|
export interface GetLogEventsRequest {
|
||||||
/**
|
/**
|
||||||
* The name of the log group.
|
* The name of the log group.
|
||||||
@ -182,7 +188,7 @@ export interface DescribeLogGroupsRequest {
|
|||||||
*/
|
*/
|
||||||
limit?: number;
|
limit?: number;
|
||||||
refId?: string;
|
refId?: string;
|
||||||
region?: string;
|
region: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TSDBResponse<T = any> {
|
export interface TSDBResponse<T = any> {
|
||||||
@ -256,6 +262,7 @@ export interface GetLogGroupFieldsRequest {
|
|||||||
* The time to set as the center of the query. If you specify time, the 8 minutes before and 8 minutes after this time are searched. If you omit time, the past 15 minutes are queried. The time value is specified as epoch time, the number of seconds since January 1, 1970, 00:00:00 UTC.
|
* The time to set as the center of the query. If you specify time, the 8 minutes before and 8 minutes after this time are searched. If you omit time, the past 15 minutes are queried. The time value is specified as epoch time, the number of seconds since January 1, 1970, 00:00:00 UTC.
|
||||||
*/
|
*/
|
||||||
time?: number;
|
time?: number;
|
||||||
|
region: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LogGroupField {
|
export interface LogGroupField {
|
||||||
|
Loading…
Reference in New Issue
Block a user