CloudWatch Logs: Add selected region to autocomplete requests (#42194)

* Add region to get fields query

* Fix and add tests
This commit is contained in:
Andrej Ocenas 2021-11-26 13:33:34 +01:00 committed by GitHub
parent 879cdcd0c7
commit 802ffa3f03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 51 deletions

View File

@ -263,7 +263,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
};
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
const { datasource } = this.props;
const { datasource, query } = this.props;
const { selectedLogGroups } = this.state;
if (!datasource.languageProvider) {
@ -276,7 +276,12 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
return await cloudwatchLanguageProvider.provideCompletionItems(
{ 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,
}
);
};

View File

@ -125,6 +125,34 @@ describe('datasource', () => {
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() {

View File

@ -49,6 +49,7 @@ import {
TSDBResponse,
Dimensions,
MetricFindSuggestData,
CloudWatchLogsRequest,
} from './types';
import { CloudWatchLanguageProvider } from './language_provider';
import { VariableWithMultiSupport } from 'app/features/variables/types';
@ -547,7 +548,7 @@ export class CloudWatchDatasource
makeLogActionRequest(
subtype: LogAction,
queryParams: Array<GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest>,
queryParams: CloudWatchLogsRequest[],
options: {
scopedVars?: ScopedVars;
makeReplacements?: boolean;
@ -562,8 +563,7 @@ export class CloudWatchDatasource
const requestParams = {
from: range.from.valueOf().toString(),
to: range.to.valueOf().toString(),
queries: queryParams.map(
(param: GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest) => ({
queries: queryParams.map((param: CloudWatchLogsRequest) => ({
refId: (param as StartQueryRequest).refId || 'A',
intervalMs: 1, // dummy
maxDataPoints: 1, // dummy
@ -571,13 +571,11 @@ export class CloudWatchDatasource
type: 'logAction',
subtype: subtype,
...param,
})
),
})),
};
if (options.makeReplacements) {
requestParams.queries.forEach(
(query: GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest) => {
requestParams.queries.forEach((query: CloudWatchLogsRequest) => {
const fieldsToReplace: Array<
keyof (GetLogEventsRequest & StartQueryRequest & DescribeLogGroupsRequest & GetLogGroupFieldsRequest)
> = ['queryString', 'logGroupNames', 'logGroupName', 'logGroupNamePrefix'];
@ -601,8 +599,7 @@ export class CloudWatchDatasource
anyQuery.region = this.replace(anyQuery.region, options.scopedVars, true, 'region');
anyQuery.region = this.getActualRegion(anyQuery.region);
}
}
);
});
}
const resultsToDataFrames = (val: any): DataFrame[] => toDataQueryResponse(val).data || [];

View File

@ -127,7 +127,7 @@ function getProvideCompletionItems(query: string): Promise<TypeaheadOutput> {
{
value,
} as any,
{ logGroupNames: ['logGroup1'] }
{ logGroupNames: ['logGroup1'], region: 'custom' }
);
}

View File

@ -23,6 +23,7 @@ type TypeaheadContext = {
history?: CloudWatchHistoryItem[];
absoluteRange?: AbsoluteTimeRange;
logGroupNames?: string[];
region: string;
};
export class CloudWatchLanguageProvider extends LanguageProvider {
@ -104,7 +105,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
}
if (isInsideFunctionParenthesis(curToken)) {
return await this.getFieldCompletionItems(context?.logGroupNames ?? []);
return await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
}
if (isAfterKeyword('by', curToken)) {
@ -133,7 +134,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
}
| undefined;
private fetchFields = async (logGroups: string[]): Promise<string[]> => {
private fetchFields = async (logGroups: string[], region: string): Promise<string[]> => {
if (
this.fetchedFieldsCache &&
Date.now() - this.fetchedFieldsCache.time < 30 * 1000 &&
@ -143,7 +144,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
}
const results = await Promise.all(
logGroups.map((logGroup) => this.datasource.getLogGroupFields({ logGroupName: logGroup }))
logGroups.map((logGroup) => this.datasource.getLogGroupFields({ logGroupName: logGroup, region }))
);
const fields = [
@ -162,7 +163,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
};
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[] = [
{
searchFunctionType: SearchFunctionType.Prefix,
@ -190,7 +191,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
if (queryCommand === 'parse') {
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)) {
const typeaheadOutput = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
const typeaheadOutput = await this.getFieldCompletionItems(
context?.logGroupNames ?? [],
context?.region || 'default'
);
typeaheadOutput.suggestions.push(...this.getFieldAndFilterFunctionCompletionItems().suggestions);
return typeaheadOutput;
@ -224,7 +228,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
}
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();
sugg.suggestions.push(...boolFuncs.suggestions);
return sugg;
@ -238,7 +242,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
context?: TypeaheadContext
): Promise<TypeaheadOutput> {
if (isFirstArgument) {
return await this.getFieldCompletionItems(context?.logGroupNames ?? []);
return await this.getFieldCompletionItems(context?.logGroupNames ?? [], context?.region || 'default');
} else if (isTokenType(prevNonWhitespaceToken(curToken), 'field-name')) {
// suggest sort options
return {
@ -261,7 +265,10 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
}
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();
fieldsSuggestions.suggestions.push(...comparisonSuggestions.suggestions);
return fieldsSuggestions;
@ -313,8 +320,8 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
};
};
private getFieldCompletionItems = async (logGroups: string[]): Promise<TypeaheadOutput> => {
const fields = await this.fetchFields(logGroups);
private getFieldCompletionItems = async (logGroups: string[], region: string): Promise<TypeaheadOutput> => {
const fields = await this.fetchFields(logGroups, region);
return {
suggestions: [

View File

@ -164,7 +164,7 @@ describe('CloudWatchDatasource', () => {
'container-insights-prometheus-demo',
];
const logGroups = await ds.describeLogGroups({});
const logGroups = await ds.describeLogGroups({ region: 'default' });
expect(logGroups).toEqual(expectedLogGroups);
});

View File

@ -122,6 +122,12 @@ export interface QueryStatistics {
export type QueryStatus = 'Scheduled' | 'Running' | 'Complete' | 'Failed' | 'Cancelled' | string;
export type CloudWatchLogsRequest =
| GetLogEventsRequest
| StartQueryRequest
| DescribeLogGroupsRequest
| GetLogGroupFieldsRequest;
export interface GetLogEventsRequest {
/**
* The name of the log group.
@ -182,7 +188,7 @@ export interface DescribeLogGroupsRequest {
*/
limit?: number;
refId?: string;
region?: string;
region: string;
}
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.
*/
time?: number;
region: string;
}
export interface LogGroupField {