grafana/public/app/plugins/datasource/cloudwatch/datasource.ts
Erik Sundell 254577ba56
CloudWatch: Cross-account querying support (#59362)
* Lattice: Point to private prerelease of aws-sdk-go (#515)

* point to private prerelease of aws-sdk-go

* fix build issue

* Lattice: Adding a feature toggle (#549)

* Adding a feature toggle for lattice

* Change name of feature toggle

* Lattice: List accounts (#543)

* Separate layers

* Introduce testify/mock library

Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>

* point to version that includes metric api changes (#574)

* add accounts component (#575)

* Test refactor: remove unneeded clientFactoryMock (#581)

* Lattice: Add monitoring badge (#576)

* add monitoring badge

* fix tests

* solve conflict

* Lattice: Add dynamic label for account display name (#579)

* Build: Automatically sync lattice-main with OSS

* Lattice: Point to private prerelease of aws-sdk-go (#515)

* point to private prerelease of aws-sdk-go

* fix build issue

* Lattice: Adding a feature toggle (#549)

* Adding a feature toggle for lattice

* Change name of feature toggle

* Lattice: List accounts (#543)

* Separate layers

* Introduce testify/mock library

Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>

* point to version that includes metric api changes (#574)

* add accounts component (#575)

* Test refactor: remove unneeded clientFactoryMock (#581)

* Lattice: Add monitoring badge (#576)

* add monitoring badge

* fix tests

* solve conflict

* add account label

Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>
Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* fix import

* solve merge related problem

* add account info (#608)

* add back namespaces handler

* Lattice: Parse account id and return it to frontend (#609)

* parse account id and return to frontend

* fix route test

* only show badge when feature toggle is enabled (#615)

* Lattice: Refactor resource response type and return account (#613)

* refactor resource response type

* remove not used file.

* go lint

* fix tests

* remove commented code

* Lattice: Use account as input when listing metric names and dimensions (#611)

* use account in resource requests

* add account to response

* revert accountInfo to accountId

* PR feedback

* unit test account in list metrics response

* remove not used asserts

* don't assert on response that is not relevant to the test

* removed dupe test

* pr feedback

* rename request package (#626)

* Lattice: Move account component and add tooltip (#630)

* move accounts component to the top of metric stat editor

* add tooltip

* CloudWatch: add account to GetMetricData queries (#627)

* Add AccountId to metric stat query

* Lattice: Account variable support  (#625)

* add variable support in accounts component

* add account variable query type

* update variables

* interpolate variable before its sent to backend

* handle variable change in hooks

* remove not used import

* Update public/app/plugins/datasource/cloudwatch/components/Account.tsx

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* Update public/app/plugins/datasource/cloudwatch/hooks.ts

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* add one more unit test

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* cleanup (#629)

* Set account Id according to crossAccountQuerying feature flag in backend (#632)

* CloudWatch: Change spelling of feature-toggle (#634)

* Lattice Logs (#631)

* Lattice Logs

* Fixes after CR

* Lattice: Bug: fix dimension keys request (#644)

* fix dimension keys

* fix lint

* more lint

* CloudWatch: Add tests for QueryData with AccountId (#637)

* Update from breaking change (#645)

* Update from breaking change

* Remove extra interface and methods

Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>

* CloudWatch: Add business logic layer for getting log groups (#642)



Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* Lattice: Fix - unset account id in region change handler (#646)

* move reset of account to region change handler

* fix broken test

* Lattice: Add account id to metric stat query deep link (#656)

add account id to metric stat link

* CloudWatch: Add new log groups handler for cross-account querying (#643)

* Lattice: Add feature tracking (#660)

* add tracking for account id prescense in metrics query

* also check feature toggle

* fix broken test

* CloudWatch: Add route for DescribeLogGroups for cross-account querying (#647)

Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>

* Lattice: Handle account id default value (#662)

* make sure right type is returned

* set right default values

* Suggestions to lattice changes (#663)

* Change ListMetricsWithPageLimit response to slice of non-pointers

* Change GetAccountsForCurrentUserOrRole response to be not pointer

* Clean test Cleanup calls in test

* Remove CloudWatchAPI as part of mock

* Resolve conflicts

* Add Latest SDK (#672)

* add tooltip (#674)

* Docs: Add documentation for CloudWatch cross account querying (#676)

* wip docs

* change wordings

* add sections about metrics and logs

* change from monitoring to observability

* Update docs/sources/datasources/aws-cloudwatch/_index.md

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* Update docs/sources/datasources/aws-cloudwatch/query-editor/index.md

Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>

* Update docs/sources/datasources/aws-cloudwatch/query-editor/index.md

Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>

* Update docs/sources/datasources/aws-cloudwatch/query-editor/index.md

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

* Update docs/sources/datasources/aws-cloudwatch/query-editor/index.md

Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>

* apply pr feedback

* fix file name

* more pr feedback

* pr feedback

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>
Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>

* use latest version of the aws-sdk-go

* Fix tests' mock response type

* Remove change in Azure Monitor

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>
Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>
Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>
2022-11-28 12:39:12 +01:00

177 lines
6.3 KiB
TypeScript

import { cloneDeep, find, isEmpty } from 'lodash';
import { merge, Observable, of } from 'rxjs';
import {
DataFrame,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataSourceWithLogsContextSupport,
LoadingState,
LogRowModel,
ScopedVars,
} from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime';
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
import { RowContextOptions } from '../../../features/logs/components/LogRowContextProvider';
import { CloudWatchAnnotationSupport } from './annotationSupport';
import { CloudWatchAPI } from './api';
import { SQLCompletionItemProvider } from './cloudwatch-sql/completion/CompletionItemProvider';
import { isCloudWatchAnnotationQuery, isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from './guards';
import { CloudWatchLanguageProvider } from './language_provider';
import { MetricMathCompletionItemProvider } from './metric-math/completion/CompletionItemProvider';
import { CloudWatchAnnotationQueryRunner } from './query-runner/CloudWatchAnnotationQueryRunner';
import { CloudWatchLogsQueryRunner } from './query-runner/CloudWatchLogsQueryRunner';
import { CloudWatchMetricsQueryRunner } from './query-runner/CloudWatchMetricsQueryRunner';
import {
CloudWatchAnnotationQuery,
CloudWatchJsonData,
CloudWatchLogsQuery,
CloudWatchMetricsQuery,
CloudWatchQuery,
} from './types';
import { CloudWatchVariableSupport } from './variables';
export class CloudWatchDatasource
extends DataSourceWithBackend<CloudWatchQuery, CloudWatchJsonData>
implements DataSourceWithLogsContextSupport<CloudWatchLogsQuery>
{
defaultRegion?: string;
languageProvider: CloudWatchLanguageProvider;
sqlCompletionItemProvider: SQLCompletionItemProvider;
metricMathCompletionItemProvider: MetricMathCompletionItemProvider;
type = 'cloudwatch';
private metricsQueryRunner: CloudWatchMetricsQueryRunner;
private annotationQueryRunner: CloudWatchAnnotationQueryRunner;
logsQueryRunner: CloudWatchLogsQueryRunner;
api: CloudWatchAPI;
constructor(
instanceSettings: DataSourceInstanceSettings<CloudWatchJsonData>,
readonly templateSrv: TemplateSrv = getTemplateSrv(),
timeSrv: TimeSrv = getTimeSrv()
) {
super(instanceSettings);
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
this.api = new CloudWatchAPI(instanceSettings, templateSrv);
this.languageProvider = new CloudWatchLanguageProvider(this);
this.sqlCompletionItemProvider = new SQLCompletionItemProvider(this.api, this.templateSrv);
this.metricMathCompletionItemProvider = new MetricMathCompletionItemProvider(this.api, this.templateSrv);
this.metricsQueryRunner = new CloudWatchMetricsQueryRunner(instanceSettings, templateSrv);
this.logsQueryRunner = new CloudWatchLogsQueryRunner(instanceSettings, templateSrv, timeSrv);
this.annotationQueryRunner = new CloudWatchAnnotationQueryRunner(instanceSettings, templateSrv);
this.variables = new CloudWatchVariableSupport(this.api);
this.annotations = CloudWatchAnnotationSupport;
}
filterQuery(query: CloudWatchQuery) {
return query.hide !== true || (isCloudWatchMetricsQuery(query) && query.id !== '');
}
query(options: DataQueryRequest<CloudWatchQuery>): Observable<DataQueryResponse> {
options = cloneDeep(options);
let queries = options.targets.filter(this.filterQuery);
const logQueries: CloudWatchLogsQuery[] = [];
const metricsQueries: CloudWatchMetricsQuery[] = [];
const annotationQueries: CloudWatchAnnotationQuery[] = [];
queries.forEach((query) => {
if (isCloudWatchAnnotationQuery(query)) {
annotationQueries.push(query);
} else if (isCloudWatchLogsQuery(query)) {
logQueries.push(query);
} else {
metricsQueries.push(query);
}
});
const dataQueryResponses: Array<Observable<DataQueryResponse>> = [];
if (logQueries.length) {
dataQueryResponses.push(this.logsQueryRunner.handleLogQueries(logQueries, options));
}
if (metricsQueries.length) {
dataQueryResponses.push(this.metricsQueryRunner.handleMetricQueries(metricsQueries, options));
}
if (annotationQueries.length) {
dataQueryResponses.push(this.annotationQueryRunner.handleAnnotationQuery(annotationQueries, options));
}
// No valid targets, return the empty result to save a round trip.
if (isEmpty(dataQueryResponses)) {
return of({
data: [],
state: LoadingState.Done,
});
}
return merge(...dataQueryResponses);
}
interpolateVariablesInQueries(queries: CloudWatchQuery[], scopedVars: ScopedVars): CloudWatchQuery[] {
if (!queries.length) {
return queries;
}
return queries.map((query) => ({
...query,
region: this.metricsQueryRunner.replaceVariableAndDisplayWarningIfMulti(
this.getActualRegion(query.region),
scopedVars
),
...(isCloudWatchMetricsQuery(query) &&
this.metricsQueryRunner.interpolateMetricsQueryVariables(query, scopedVars)),
}));
}
getLogRowContext = async (
row: LogRowModel,
context?: RowContextOptions,
query?: CloudWatchLogsQuery
): Promise<{ data: DataFrame[] }> => {
return this.logsQueryRunner.getLogRowContext(row, context, query);
};
targetContainsTemplate(target: any) {
return (
this.templateSrv.containsTemplate(target.region) ||
this.templateSrv.containsTemplate(target.namespace) ||
this.templateSrv.containsTemplate(target.metricName) ||
this.templateSrv.containsTemplate(target.expression!) ||
target.logGroupNames?.some((logGroup: string) => this.templateSrv.containsTemplate(logGroup)) ||
find(target.dimensions, (v, k) => this.templateSrv.containsTemplate(k) || this.templateSrv.containsTemplate(v))
);
}
showContextToggle() {
return true;
}
getQueryDisplayText(query: CloudWatchQuery) {
if (query.queryMode === 'Logs') {
return query.expression ?? '';
} else {
return JSON.stringify(query);
}
}
// public
getVariables() {
return this.api.getVariables();
}
getActualRegion(region?: string) {
if (region === 'default' || region === undefined || region === '') {
return this.defaultRegion ?? '';
}
return region;
}
}