diff --git a/package.json b/package.json index 1a7947b3609..8c8e7f89c44 100644 --- a/package.json +++ b/package.json @@ -195,7 +195,7 @@ "redux-mock-store": "1.5.4", "regexp-replace-loader": "1.0.1", "rimraf": "3.0.1", - "rxjs-spy": "^7.5.1", + "rxjs-spy": "8.0.0", "sass": "1.27.0", "sass-lint": "1.12.1", "sass-loader": "8.0.2", @@ -297,7 +297,7 @@ "regenerator-runtime": "0.13.3", "reselect": "4.0.0", "rst2html": "github:thoward/rst2html#990cb89", - "rxjs": "6.6.3", + "rxjs": "7.3.0", "search-query-parser": "1.5.4", "semver": "^7.1.3", "slate": "0.47.8", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index ea54e37ad91..8ee15ddbf8a 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -28,7 +28,7 @@ "eventemitter3": "4.0.7", "lodash": "4.17.21", "marked": "2.0.1", - "rxjs": "6.6.3", + "rxjs": "7.3.0", "xss": "1.0.6" }, "devDependencies": { diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index a0cbd81cfd0..594a5019e7c 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -4,6 +4,11 @@ // but you still might need to select it for testing, // in that case please add the attribute data-test-id={selector} in the component and // prefix your selector string with 'data-test-id' so that when create the selectors we know to search for it on the right attribute +/** + * Selectors grouped/defined in Components + * + * @alpha + */ export const Components = { TimePicker: { openButton: 'data-testid TimePicker Open Button', diff --git a/packages/grafana-e2e-selectors/src/selectors/index.ts b/packages/grafana-e2e-selectors/src/selectors/index.ts index 6404693167d..7f82a08b6ce 100644 --- a/packages/grafana-e2e-selectors/src/selectors/index.ts +++ b/packages/grafana-e2e-selectors/src/selectors/index.ts @@ -2,7 +2,19 @@ import { Pages } from './pages'; import { Components } from './components'; import { E2ESelectors } from '../types'; +/** + * Exposes selectors in package for easy use in e2e tests and in production code + * + * @alpha + */ export const selectors: { pages: E2ESelectors; components: E2ESelectors } = { pages: Pages, components: Components, }; + +/** + * Exposes Pages, Component selectors and E2ESelectors type in package for easy use in e2e tests and in production code + * + * @alpha + */ +export { Pages, Components, E2ESelectors }; diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index a99217651a6..26d0d1c98a2 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -1,5 +1,10 @@ import { Components } from './components'; +/** + * Selectors grouped/defined in Pages + * + * @alpha + */ export const Pages = { Login: { url: '/login', diff --git a/packages/grafana-e2e-selectors/src/types/selectors.ts b/packages/grafana-e2e-selectors/src/types/selectors.ts index f19c55f5628..6b9e9a02294 100644 --- a/packages/grafana-e2e-selectors/src/types/selectors.ts +++ b/packages/grafana-e2e-selectors/src/types/selectors.ts @@ -1,5 +1,23 @@ +/** + * A string selector + * + * @alpha + */ + export type StringSelector = string; + +/** + * A function selector with an argument + * + * @alpha + */ export type FunctionSelector = (id: string) => string; + +/** + * A function selector without argument + * + * @alpha + */ export type CssSelector = () => string; /** diff --git a/packages/grafana-runtime/src/services/backendSrv.ts b/packages/grafana-runtime/src/services/backendSrv.ts index 8f39f2551c1..d80f8a89417 100644 --- a/packages/grafana-runtime/src/services/backendSrv.ts +++ b/packages/grafana-runtime/src/services/backendSrv.ts @@ -147,7 +147,7 @@ export interface BackendSrv { /** * @deprecated Use the fetch function instead. If you prefer to work with a promise - * call the toPromise() function on the Observable returned by fetch. + * wrap the Observable returned by fetch with the lastValueFrom function. */ request(options: BackendSrvRequest): Promise; diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 5e0fdd36603..5d19a05316d 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -1,4 +1,14 @@ -import { from, merge, MonoTypeOperatorFunction, Observable, of, Subject, Subscription, throwError } from 'rxjs'; +import { + from, + lastValueFrom, + merge, + MonoTypeOperatorFunction, + Observable, + of, + Subject, + Subscription, + throwError, +} from 'rxjs'; import { catchError, filter, map, mergeMap, retryWhen, share, takeUntil, tap, throwIfEmpty } from 'rxjs/operators'; import { fromFetch } from 'rxjs/fetch'; import { v4 as uuidv4 } from 'uuid'; @@ -62,9 +72,7 @@ export class BackendSrv implements BackendService { } async request(options: BackendSrvRequest): Promise { - return this.fetch(options) - .pipe(map((response: FetchResponse) => response.data)) - .toPromise(); + return await lastValueFrom(this.fetch(options).pipe(map((response: FetchResponse) => response.data))); } fetch(options: BackendSrvRequest): Observable> { @@ -134,7 +142,7 @@ export class BackendSrv implements BackendService { } async datasourceRequest(options: BackendSrvRequest): Promise { - return this.fetch(options).toPromise(); + return lastValueFrom(this.fetch(options)); } private parseRequestOptions(options: BackendSrvRequest): BackendSrvRequest { diff --git a/public/app/features/alerting/unified/api/alertmanager.ts b/public/app/features/alerting/unified/api/alertmanager.ts index c75cdf491a0..58015cd5e6f 100644 --- a/public/app/features/alerting/unified/api/alertmanager.ts +++ b/public/app/features/alerting/unified/api/alertmanager.ts @@ -1,5 +1,7 @@ +import { lastValueFrom } from 'rxjs'; import { urlUtil } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; + import { AlertmanagerAlert, AlertManagerCortexConfig, @@ -17,13 +19,13 @@ import { getDatasourceAPIId, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasour // "grafana" for grafana-managed, otherwise a datasource name export async function fetchAlertManagerConfig(alertManagerSourceName: string): Promise { try { - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/alerts`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return { template_files: result.data.template_files ?? {}, alertmanager_config: result.data.alertmanager_config ?? {}, @@ -47,36 +49,36 @@ export async function updateAlertManagerConfig( alertManagerSourceName: string, config: AlertManagerCortexConfig ): Promise { - await getBackendSrv() - .fetch({ + await lastValueFrom( + getBackendSrv().fetch({ method: 'POST', url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/alerts`, data: config, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); } export async function deleteAlertManagerConfig(alertManagerSourceName: string): Promise { - await getBackendSrv() - .fetch({ + await lastValueFrom( + getBackendSrv().fetch({ method: 'DELETE', url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/alerts`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); } export async function fetchSilences(alertManagerSourceName: string): Promise { - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/api/v2/silences`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return result.data; } @@ -85,15 +87,15 @@ export async function createOrUpdateSilence( alertmanagerSourceName: string, payload: SilenceCreatePayload ): Promise { - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertmanagerSourceName)}/api/v2/silences`, data: payload, showErrorAlert: false, showSuccessAlert: false, method: 'POST', }) - .toPromise(); + ); return result.data; } @@ -121,39 +123,39 @@ export async function fetchAlerts( ) .join('&') || ''; - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertmanagerSourceName)}/api/v2/alerts` + (filters ? '?' + filters : ''), showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return result.data; } export async function fetchAlertGroups(alertmanagerSourceName: string): Promise { - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertmanagerSourceName)}/api/v2/alerts/groups`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return result.data; } export async function fetchStatus(alertManagerSourceName: string): Promise { - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/api/v2/status`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return result.data; } @@ -162,15 +164,15 @@ export async function testReceivers(alertManagerSourceName: string, receivers: R const data: TestReceiversPayload = { receivers, }; - const result = await getBackendSrv() - .fetch({ + const result = await lastValueFrom( + getBackendSrv().fetch({ method: 'POST', data, url: `/api/alertmanager/${getDatasourceAPIId(alertManagerSourceName)}/config/api/v1/receivers/test`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); // api returns 207 if one or more receivers has failed test. Collect errors in this case if (result.status === 207) { diff --git a/public/app/features/alerting/unified/api/prometheus.ts b/public/app/features/alerting/unified/api/prometheus.ts index fb16081bb08..900435a568f 100644 --- a/public/app/features/alerting/unified/api/prometheus.ts +++ b/public/app/features/alerting/unified/api/prometheus.ts @@ -1,22 +1,23 @@ +import { lastValueFrom } from 'rxjs'; import { getBackendSrv } from '@grafana/runtime'; + import { RuleNamespace } from 'app/types/unified-alerting'; import { PromRulesResponse } from 'app/types/unified-alerting-dto'; import { getDatasourceAPIId } from '../utils/datasource'; export async function fetchRules(dataSourceName: string): Promise { - const response = await getBackendSrv() - .fetch({ + const response = await lastValueFrom( + getBackendSrv().fetch({ url: `/api/prometheus/${getDatasourceAPIId(dataSourceName)}/api/v1/rules`, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise() - .catch((e) => { - if ('status' in e && e.status === 404) { - throw new Error('404 from rule state endpoint. Perhaps ruler API is not enabled?'); - } - throw e; - }); + ).catch((e) => { + if ('status' in e && e.status === 404) { + throw new Error('404 from rule state endpoint. Perhaps ruler API is not enabled?'); + } + throw e; + }); const nsMap: { [key: string]: RuleNamespace } = {}; response.data.data.groups.forEach((group) => { diff --git a/public/app/features/alerting/unified/api/ruler.ts b/public/app/features/alerting/unified/api/ruler.ts index 2508dcb5601..2b73aac6562 100644 --- a/public/app/features/alerting/unified/api/ruler.ts +++ b/public/app/features/alerting/unified/api/ruler.ts @@ -1,6 +1,8 @@ +import { lastValueFrom } from 'rxjs'; +import { getBackendSrv } from '@grafana/runtime'; + import { PostableRulerRuleGroupDTO, RulerRuleGroupDTO, RulerRulesConfigDTO } from 'app/types/unified-alerting-dto'; import { getDatasourceAPIId } from '../utils/datasource'; -import { getBackendSrv } from '@grafana/runtime'; import { RULER_NOT_SUPPORTED_MSG } from '../utils/constants'; // upsert a rule group. use this to update rules @@ -9,15 +11,15 @@ export async function setRulerRuleGroup( namespace: string, group: PostableRulerRuleGroupDTO ): Promise { - await await getBackendSrv() - .fetch({ + await lastValueFrom( + getBackendSrv().fetch({ method: 'POST', url: `/api/ruler/${getDatasourceAPIId(dataSourceName)}/api/v1/rules/${encodeURIComponent(namespace)}`, data: group, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); } // fetch all ruler rule namespaces and included groups @@ -51,8 +53,8 @@ export async function fetchRulerRulesGroup( } export async function deleteRulerRulesGroup(dataSourceName: string, namespace: string, groupName: string) { - return getBackendSrv() - .fetch({ + return await lastValueFrom( + getBackendSrv().fetch({ url: `/api/ruler/${getDatasourceAPIId(dataSourceName)}/api/v1/rules/${encodeURIComponent( namespace )}/${encodeURIComponent(groupName)}`, @@ -60,19 +62,19 @@ export async function deleteRulerRulesGroup(dataSourceName: string, namespace: s showSuccessAlert: false, showErrorAlert: false, }) - .toPromise(); + ); } // false in case ruler is not supported. this is weird, but we'll work on it async function rulerGetRequest(url: string, empty: T): Promise { try { - const response = await getBackendSrv() - .fetch({ + const response = await lastValueFrom( + getBackendSrv().fetch({ url, showErrorAlert: false, showSuccessAlert: false, }) - .toPromise(); + ); return response.data; } catch (e) { if (e?.status === 404) { diff --git a/public/app/features/annotations/annotations_srv.ts b/public/app/features/annotations/annotations_srv.ts index a10d1b36674..7792b4eb6f1 100644 --- a/public/app/features/annotations/annotations_srv.ts +++ b/public/app/features/annotations/annotations_srv.ts @@ -1,11 +1,6 @@ -// Libaries import { cloneDeep, flattenDeep } from 'lodash'; -// Components -import coreModule from 'app/core/core_module'; -// Utils & Services -import { dedupAnnotations } from './events_processing'; -// Types -import { DashboardModel } from '../dashboard/state'; +import { lastValueFrom, Observable, of } from 'rxjs'; +import { map, mergeMap } from 'rxjs/operators'; import { AnnotationEvent, AppEvents, @@ -16,10 +11,12 @@ import { ScopedVars, } from '@grafana/data'; import { getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; + +import coreModule from 'app/core/core_module'; +import { dedupAnnotations } from './events_processing'; +import { DashboardModel } from '../dashboard/state'; import { appEvents } from 'app/core/core'; import { getTimeSrv } from '../dashboard/services/TimeSrv'; -import { Observable, of } from 'rxjs'; -import { map, mergeMap } from 'rxjs/operators'; import { AnnotationQueryOptions, AnnotationQueryResponse } from './types'; import { standardAnnotationSupport } from './standardAnnotationSupport'; import { runRequest } from '../query/state/runRequest'; @@ -154,11 +151,9 @@ export class AnnotationsSrv { }); } // Note: future annotation lifecycle will use observables directly - return executeAnnotationQuery(options, datasource, annotation) - .toPromise() - .then((res) => { - return res.events ?? []; - }); + return lastValueFrom(executeAnnotationQuery(options, datasource, annotation)).then((res) => { + return res.events ?? []; + }); }) .then((results) => { // store response in annotation object if this is a snapshot call diff --git a/public/app/features/annotations/components/StandardAnnotationQueryEditor.tsx b/public/app/features/annotations/components/StandardAnnotationQueryEditor.tsx index 08547f7f50c..d69d3ba056f 100644 --- a/public/app/features/annotations/components/StandardAnnotationQueryEditor.tsx +++ b/public/app/features/annotations/components/StandardAnnotationQueryEditor.tsx @@ -1,11 +1,11 @@ import React, { PureComponent } from 'react'; - +import { lastValueFrom } from 'rxjs'; +import { css, cx } from '@emotion/css'; import { AnnotationEventMappings, AnnotationQuery, DataQuery, DataSourceApi, LoadingState } from '@grafana/data'; import { Button, Icon, IconName, Spinner } from '@grafana/ui'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { css, cx } from '@emotion/css'; import { standardAnnotationSupport } from '../standardAnnotationSupport'; import { executeAnnotationQuery } from '../annotations_srv'; import { PanelModel } from 'app/features/dashboard/state'; @@ -64,15 +64,17 @@ export default class StandardAnnotationQueryEditor extends PureComponent { async function getDataSourceUsingUidOrId(uid: string): Promise { // Try first with uid api try { - const byUid = await getBackendSrv() - .fetch({ + const byUid = await lastValueFrom( + getBackendSrv().fetch({ method: 'GET', url: `/api/datasources/uid/${uid}`, showErrorAlert: false, }) - .toPromise(); + ); if (byUid.ok) { return byUid.data; @@ -158,13 +159,13 @@ async function getDataSourceUsingUidOrId(uid: string): Promise({ + const response = await lastValueFrom( + getBackendSrv().fetch({ method: 'GET', url: `/api/datasources/${id}`, showErrorAlert: false, }) - .toPromise(); + ); // Not ideal to do a full page reload here but so tricky to handle this // otherwise We can update the location using react router, but need to diff --git a/public/app/features/explore/utils/decorators.test.ts b/public/app/features/explore/utils/decorators.test.ts index 186563994bf..550431de25b 100644 --- a/public/app/features/explore/utils/decorators.test.ts +++ b/public/app/features/explore/utils/decorators.test.ts @@ -1,10 +1,5 @@ import { DrawStyle, StackingMode } from '@grafana/ui'; - -jest.mock('@grafana/data/src/datetime/formatter', () => ({ - dateTimeFormat: () => 'format() jest mocked', - dateTimeFormatTimeAgo: (ts: any) => 'fromNow() jest mocked', -})); - +import { lastValueFrom } from 'rxjs'; import { ArrayVector, DataFrame, @@ -26,6 +21,11 @@ import { describe } from '../../../../test/lib/common'; import { ExplorePanelData } from 'app/types'; import TableModel from 'app/core/table_model'; +jest.mock('@grafana/data/src/datetime/formatter', () => ({ + dateTimeFormat: () => 'format() jest mocked', + dateTimeFormatTimeAgo: (ts: any) => 'fromNow() jest mocked', +})); + const getTestContext = () => { const timeSeries = toDataFrame({ name: 'A-series', @@ -184,7 +184,7 @@ describe('decorateWithTableResult', () => { it('should process table type dataFrame', async () => { const { table, emptyTable } = getTestContext(); const panelData = createExplorePanelData({ tableFrames: [table, emptyTable] }); - const panelResult = await decorateWithTableResult(panelData).toPromise(); + const panelResult = await lastValueFrom(decorateWithTableResult(panelData)); let theResult = panelResult.tableResult; @@ -241,7 +241,7 @@ describe('decorateWithTableResult', () => { }), ]; const panelData = createExplorePanelData({ tableFrames }); - const panelResult = await decorateWithTableResult(panelData).toPromise(); + const panelResult = await lastValueFrom(decorateWithTableResult(panelData)); const result = panelResult.tableResult; expect(result?.fields[0].name).toBe('Time'); @@ -264,20 +264,20 @@ describe('decorateWithTableResult', () => { tableFrames[0].fields[0].display = displayFunctionMock; const panelData = createExplorePanelData({ tableFrames }); - const panelResult = await decorateWithTableResult(panelData).toPromise(); + const panelResult = await lastValueFrom(decorateWithTableResult(panelData)); expect(panelResult.tableResult?.fields[0].display).toBe(displayFunctionMock); }); it('should return null when passed empty array', async () => { const panelData = createExplorePanelData({ tableFrames: [] }); - const panelResult = await decorateWithTableResult(panelData).toPromise(); + const panelResult = await lastValueFrom(decorateWithTableResult(panelData)); expect(panelResult.tableResult).toBeNull(); }); it('returns data if panelData has error', async () => { const { table, emptyTable } = getTestContext(); const panelData = createExplorePanelData({ error: {}, tableFrames: [table, emptyTable] }); - const panelResult = await decorateWithTableResult(panelData).toPromise(); + const panelResult = await lastValueFrom(decorateWithTableResult(panelData)); expect(panelResult.tableResult).not.toBeNull(); }); }); diff --git a/public/app/plugins/datasource/alertmanager/DataSource.ts b/public/app/plugins/datasource/alertmanager/DataSource.ts index 85594d2713f..775d77c6cf8 100644 --- a/public/app/plugins/datasource/alertmanager/DataSource.ts +++ b/public/app/plugins/datasource/alertmanager/DataSource.ts @@ -1,6 +1,6 @@ +import { lastValueFrom, Observable, of } from 'rxjs'; import { DataQuery, DataQueryResponse, DataSourceApi, DataSourceInstanceSettings } from '@grafana/data'; import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; -import { Observable, of } from 'rxjs'; export type AlertManagerQuery = { query: string; @@ -35,7 +35,7 @@ export class AlertManagerDatasource extends DataSourceApi { options.headers!.Authorization = this.instanceSettings.basicAuth; } - return getBackendSrv().fetch(options).toPromise(); + return lastValueFrom(getBackendSrv().fetch(options)); } async testDatasource() { diff --git a/public/app/plugins/datasource/cloud-monitoring/api.ts b/public/app/plugins/datasource/cloud-monitoring/api.ts index 87ba4fdff0a..7cb1db80889 100644 --- a/public/app/plugins/datasource/cloud-monitoring/api.ts +++ b/public/app/plugins/datasource/cloud-monitoring/api.ts @@ -1,4 +1,4 @@ -import { Observable, of } from 'rxjs'; +import { lastValueFrom, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { SelectableValue } from '@grafana/data'; import { FetchResponse, getBackendSrv } from '@grafana/runtime'; @@ -38,33 +38,34 @@ export default class Api { return Promise.resolve(this.cache[path]); } - return getBackendSrv() - .fetch>({ - url: baseUrl + path, - method: 'GET', - }) - .pipe( - map((response) => { - const responsePropName = path.match(/([^\/]*)\/*$/)![1].split('?')[0]; - let res = []; - if (response && response.data && response.data[responsePropName]) { - res = response.data[responsePropName].map(responseMap); - } - - if (useCache) { - this.cache[path] = res; - } - - return res; - }), - catchError((error) => { - appEvents.emit(CoreEvents.dsRequestError, { - error: { data: { error: formatCloudMonitoringError(error) } }, - }); - return of([]); + return lastValueFrom( + getBackendSrv() + .fetch>({ + url: baseUrl + path, + method: 'GET', }) - ) - .toPromise(); + .pipe( + map((response) => { + const responsePropName = path.match(/([^\/]*)\/*$/)![1].split('?')[0]; + let res = []; + if (response && response.data && response.data[responsePropName]) { + res = response.data[responsePropName].map(responseMap); + } + + if (useCache) { + this.cache[path] = res; + } + + return res; + }), + catchError((error) => { + appEvents.emit(CoreEvents.dsRequestError, { + error: { data: { error: formatCloudMonitoringError(error) } }, + }); + return of([]); + }) + ) + ); } post(data: Record): Observable> { @@ -76,11 +77,11 @@ export default class Api { } test(projectName: string) { - return getBackendSrv() - .fetch({ + return lastValueFrom( + getBackendSrv().fetch({ url: `${this.baseUrl}${projectName}/metricDescriptors`, method: 'GET', }) - .toPromise(); + ); } } diff --git a/public/app/plugins/datasource/cloud-monitoring/datasource.ts b/public/app/plugins/datasource/cloud-monitoring/datasource.ts index f8b058cd005..17699efbab6 100644 --- a/public/app/plugins/datasource/cloud-monitoring/datasource.ts +++ b/public/app/plugins/datasource/cloud-monitoring/datasource.ts @@ -1,21 +1,20 @@ import { chunk, flatten, isString } from 'lodash'; - +import { from, lastValueFrom, Observable, of, throwError } from 'rxjs'; +import { catchError, map, mergeMap } from 'rxjs/operators'; import { DataQueryRequest, + DataQueryResponse, DataSourceInstanceSettings, ScopedVars, SelectableValue, - DataQueryResponse, } from '@grafana/data'; +import { DataSourceWithBackend, toDataQueryResponse } from '@grafana/runtime'; + import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; - -import { CloudMonitoringOptions, CloudMonitoringQuery, Filter, MetricDescriptor, QueryType, EditorMode } from './types'; +import { CloudMonitoringOptions, CloudMonitoringQuery, EditorMode, Filter, MetricDescriptor, QueryType } from './types'; import API from './api'; -import { DataSourceWithBackend, toDataQueryResponse } from '@grafana/runtime'; import { CloudMonitoringVariableSupport } from './variables'; -import { catchError, map, mergeMap } from 'rxjs/operators'; -import { from, Observable, of, throwError } from 'rxjs'; export default class CloudMonitoringDatasource extends DataSourceWithBackend< CloudMonitoringQuery, @@ -72,35 +71,36 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< }, ]; - return this.api - .post({ - from: options.range.from.valueOf().toString(), - to: options.range.to.valueOf().toString(), - queries, - }) - .pipe( - map(({ data }) => { - const dataQueryResponse = toDataQueryResponse({ - data: data, - }); - const df: any = []; - if (dataQueryResponse.data.length !== 0) { - for (let i = 0; i < dataQueryResponse.data.length; i++) { - for (let j = 0; j < dataQueryResponse.data[i].fields[0].values.length; j++) { - df.push({ - annotation: annotation, - time: Date.parse(dataQueryResponse.data[i].fields[0].values.get(j)), - title: dataQueryResponse.data[i].fields[1].values.get(j), - tags: [], - text: dataQueryResponse.data[i].fields[3].values.get(j), - }); + return lastValueFrom( + this.api + .post({ + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries, + }) + .pipe( + map(({ data }) => { + const dataQueryResponse = toDataQueryResponse({ + data: data, + }); + const df: any = []; + if (dataQueryResponse.data.length !== 0) { + for (let i = 0; i < dataQueryResponse.data.length; i++) { + for (let j = 0; j < dataQueryResponse.data[i].fields[0].values.length; j++) { + df.push({ + annotation: annotation, + time: Date.parse(dataQueryResponse.data[i].fields[0].values.get(j)), + title: dataQueryResponse.data[i].fields[1].values.get(j), + tags: [], + text: dataQueryResponse.data[i].fields[3].values.get(j), + }); + } } } - } - return df; - }) - ) - .toPromise(); + return df; + }) + ) + ); } applyTemplateVariables( @@ -150,11 +150,11 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< const queries = options.targets; if (!queries.length) { - return of({ results: [] }).toPromise(); + return lastValueFrom(of({ results: [] })); } - return from(this.ensureGCEDefaultProject()) - .pipe( + return lastValueFrom( + from(this.ensureGCEDefaultProject()).pipe( mergeMap(() => { return this.api.post({ from: options.range.from.valueOf().toString(), @@ -170,7 +170,7 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< return result && result.meta ? result.meta.labels : {}; }) ) - .toPromise(); + ); } async testDatasource() { @@ -206,27 +206,28 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< } async getGCEDefaultProject() { - return this.api - .post({ - queries: [ - { - refId: 'getGCEDefaultProject', - type: 'getGCEDefaultProject', - datasourceId: this.id, - }, - ], - }) - .pipe( - map(({ data }) => { - return data && data.results && data.results.getGCEDefaultProject && data.results.getGCEDefaultProject.meta - ? data.results.getGCEDefaultProject.meta.defaultProject - : ''; - }), - catchError((err) => { - return throwError(err.data.error); + return lastValueFrom( + this.api + .post({ + queries: [ + { + refId: 'getGCEDefaultProject', + type: 'getGCEDefaultProject', + datasourceId: this.id, + }, + ], }) - ) - .toPromise(); + .pipe( + map(({ data }) => { + return data && data.results && data.results.getGCEDefaultProject && data.results.getGCEDefaultProject.meta + ? data.results.getGCEDefaultProject.meta.defaultProject + : ''; + }), + catchError((err) => { + return throwError(err.data.error); + }) + ) + ); } getDefaultProject(): string { diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index c683efca21a..65044dfcaa2 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -1,7 +1,7 @@ import React from 'react'; import angular from 'angular'; import { find, isEmpty, isString, set } from 'lodash'; -import { merge, Observable, of, throwError, zip } from 'rxjs'; +import { lastValueFrom, merge, Observable, of, throwError, zip } from 'rxjs'; import { catchError, concatMap, @@ -15,7 +15,7 @@ import { takeWhile, tap, } from 'rxjs/operators'; -import { getBackendSrv, getGrafanaLiveSrv, toDataQueryResponse, DataSourceWithBackend } from '@grafana/runtime'; +import { DataSourceWithBackend, getBackendSrv, getGrafanaLiveSrv, toDataQueryResponse } from '@grafana/runtime'; import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider'; import { DataFrame, @@ -464,14 +464,14 @@ export class CloudWatchDatasource extends DataSourceWithBackend { - const dataFrames = await this.makeLogActionRequest('DescribeLogGroups', [params]).toPromise(); + const dataFrames = await lastValueFrom(this.makeLogActionRequest('DescribeLogGroups', [params])); const logGroupNames = dataFrames[0]?.fields[0]?.values.toArray() ?? []; return logGroupNames; } async getLogGroupFields(params: GetLogGroupFieldsRequest): Promise { - const dataFrames = await this.makeLogActionRequest('GetLogGroupFields', [params]).toPromise(); + const dataFrames = await lastValueFrom(this.makeLogActionRequest('GetLogGroupFields', [params])); const fieldNames = dataFrames[0].fields[0].values.toArray(); const fieldPercentages = dataFrames[0].fields[1].values.toArray(); @@ -516,7 +516,7 @@ export class CloudWatchDatasource extends DataSourceWithBackend> { const range = this.timeSrv.timeRange(); - return this.awsRequest(DS_QUERY_ENDPOINT, { - from: range.from.valueOf().toString(), - to: range.to.valueOf().toString(), - queries: [ - { - refId: 'metricFindQuery', - intervalMs: 1, // dummy - maxDataPoints: 1, // dummy - datasourceId: this.id, - type: 'metricFindQuery', - subtype: subtype, - ...parameters, - }, - ], - }) - .pipe( + return lastValueFrom( + this.awsRequest(DS_QUERY_ENDPOINT, { + from: range.from.valueOf().toString(), + to: range.to.valueOf().toString(), + queries: [ + { + refId: 'metricFindQuery', + intervalMs: 1, // dummy + maxDataPoints: 1, // dummy + datasourceId: this.id, + type: 'metricFindQuery', + subtype: subtype, + ...parameters, + }, + ], + }).pipe( map((r) => { return this.transformSuggestDataFromDataframes(r); }) ) - .toPromise(); + ); } makeLogActionRequest( @@ -843,19 +843,19 @@ export class CloudWatchDatasource extends DataSourceWithBackend { const frames = toDataQueryResponse({ data: r }).data as DataFrame[]; const table = toLegacyResponseData(frames[0]) as TableData; @@ -868,7 +868,7 @@ export class CloudWatchDatasource extends DataSourceWithBackend; @@ -50,7 +46,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider { } request = (url: string, params?: any): Promise => { - return this.datasource.awsRequest(url, params).toPromise(); + return lastValueFrom(this.datasource.awsRequest(url, params)); }; start = () => { diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts index b8382e62105..cfca84f0ab6 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts @@ -1,4 +1,4 @@ -import { interval, of, throwError } from 'rxjs'; +import { interval, lastValueFrom, of, throwError } from 'rxjs'; import { DataFrame, DataQueryErrorType, @@ -29,12 +29,6 @@ import { CustomVariableModel, initialVariableModelState, VariableHide } from '.. import * as rxjsUtils from '../utils/rxjs/increasingInterval'; import { createFetchResponse } from 'test/helpers/createFetchResponse'; -jest.mock('rxjs/operators', () => { - const operators = jest.requireActual('rxjs/operators'); - operators.delay = jest.fn(() => (s: any) => s); - return operators; -}); - jest.mock('@grafana/runtime', () => ({ ...((jest.requireActual('@grafana/runtime') as unknown) as object), getBackendSrv: () => backendSrv, @@ -272,7 +266,9 @@ describe('CloudWatchDatasource', () => { } }); - const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise(); + const myResponse = await lastValueFrom( + ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]) + ); const expectedData = [ { @@ -313,7 +309,9 @@ describe('CloudWatchDatasource', () => { } }); - const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise(); + const myResponse = await lastValueFrom( + ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]) + ); expect(myResponse).toEqual({ data: [fakeFrames[fakeFrames.length - 1]], key: 'test-key', @@ -336,7 +334,9 @@ describe('CloudWatchDatasource', () => { } }); - const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise(); + const myResponse = await lastValueFrom( + ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]) + ); expect(myResponse).toEqual({ data: [fakeFrames[2]], diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index 03fd28cec11..d429e6d1729 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -1,4 +1,8 @@ -import { cloneDeep, find, isNumber, isObject, isString, first as _first, map as _map } from 'lodash'; +import { cloneDeep, find, first as _first, isNumber, isObject, isString, map as _map } from 'lodash'; +import { generate, lastValueFrom, Observable, of, throwError } from 'rxjs'; +import { catchError, first, map, mergeMap, skipWhile, throwIfEmpty } from 'rxjs/operators'; +import { gte, lt, satisfies } from 'semver'; +import { BackendSrvRequest, getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; import { DataFrame, DataLink, @@ -22,7 +26,6 @@ import { ElasticResponse } from './elastic_response'; import { IndexPattern } from './index_pattern'; import { ElasticQueryBuilder } from './query_builder'; import { defaultBucketAgg, hasMetricOfType } from './query_def'; -import { BackendSrvRequest, getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { DataLinkConfig, ElasticsearchOptions, ElasticsearchQuery } from './types'; import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider'; @@ -37,10 +40,7 @@ import { BucketAggregation, isBucketAggregationWithField, } from './components/QueryEditor/BucketAggregationsEditor/aggregations'; -import { generate, Observable, of, throwError } from 'rxjs'; -import { catchError, first, map, mergeMap, skipWhile, throwIfEmpty } from 'rxjs/operators'; import { coerceESVersion, getScriptValue } from './utils'; -import { gte, lt, satisfies } from 'semver'; // Those are metadata fields as defined in https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-fields.html#_identity_metadata_fields. // custom fields can start with underscores, therefore is not safe to exclude anything that starts with one. @@ -183,16 +183,16 @@ export class ElasticDatasource extends DataSourceApi i < Math.min(listLen, maxTraversals), - (i) => i + 1 - ).pipe( + return generate({ + initialState: 0, + condition: (i) => i < Math.min(listLen, maxTraversals), + iterate: (i) => i + 1, + }).pipe( mergeMap((index) => { // catch all errors and emit an object with an err property to simplify checks later in the pipeline return this.request('GET', indexList[listLen - index - 1]).pipe(catchError((err) => of({ err }))); }), - skipWhile((resp) => resp.err && resp.err.status === 404), // skip all requests that fail because missing Elastic index + skipWhile((resp) => resp?.err?.status === 404), // skip all requests that fail because missing Elastic index throwIfEmpty(() => 'Could not find an available index for this time range.'), // when i === Math.min(listLen, maxTraversals) generate will complete but without emitting any values which means we didn't find a valid index first(), // take the first value that isn't skipped map((resp) => { @@ -279,8 +279,8 @@ export class ElasticDatasource extends DataSourceApi { const list = []; const hits = res.responses[0].hits.hits; @@ -351,7 +351,7 @@ export class ElasticDatasource extends DataSourceApi { const timeField: any = find(dateFields, { text: this.timeField }); if (!timeField) { @@ -415,7 +415,7 @@ export class ElasticDatasource extends DataSourceApi { return async (q?: string) => { // _mapping doesn't support filtering, we avoid sending a request everytime q changes if (!rawFields) { - rawFields = await datasource.getFields(filter, range).toPromise(); + rawFields = await lastValueFrom(datasource.getFields(filter, range)); } return rawFields.filter(({ text }) => q === undefined || text.includes(q)).map(toSelectableValue); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts index 78731f57f3f..00162450a4c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts @@ -1,9 +1,10 @@ +import { lastValueFrom, of } from 'rxjs'; import { DataFrame, getFrameDisplayName, toUtc } from '@grafana/data'; +import { setBackendSrv } from '@grafana/runtime'; + import { TemplateSrv } from 'app/features/templating/template_srv'; import { backendSrv } from 'app/core/services/backend_srv'; -import { setBackendSrv } from '@grafana/runtime'; import AppInsightsDatasource from './app_insights_datasource'; -import { of } from 'rxjs'; const templateSrv = new TemplateSrv(); @@ -174,17 +175,14 @@ describe('AppInsightsDatasource', () => { }); it('should return a list of datapoints', () => { - return ctx.ds - .query(options) - .toPromise() - .then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('PrimaryResult'); - expect(data.fields[0].values.length).toEqual(1); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); + return lastValueFrom(ctx.ds.query(options)).then((results: any) => { + expect(results.data.length).toBe(1); + const data = results.data[0] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('PrimaryResult'); + expect(data.fields[0].values.length).toEqual(1); + expect(data.fields[0].values.get(0)).toEqual(1558278660000); + expect(data.fields[1].values.get(0)).toEqual(2.2075); + }); }); }); @@ -216,17 +214,14 @@ describe('AppInsightsDatasource', () => { }); it('should return a list of datapoints', () => { - return ctx.ds - .query(options) - .toPromise() - .then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('paritionA'); - expect(data.fields[0].values.length).toEqual(1); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); + return lastValueFrom(ctx.ds.query(options)).then((results: any) => { + expect(results.data.length).toBe(1); + const data = results.data[0] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('paritionA'); + expect(data.fields[0].values.length).toEqual(1); + expect(data.fields[0].values.get(0)).toEqual(1558278660000); + expect(data.fields[1].values.get(0)).toEqual(2.2075); + }); }); }); }); @@ -280,16 +275,13 @@ describe('AppInsightsDatasource', () => { }); it('should return a single datapoint', () => { - return ctx.ds - .query(options) - .toPromise() - .then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server'); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); + return lastValueFrom(ctx.ds.query(options)).then((results: any) => { + expect(results.data.length).toBe(1); + const data = results.data[0] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('exceptions/server'); + expect(data.fields[0].values.get(0)).toEqual(1558278660000); + expect(data.fields[1].values.get(0)).toEqual(2.2075); + }); }); }); @@ -326,19 +318,16 @@ describe('AppInsightsDatasource', () => { }); it('should return a list of datapoints', () => { - return ctx.ds - .query(options) - .toPromise() - .then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server'); - expect(data.fields[0].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(3); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(6); - }); + return lastValueFrom(ctx.ds.query(options)).then((results: any) => { + expect(results.data.length).toBe(1); + const data = results.data[0] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('exceptions/server'); + expect(data.fields[0].values.length).toEqual(2); + expect(data.fields[0].values.get(0)).toEqual(1504108800000); + expect(data.fields[1].values.get(0)).toEqual(3); + expect(data.fields[0].values.get(1)).toEqual(1504112400000); + expect(data.fields[1].values.get(1)).toEqual(6); + }); }); }); @@ -383,26 +372,23 @@ describe('AppInsightsDatasource', () => { }); it('should return a list of datapoints', () => { - return ctx.ds - .query(options) - .toPromise() - .then((results: any) => { - expect(results.data.length).toBe(2); - let data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="Miami"}'); - expect(data.fields[1].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(10); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(20); - data = results.data[1] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="San Antonio"}'); - expect(data.fields[1].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(1); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(2); - }); + return lastValueFrom(ctx.ds.query(options)).then((results: any) => { + expect(results.data.length).toBe(2); + let data = results.data[0] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="Miami"}'); + expect(data.fields[1].values.length).toEqual(2); + expect(data.fields[0].values.get(0)).toEqual(1504108800000); + expect(data.fields[1].values.get(0)).toEqual(10); + expect(data.fields[0].values.get(1)).toEqual(1504112400000); + expect(data.fields[1].values.get(1)).toEqual(20); + data = results.data[1] as DataFrame; + expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="San Antonio"}'); + expect(data.fields[1].values.length).toEqual(2); + expect(data.fields[0].values.get(0)).toEqual(1504108800000); + expect(data.fields[1].values.get(0)).toEqual(1); + expect(data.fields[0].values.get(1)).toEqual(1504112400000); + expect(data.fields[1].values.get(1)).toEqual(2); + }); }); }); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index daca6b14b32..0aacf3b031d 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -1,4 +1,7 @@ import { each, indexOf, isArray, isString, map as _map } from 'lodash'; +import { lastValueFrom, Observable, of, OperatorFunction, pipe, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { getBackendSrv } from '@grafana/runtime'; import { DataFrame, DataQueryRequest, @@ -11,9 +14,9 @@ import { TimeRange, toDataFrame, } from '@grafana/data'; + import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version'; import gfunc, { FuncDefs, FuncInstance } from './gfunc'; -import { getBackendSrv } from '@grafana/runtime'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; // Types import { @@ -26,8 +29,6 @@ import { } from './types'; import { getRollupNotice, getRuntimeConsolidationNotice } from 'app/plugins/datasource/graphite/meta'; import { getSearchFilterScopedVar } from '../../../features/variables/utils'; -import { Observable, of, OperatorFunction, pipe, throwError } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; import { DEFAULT_GRAPHITE_VERSION } from './versions'; import { reduceError } from './utils'; @@ -250,8 +251,8 @@ export class GraphiteDatasource extends DataSourceApi< maxDataPoints: 100, } as unknown) as DataQueryRequest; - return this.query(graphiteQuery) - .pipe( + return lastValueFrom( + this.query(graphiteQuery).pipe( map((result: any) => { const list = []; @@ -277,7 +278,7 @@ export class GraphiteDatasource extends DataSourceApi< return list; }) ) - .toPromise(); + ); } else { // Graphite event as annotation const tags = this.templateSrv.replace(options.annotation.tags); @@ -315,15 +316,17 @@ export class GraphiteDatasource extends DataSourceApi< if (options.tags) { tags = '&tags=' + options.tags; } - return this.doGraphiteRequest({ - method: 'GET', - url: - '/events/get_data?from=' + - this.translateTime(options.range.raw.from, false, options.timezone) + - '&until=' + - this.translateTime(options.range.raw.to, true, options.timezone) + - tags, - }).toPromise(); + return lastValueFrom( + this.doGraphiteRequest({ + method: 'GET', + url: + '/events/get_data?from=' + + this.translateTime(options.range.raw.from, false, options.timezone) + + '&until=' + + this.translateTime(options.range.raw.to, true, options.timezone) + + tags, + }) + ); } catch (err) { return Promise.reject(err); } @@ -444,8 +447,8 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.until = range.until; } - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { return _map(results.data, (metric) => { return { @@ -455,7 +458,7 @@ export class GraphiteDatasource extends DataSourceApi< }); }) ) - .toPromise(); + ); } /** @@ -484,8 +487,8 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.until = range.until; } - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { return _map(results.data.results, (metric) => { return { @@ -495,7 +498,7 @@ export class GraphiteDatasource extends DataSourceApi< }); }) ) - .toPromise(); + ); } getTags(optionalOptions: any) { @@ -513,8 +516,8 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { return _map(results.data, (tag) => { return { @@ -524,7 +527,7 @@ export class GraphiteDatasource extends DataSourceApi< }); }) ) - .toPromise(); + ); } getTagValues(options: any = {}) { @@ -540,8 +543,8 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { if (results.data && results.data.values) { return _map(results.data.values, (value) => { @@ -555,7 +558,7 @@ export class GraphiteDatasource extends DataSourceApi< } }) ) - .toPromise(); + ); } getTagsAutoComplete(expressions: any[], tagPrefix: any, optionalOptions?: any) { @@ -581,7 +584,7 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } - return this.doGraphiteRequest(httpOptions).pipe(mapToTags()).toPromise(); + return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags())); } getTagValuesAutoComplete(expressions: any[], tag: any, valuePrefix: any, optionalOptions: any) { @@ -608,7 +611,7 @@ export class GraphiteDatasource extends DataSourceApi< httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } - return this.doGraphiteRequest(httpOptions).pipe(mapToTags()).toPromise(); + return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags())); } getVersion(optionalOptions: any) { @@ -620,8 +623,8 @@ export class GraphiteDatasource extends DataSourceApi< requestId: options.requestId, }; - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { if (results.data) { const semver = new SemVersion(results.data); @@ -633,7 +636,7 @@ export class GraphiteDatasource extends DataSourceApi< return of(''); }) ) - .toPromise(); + ); } createFuncInstance(funcDef: any, options?: any): FuncInstance { @@ -664,8 +667,8 @@ export class GraphiteDatasource extends DataSourceApi< url: '/functions', }; - return this.doGraphiteRequest(httpOptions) - .pipe( + return lastValueFrom( + this.doGraphiteRequest(httpOptions).pipe( map((results: any) => { if (results.status !== 200 || typeof results.data !== 'object') { if (typeof results.data === 'string') { @@ -690,7 +693,7 @@ export class GraphiteDatasource extends DataSourceApi< return of(this.funcDefs); }) ) - .toPromise(); + ); } testDatasource() { @@ -704,9 +707,7 @@ export class GraphiteDatasource extends DataSourceApi< maxDataPoints: 300, } as unknown) as DataQueryRequest; - return this.query(query) - .toPromise() - .then(() => ({ status: 'success', message: 'Data source is working' })); + return lastValueFrom(this.query(query)).then(() => ({ status: 'success', message: 'Data source is working' })); } doGraphiteRequest(options: { diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index fb03acd1457..ce1e95f6a22 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -1,36 +1,36 @@ -import { cloneDeep, map as _map, reduce, get, has, extend, omit, pick, isString } from 'lodash'; - +import { cloneDeep, extend, get, has, isString, map as _map, omit, pick, reduce } from 'lodash'; +import { lastValueFrom, Observable, of, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { v4 as uuidv4 } from 'uuid'; +import { DataSourceWithBackend, frameToMetricFindValue, getBackendSrv } from '@grafana/runtime'; import { - dateMath, - DataSourceInstanceSettings, - ScopedVars, + AnnotationEvent, + AnnotationQueryRequest, + ArrayVector, + DataFrame, + DataQueryError, DataQueryRequest, DataQueryResponse, + DataSourceInstanceSettings, + dateMath, dateTime, + FieldType, LoadingState, - QueryResultMeta, MetricFindValue, - AnnotationQueryRequest, - AnnotationEvent, - DataQueryError, - DataFrame, - TimeSeries, + QueryResultMeta, + ScopedVars, TIME_SERIES_TIME_FIELD_NAME, TIME_SERIES_VALUE_FIELD_NAME, - FieldType, - ArrayVector, + TimeSeries, } from '@grafana/data'; -import { v4 as uuidv4 } from 'uuid'; + import InfluxSeries from './influx_series'; import InfluxQueryModel from './influx_query_model'; import ResponseParser from './response_parser'; import { InfluxQueryBuilder } from './query_builder'; -import { InfluxQuery, InfluxOptions, InfluxVersion } from './types'; +import { InfluxOptions, InfluxQuery, InfluxVersion } from './types'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; -import { getBackendSrv, DataSourceWithBackend, frameToMetricFindValue } from '@grafana/runtime'; -import { Observable, throwError, of } from 'rxjs'; import { FluxQueryEditor } from './components/FluxQueryEditor'; -import { catchError, map } from 'rxjs/operators'; import { buildRawQuery } from './queryUtils'; // we detect the field type based on the value-array @@ -310,17 +310,15 @@ export default class InfluxDatasource extends DataSourceWithBackend { - if (!data || !data.results || !data.results[0]) { - throw { message: 'No results in response from InfluxDB' }; - } - return new InfluxSeries({ - series: data.results[0].series, - annotation: options.annotation, - }).getAnnotations(); - }); + return lastValueFrom(this._seriesQuery(query, options)).then((data: any) => { + if (!data || !data.results || !data.results[0]) { + throw { message: 'No results in response from InfluxDB' }; + } + return new InfluxSeries({ + series: data.results[0].series, + annotation: options.annotation, + }).getAnnotations(); + }); } targetContainsTemplate(target: any) { @@ -370,27 +368,24 @@ export default class InfluxDatasource extends DataSourceWithBackend { - if (rsp.data?.length) { - return frameToMetricFindValue(rsp.data[0]); - } - return []; - }); + ).then((rsp) => { + if (rsp.data?.length) { + return frameToMetricFindValue(rsp.data[0]); + } + return []; + }); } const interpolated = this.templateSrv.replace(query, undefined, 'regex'); - return this._seriesQuery(interpolated, options) - .toPromise() - .then((resp) => { - return this.responseParser.parse(query, resp); - }); + return lastValueFrom(this._seriesQuery(interpolated, options)).then((resp) => { + return this.responseParser.parse(query, resp); + }); } getTagKeys(options: any = {}) { @@ -453,9 +448,7 @@ export default class InfluxDatasource extends DataSourceWithBackend; - return super - .query(request) - .toPromise() + return lastValueFrom(super.query(request)) .then((res: DataQueryResponse) => { if (!res || !res.data || res.state !== LoadingState.Done) { console.error('InfluxDB Error', res); @@ -477,8 +470,7 @@ export default class InfluxDatasource extends DataSourceWithBackend { const error = get(res, 'results[0].error'); if (error) { diff --git a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts index d42c54b0421..796290608bb 100644 --- a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts +++ b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts @@ -1,9 +1,9 @@ -import InfluxDatasource from '../datasource'; +import { lastValueFrom, of } from 'rxjs'; +import { FetchResponse } from '@grafana/runtime'; +import InfluxDatasource from '../datasource'; import { TemplateSrvStub } from 'test/specs/helpers'; import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ -import { of } from 'rxjs'; -import { FetchResponse } from '@grafana/runtime'; //@ts-ignore const templateSrv = new TemplateSrvStub(); @@ -112,7 +112,7 @@ describe('InfluxDataSource', () => { }); try { - await ctx.ds.query(queryOptions).toPromise(); + await lastValueFrom(ctx.ds.query(queryOptions)); } catch (err) { expect(err.message).toBe('InfluxDB Error: Query timeout'); } diff --git a/public/app/plugins/datasource/jaeger/datasource.test.ts b/public/app/plugins/datasource/jaeger/datasource.test.ts index cef92297731..85cbc37c758 100644 --- a/public/app/plugins/datasource/jaeger/datasource.test.ts +++ b/public/app/plugins/datasource/jaeger/datasource.test.ts @@ -1,6 +1,7 @@ +import { lastValueFrom, of, throwError } from 'rxjs'; import { DataQueryRequest, DataSourceInstanceSettings, dateTime, FieldType, PluginType } from '@grafana/data'; + import { backendSrv } from 'app/core/services/backend_srv'; -import { of, throwError } from 'rxjs'; import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { ALL_OPERATIONS_KEY } from './components/SearchForm'; import { JaegerDatasource } from './datasource'; @@ -36,7 +37,7 @@ describe('JaegerDatasource', () => { setupFetchMock({ data: [testResponse] }); const ds = new JaegerDatasource(defaultSettings); - const response = await ds.query(defaultQuery).toPromise(); + const response = await lastValueFrom(ds.query(defaultQuery)); expect(response.data.length).toBe(3); expect(response.data[0].fields).toMatchObject(testResponseDataFrameFields); expect(response.data[1].fields).toMatchObject(testResponseNodesFields); @@ -55,18 +56,18 @@ describe('JaegerDatasource', () => { }, ], }; - await ds.query(query).toPromise(); + await lastValueFrom(ds.query(query)); expect(mock).toBeCalledWith({ url: `${defaultSettings.url}/api/traces/a%2Fb` }); }); it('returns empty response if trace id is not specified', async () => { const ds = new JaegerDatasource(defaultSettings); - const response = await ds - .query({ + const response = await lastValueFrom( + ds.query({ ...defaultQuery, targets: [], }) - .toPromise(); + ); const field = response.data[0].fields[0]; expect(field.name).toBe('trace'); expect(field.type).toBe(FieldType.trace); @@ -76,12 +77,12 @@ describe('JaegerDatasource', () => { it('should handle json file upload', async () => { const ds = new JaegerDatasource(defaultSettings); ds.uploadedJson = JSON.stringify(mockJson); - const response = await ds - .query({ + const response = await lastValueFrom( + ds.query({ ...defaultQuery, targets: [{ queryType: 'upload', refId: 'A' }], }) - .toPromise(); + ); const field = response.data[0].fields[0]; expect(field.name).toBe('traceID'); expect(field.type).toBe(FieldType.string); @@ -91,12 +92,12 @@ describe('JaegerDatasource', () => { it('should return search results when the query type is search', async () => { const mock = setupFetchMock({ data: [testResponse] }); const ds = new JaegerDatasource(defaultSettings, timeSrvStub); - const response = await ds - .query({ + const response = await lastValueFrom( + ds.query({ ...defaultQuery, targets: [{ queryType: 'search', refId: 'a', service: 'jaeger-query', operation: '/api/services' }], }) - .toPromise(); + ); expect(mock).toBeCalledWith({ url: `${defaultSettings.url}/api/traces?operation=%2Fapi%2Fservices&service=jaeger-query&start=1531468681000&end=1531489712000&lookback=custom`, }); @@ -109,12 +110,12 @@ describe('JaegerDatasource', () => { it('should remove operation from the query when all is selected', async () => { const mock = setupFetchMock({ data: [testResponse] }); const ds = new JaegerDatasource(defaultSettings, timeSrvStub); - await ds - .query({ + await lastValueFrom( + ds.query({ ...defaultQuery, targets: [{ queryType: 'search', refId: 'a', service: 'jaeger-query', operation: ALL_OPERATIONS_KEY }], }) - .toPromise(); + ); expect(mock).toBeCalledWith({ url: `${defaultSettings.url}/api/traces?service=jaeger-query&start=1531468681000&end=1531489712000&lookback=custom`, }); @@ -123,12 +124,12 @@ describe('JaegerDatasource', () => { it('should convert tags from logfmt format to an object', async () => { const mock = setupFetchMock({ data: [testResponse] }); const ds = new JaegerDatasource(defaultSettings, timeSrvStub); - await ds - .query({ + await lastValueFrom( + ds.query({ ...defaultQuery, targets: [{ queryType: 'search', refId: 'a', service: 'jaeger-query', tags: 'error=true' }], }) - .toPromise(); + ); expect(mock).toBeCalledWith({ url: `${defaultSettings.url}/api/traces?service=jaeger-query&tags=%7B%22error%22%3A%22true%22%7D&start=1531468681000&end=1531489712000&lookback=custom`, }); diff --git a/public/app/plugins/datasource/jaeger/datasource.ts b/public/app/plugins/datasource/jaeger/datasource.ts index 8ffef846b7f..5975d57b533 100644 --- a/public/app/plugins/datasource/jaeger/datasource.ts +++ b/public/app/plugins/datasource/jaeger/datasource.ts @@ -1,3 +1,6 @@ +import { identity, omit, pick, pickBy } from 'lodash'; +import { lastValueFrom, Observable, of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; import { DataQueryRequest, DataQueryResponse, @@ -9,14 +12,12 @@ import { MutableDataFrame, } from '@grafana/data'; import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; + import { serializeParams } from 'app/core/utils/fetch'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { Observable, of } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; import { createTableFrame, createTraceFrame } from './responseTransform'; import { createGraphFrames } from './graphTransform'; import { JaegerQuery } from './types'; -import { identity, omit, pick, pickBy } from 'lodash'; import { convertTagsLogfmt } from './util'; import { ALL_OPERATIONS_KEY } from './components/SearchForm'; @@ -27,7 +28,7 @@ export class JaegerDatasource extends DataSourceApi { } async metadataRequest(url: string, params?: Record): Promise { - const res = await this._request(url, params, { hideFromInspector: true }).toPromise(); + const res = await lastValueFrom(this._request(url, params, { hideFromInspector: true })); return res.data.data; } @@ -87,8 +88,8 @@ export class JaegerDatasource extends DataSourceApi { } async testDatasource(): Promise { - return this._request('/api/services') - .pipe( + return lastValueFrom( + this._request('/api/services').pipe( map((res) => { const values: any[] = res?.data?.data || []; const testResult = @@ -121,7 +122,7 @@ export class JaegerDatasource extends DataSourceApi { return of({ status: 'error', message: message }); }) ) - .toPromise(); + ); } getTimeRange(): { start: number; end: number } { diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 73aa7b14059..9e9aea37ba9 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -1,4 +1,4 @@ -import { of, throwError } from 'rxjs'; +import { lastValueFrom, of, throwError } from 'rxjs'; import { take } from 'rxjs/operators'; import { AnnotationQueryRequest, CoreApp, DataFrame, dateTime, FieldCache, TimeSeries, toUtc } from '@grafana/data'; import { BackendSrvRequest, FetchResponse } from '@grafana/runtime'; @@ -219,56 +219,56 @@ describe('LokiDatasource', () => { it('should run logs instant if only instant is selected', async () => { const { ds, options } = setup(logsQuery, CoreApp.Explore, true, false); - await ds.query(options).toPromise(); + await lastValueFrom(ds.query(options)); expect(ds.runInstantQuery).toBeCalled(); expect(ds.runRangeQuery).not.toBeCalled(); }); it('should run metrics instant if only instant is selected', async () => { const { ds, options } = setup(metricsQuery, CoreApp.Explore, true, false); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).toBeCalled(); expect(ds.runRangeQuery).not.toBeCalled(); }); it('should run only logs range query if only range is selected', async () => { const { ds, options } = setup(logsQuery, CoreApp.Explore, false, true); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); it('should run only metrics range query if only range is selected', async () => { const { ds, options } = setup(metricsQuery, CoreApp.Explore, false, true); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); it('should run only logs range query if no query type is selected in Explore', async () => { const { ds, options } = setup(logsQuery, CoreApp.Explore); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); it('should run only metrics range query if no query type is selected in Explore', async () => { const { ds, options } = setup(metricsQuery, CoreApp.Explore); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); it('should run only logs range query in Dashboard', async () => { const { ds, options } = setup(logsQuery, CoreApp.Dashboard); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); it('should run only metrics range query in Dashboard', async () => { const { ds, options } = setup(metricsQuery, CoreApp.Dashboard); - await ds.query(options).toPromise(); + lastValueFrom(await ds.query(options)); expect(ds.runInstantQuery).not.toBeCalled(); expect(ds.runRangeQuery).toBeCalled(); }); @@ -349,7 +349,7 @@ describe('LokiDatasource', () => { }); it('should not modify expression with no filters', async () => { - await ds.query(options as any).toPromise(); + await lastValueFrom(ds.query(options as any)); expect(ds.runRangeQuery).toBeCalledWith({ expr: DEFAULT_EXPR }, expect.anything(), expect.anything()); }); @@ -367,7 +367,7 @@ describe('LokiDatasource', () => { }, ]); - await ds.query(options as any).toPromise(); + await lastValueFrom(ds.query(options as any)); expect(ds.runRangeQuery).toBeCalledWith( { expr: 'rate({bar="baz",job="foo",k1="v1",k2!="v2"} |= "bar" [5m])' }, expect.anything(), @@ -388,7 +388,7 @@ describe('LokiDatasource', () => { value: `v'.*`, }, ]); - await ds.query(options as any).toPromise(); + await lastValueFrom(ds.query(options as any)); expect(ds.runRangeQuery).toBeCalledWith( { expr: 'rate({bar="baz",job="foo",k1=~"v.*",k2=~"v\\\\\'.*"} |= "bar" [5m])' }, expect.anything(), diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index e13662eaaa5..c656043fe2a 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -1,6 +1,6 @@ // Libraries import { cloneDeep, isEmpty, map as lodashMap } from 'lodash'; -import { merge, Observable, of, throwError } from 'rxjs'; +import { lastValueFrom, merge, Observable, of, throwError } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import Prism from 'prismjs'; @@ -36,7 +36,7 @@ import { lokiStreamsToDataFrames, processRangeQueryResponse, } from './result_transformer'; -import { getHighlighterExpressionsFromQuery, queryHasPipeParser, addParsedLabelToQuery } from './query_utils'; +import { addParsedLabelToQuery, getHighlighterExpressionsFromQuery, queryHasPipeParser } from './query_utils'; import { LokiOptions, @@ -323,7 +323,7 @@ export class LokiDatasource extends DataSourceApi { } async metadataRequest(url: string, params?: Record) { - const res = await this._request(url, params, { hideFromInspector: true }).toPromise(); + const res = await lastValueFrom(this._request(url, params, { hideFromInspector: true })); return res.data.data || res.data.values || []; } @@ -459,8 +459,8 @@ export class LokiDatasource extends DataSourceApi { ); const reverse = options && options.direction === 'FORWARD'; - return this._request(RANGE_QUERY_ENDPOINT, target) - .pipe( + return lastValueFrom( + this._request(RANGE_QUERY_ENDPOINT, target).pipe( catchError((err: any) => { if (err.status === 404) { return of(err); @@ -479,7 +479,7 @@ export class LokiDatasource extends DataSourceApi { }) ) ) - .toPromise(); + ); }; prepareLogRowContextQueryTarget = (row: LogRowModel, limit: number, direction: 'BACKWARD' | 'FORWARD') => { @@ -524,8 +524,8 @@ export class LokiDatasource extends DataSourceApi { // Consider only last 10 minutes otherwise request takes too long const startMs = Date.now() - 10 * 60 * 1000; const start = `${startMs}000000`; // API expects nanoseconds - return this._request(`${LOKI_ENDPOINT}/label`, { start }) - .pipe( + return lastValueFrom( + this._request(`${LOKI_ENDPOINT}/label`, { start }).pipe( map((res) => { const values: any[] = res?.data?.data || res?.data?.values || []; const testResult = @@ -558,7 +558,7 @@ export class LokiDatasource extends DataSourceApi { return of({ status: 'error', message: message }); }) ) - .toPromise(); + ); } async annotationQuery(options: any): Promise { @@ -585,8 +585,8 @@ export class LokiDatasource extends DataSourceApi { stepInterval, }; const { data } = instant - ? await this.runInstantQuery(query, options as any).toPromise() - : await this.runRangeQuery(query, options as any).toPromise(); + ? await lastValueFrom(this.runInstantQuery(query, options as any)) + : await lastValueFrom(this.runRangeQuery(query, options as any)); const annotations: AnnotationEvent[] = []; const splitKeys: string[] = tagKeys.split(',').filter((v: string) => v !== ''); diff --git a/public/app/plugins/datasource/mssql/datasource.ts b/public/app/plugins/datasource/mssql/datasource.ts index 8224d496db9..1587ffd0d4d 100644 --- a/public/app/plugins/datasource/mssql/datasource.ts +++ b/public/app/plugins/datasource/mssql/datasource.ts @@ -1,12 +1,12 @@ import { map as _map } from 'lodash'; -import { of } from 'rxjs'; +import { lastValueFrom, of } from 'rxjs'; import { catchError, map, mapTo } from 'rxjs/operators'; import { BackendDataSourceResponse, DataSourceWithBackend, FetchResponse, getBackendSrv } from '@grafana/runtime'; -import { AnnotationEvent, DataSourceInstanceSettings, ScopedVars, MetricFindValue } from '@grafana/data'; +import { AnnotationEvent, DataSourceInstanceSettings, MetricFindValue, ScopedVars } from '@grafana/data'; import ResponseParser from './response_parser'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; -import { MssqlQueryForInterpolation, MssqlQuery, MssqlOptions } from './types'; +import { MssqlOptions, MssqlQuery, MssqlQueryForInterpolation } from './types'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse'; @@ -92,24 +92,25 @@ export class MssqlDatasource extends DataSourceWithBackend({ - url: '/api/ds/query', - method: 'POST', - data: { - from: options.range.from.valueOf().toString(), - to: options.range.to.valueOf().toString(), - queries: [query], - }, - requestId: options.annotation.name, - }) - .pipe( - map( - async (res: FetchResponse) => - await this.responseParser.transformAnnotationResponse(options, res.data) + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries: [query], + }, + requestId: options.annotation.name, + }) + .pipe( + map( + async (res: FetchResponse) => + await this.responseParser.transformAnnotationResponse(options, res.data) + ) ) - ) - .toPromise(); + ); } filterQuery(query: MssqlQuery): boolean { @@ -131,52 +132,54 @@ export class MssqlDatasource extends DataSourceWithBackend({ - url: '/api/ds/query', - method: 'POST', - data: { - from: range.from.valueOf().toString(), - to: range.to.valueOf().toString(), - queries: [interpolatedQuery], - }, - requestId: refId, - }) - .pipe( - map((rsp) => { - return this.responseParser.transformMetricFindResponse(rsp); + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: range.from.valueOf().toString(), + to: range.to.valueOf().toString(), + queries: [interpolatedQuery], + }, + requestId: refId, }) - ) - .toPromise(); + .pipe( + map((rsp) => { + return this.responseParser.transformMetricFindResponse(rsp); + }) + ) + ); } testDatasource(): Promise { - return getBackendSrv() - .fetch({ - url: '/api/ds/query', - method: 'POST', - data: { - from: '5m', - to: 'now', - queries: [ - { - refId: 'A', - intervalMs: 1, - maxDataPoints: 1, - datasourceId: this.id, - rawSql: 'SELECT 1', - format: 'table', - }, - ], - }, - }) - .pipe( - mapTo({ status: 'success', message: 'Database Connection OK' }), - catchError((err) => { - return of(toTestingStatus(err)); + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: '5m', + to: 'now', + queries: [ + { + refId: 'A', + intervalMs: 1, + maxDataPoints: 1, + datasourceId: this.id, + rawSql: 'SELECT 1', + format: 'table', + }, + ], + }, }) - ) - .toPromise(); + .pipe( + mapTo({ status: 'success', message: 'Database Connection OK' }), + catchError((err) => { + return of(toTestingStatus(err)); + }) + ) + ); } targetContainsTemplate(query: MssqlQuery): boolean { diff --git a/public/app/plugins/datasource/mysql/datasource.ts b/public/app/plugins/datasource/mysql/datasource.ts index b93860550ef..ede1de31de7 100644 --- a/public/app/plugins/datasource/mysql/datasource.ts +++ b/public/app/plugins/datasource/mysql/datasource.ts @@ -1,14 +1,15 @@ import { map as _map } from 'lodash'; +import { lastValueFrom, of } from 'rxjs'; import { catchError, map, mapTo } from 'rxjs/operators'; -import { getBackendSrv, DataSourceWithBackend, FetchResponse, BackendDataSourceResponse } from '@grafana/runtime'; -import { DataSourceInstanceSettings, ScopedVars, MetricFindValue, AnnotationEvent } from '@grafana/data'; +import { BackendDataSourceResponse, DataSourceWithBackend, FetchResponse, getBackendSrv } from '@grafana/runtime'; +import { AnnotationEvent, DataSourceInstanceSettings, MetricFindValue, ScopedVars } from '@grafana/data'; + import MySQLQueryModel from 'app/plugins/datasource/mysql/mysql_query_model'; import ResponseParser from './response_parser'; -import { MysqlQueryForInterpolation, MySQLOptions, MySQLQuery } from './types'; +import { MySQLOptions, MySQLQuery, MysqlQueryForInterpolation } from './types'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { getSearchFilterScopedVar } from '../../../features/variables/utils'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { of } from 'rxjs'; import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse'; export class MysqlDatasource extends DataSourceWithBackend { @@ -99,24 +100,25 @@ export class MysqlDatasource extends DataSourceWithBackend({ - url: '/api/ds/query', - method: 'POST', - data: { - from: options.range.from.valueOf().toString(), - to: options.range.to.valueOf().toString(), - queries: [query], - }, - requestId: options.annotation.name, - }) - .pipe( - map( - async (res: FetchResponse) => - await this.responseParser.transformAnnotationResponse(options, res.data) + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries: [query], + }, + requestId: options.annotation.name, + }) + .pipe( + map( + async (res: FetchResponse) => + await this.responseParser.transformAnnotationResponse(options, res.data) + ) ) - ) - .toPromise(); + ); } metricFindQuery(query: string, optionalOptions: any): Promise { @@ -140,52 +142,54 @@ export class MysqlDatasource extends DataSourceWithBackend({ - url: '/api/ds/query', - method: 'POST', - data: { - from: range.from.valueOf().toString(), - to: range.to.valueOf().toString(), - queries: [interpolatedQuery], - }, - requestId: refId, - }) - .pipe( - map((rsp) => { - return this.responseParser.transformMetricFindResponse(rsp); + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: range.from.valueOf().toString(), + to: range.to.valueOf().toString(), + queries: [interpolatedQuery], + }, + requestId: refId, }) - ) - .toPromise(); + .pipe( + map((rsp) => { + return this.responseParser.transformMetricFindResponse(rsp); + }) + ) + ); } testDatasource(): Promise { - return getBackendSrv() - .fetch({ - url: '/api/ds/query', - method: 'POST', - data: { - from: '5m', - to: 'now', - queries: [ - { - refId: 'A', - intervalMs: 1, - maxDataPoints: 1, - datasourceId: this.id, - rawSql: 'SELECT 1', - format: 'table', - }, - ], - }, - }) - .pipe( - mapTo({ status: 'success', message: 'Database Connection OK' }), - catchError((err) => { - return of(toTestingStatus(err)); + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: '5m', + to: 'now', + queries: [ + { + refId: 'A', + intervalMs: 1, + maxDataPoints: 1, + datasourceId: this.id, + rawSql: 'SELECT 1', + format: 'table', + }, + ], + }, }) - ) - .toPromise(); + .pipe( + mapTo({ status: 'success', message: 'Database Connection OK' }), + catchError((err) => { + return of(toTestingStatus(err)); + }) + ) + ); } targetContainsTemplate(target: any) { diff --git a/public/app/plugins/datasource/opentsdb/datasource.ts b/public/app/plugins/datasource/opentsdb/datasource.ts index acfb6434a0a..07757aa5356 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.ts +++ b/public/app/plugins/datasource/opentsdb/datasource.ts @@ -10,10 +10,10 @@ import { includes, isArray, isEmpty, - toPairs, map as _map, + toPairs, } from 'lodash'; -import { Observable, of } from 'rxjs'; +import { lastValueFrom, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { FetchResponse, getBackendSrv } from '@grafana/runtime'; import { @@ -133,8 +133,8 @@ export default class OpenTsDatasource extends DataSourceApi { if (results.data[0]) { let annotationObject = results.data[0].annotations; @@ -156,7 +156,7 @@ export default class OpenTsDatasource extends DataSourceApi { return { status: 'success', message: 'Data source is working' }; }) ) - .toPromise(); + ); } getAggregators() { @@ -375,8 +375,8 @@ export default class OpenTsDatasource extends DataSourceApi { if (result.data && isArray(result.data)) { return result.data.sort(); @@ -384,7 +384,7 @@ export default class OpenTsDatasource extends DataSourceApi { if (result.data) { return Object.keys(result.data).sort(); @@ -402,7 +402,7 @@ export default class OpenTsDatasource extends DataSourceApi({ - url: '/api/ds/query', - method: 'POST', - data: { - from: options.range.from.valueOf().toString(), - to: options.range.to.valueOf().toString(), - queries: [query], - }, - requestId: options.annotation.name, - }) - .pipe( - map( - async (res: FetchResponse) => - await this.responseParser.transformAnnotationResponse(options, res.data) + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries: [query], + }, + requestId: options.annotation.name, + }) + .pipe( + map( + async (res: FetchResponse) => + await this.responseParser.transformAnnotationResponse(options, res.data) + ) ) - ) - .toPromise(); + ); } metricFindQuery(query: string, optionalOptions: any): Promise { @@ -142,23 +144,24 @@ export class PostgresDatasource extends DataSourceWithBackend({ - url: '/api/ds/query', - method: 'POST', - data: { - from: range.from.valueOf().toString(), - to: range.to.valueOf().toString(), - queries: [interpolatedQuery], - }, - requestId: refId, - }) - .pipe( - map((rsp) => { - return this.responseParser.transformMetricFindResponse(rsp); + return lastValueFrom( + getBackendSrv() + .fetch({ + url: '/api/ds/query', + method: 'POST', + data: { + from: range.from.valueOf().toString(), + to: range.to.valueOf().toString(), + queries: [interpolatedQuery], + }, + requestId: refId, }) - ) - .toPromise(); + .pipe( + map((rsp) => { + return this.responseParser.transformMetricFindResponse(rsp); + }) + ) + ); } getVersion(): Promise { diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 75cfaae4870..71f433ee2d0 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -1,3 +1,7 @@ +import { cloneDeep, defaults } from 'lodash'; +import { forkJoin, lastValueFrom, merge, Observable, of, OperatorFunction, pipe, Subject, throwError } from 'rxjs'; +import { catchError, filter, map, tap } from 'rxjs/operators'; +import LRU from 'lru-cache'; import { AnnotationEvent, CoreApp, @@ -14,17 +18,14 @@ import { TimeRange, } from '@grafana/data'; import { BackendSrvRequest, FetchError, FetchResponse, getBackendSrv } from '@grafana/runtime'; + import { safeStringifyValue } from 'app/core/utils/explore'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; -import { defaults, cloneDeep } from 'lodash'; -import LRU from 'lru-cache'; -import { forkJoin, merge, Observable, of, OperatorFunction, pipe, Subject, throwError } from 'rxjs'; -import { catchError, filter, map, tap } from 'rxjs/operators'; import addLabelToQuery from './add_label_to_query'; import PrometheusLanguageProvider from './language_provider'; import { expandRecordingRules } from './language_utils'; -import { getQueryHints, getInitHints } from './query_hints'; +import { getInitHints, getQueryHints } from './query_hints'; import { getOriginalMetricName, renderTemplate, transform } from './result_transformer'; import { ExemplarTraceIdDestination, @@ -160,7 +161,9 @@ export class PrometheusDatasource extends DataSourceApi // If URL includes endpoint that supports POST and GET method, try to use configured method. This might fail as POST is supported only in v2.10+. if (GET_AND_POST_METADATA_ENDPOINTS.some((endpoint) => url.includes(endpoint))) { try { - return await this._request(url, params, { method: this.httpMethod, hideFromInspector: true }).toPromise(); + return await lastValueFrom( + this._request(url, params, { method: this.httpMethod, hideFromInspector: true }) + ); } catch (err) { // If status code of error is Method Not Allowed (405) and HTTP method is POST, retry with GET if (this.httpMethod === 'POST' && err.status === 405) { @@ -171,7 +174,9 @@ export class PrometheusDatasource extends DataSourceApi } } - return await this._request(url, params, { method: 'GET', hideFromInspector: true }).toPromise(); // toPromise until we change getTagValues, getTagKeys to Observable + return await lastValueFrom( + this._request(url, params, { method: 'GET', hideFromInspector: true }) + ); // toPromise until we change getTagValues, getTagKeys to Observable } interpolateQueryExpr(value: string | string[] = [], variable: any) { @@ -654,7 +659,7 @@ export class PrometheusDatasource extends DataSourceApi }; const query = this.createQuery(queryModel, queryOptions, start, end); - const response = await this.performTimeSeriesQuery(query, query.start, query.end).toPromise(); + const response = await lastValueFrom(this.performTimeSeriesQuery(query, query.start, query.end)); const eventList: AnnotationEvent[] = []; const splitKeys = tagKeys.split(','); @@ -743,7 +748,7 @@ export class PrometheusDatasource extends DataSourceApi async testDatasource() { const now = new Date().getTime(); const query = { expr: '1+1' } as PromQueryRequest; - const response = await this.performInstantQuery(query, now / 1000).toPromise(); + const response = await lastValueFrom(this.performInstantQuery(query, now / 1000)); return response.data.status === 'success' ? { status: 'success', message: 'Data source is working' } : { status: 'error', message: response.data.error }; diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts index e80e893e7bb..2ef7b7e2b09 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts @@ -1,6 +1,8 @@ -import { map as _map, uniq, chain } from 'lodash'; +import { chain, map as _map, uniq } from 'lodash'; +import { lastValueFrom } from 'rxjs'; import { map } from 'rxjs/operators'; import { MetricFindValue, TimeRange } from '@grafana/data'; + import { PrometheusDatasource } from './datasource'; import { PromQueryRequest } from './types'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -40,7 +42,7 @@ export default class PrometheusMetricFindQuery { const queryResultQuery = this.query.match(queryResultRegex); if (queryResultQuery) { - return this.queryResultQuery(queryResultQuery[1]).toPromise(); + return lastValueFrom(this.queryResultQuery(queryResultQuery[1])); } // if query contains full metric name, return metric name and label list diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index f78500de5dd..c74399cc66b 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -1,3 +1,4 @@ +import { lastValueFrom, Observable, of } from 'rxjs'; import { DataFrame, dataFrameToJSON, @@ -8,10 +9,10 @@ import { MutableDataFrame, PluginType, } from '@grafana/data'; -import { Observable, of } from 'rxjs'; + import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { TempoDatasource } from './datasource'; -import { FetchResponse, setBackendSrv, BackendDataSourceResponse, setDataSourceSrv } from '@grafana/runtime'; +import { BackendDataSourceResponse, FetchResponse, setBackendSrv, setDataSourceSrv } from '@grafana/runtime'; import mockJson from './mockJsonResponse.json'; describe('Tempo data source', () => { @@ -33,7 +34,7 @@ describe('Tempo data source', () => { }) ); const ds = new TempoDatasource(defaultSettings); - const response = await ds.query({ targets: [{ refId: 'refid1' }] } as any).toPromise(); + const response = await lastValueFrom(ds.query({ targets: [{ refId: 'refid1' }] } as any)); expect( (response.data[0] as DataFrame).fields.map((f) => ({ @@ -89,9 +90,9 @@ describe('Tempo data source', () => { }, }); setDataSourceSrv(backendSrvWithPrometheus as any); - const response = await ds - .query({ targets: [{ queryType: 'serviceMap' }], range: getDefaultTimeRange() } as any) - .toPromise(); + const response = await lastValueFrom( + ds.query({ targets: [{ queryType: 'serviceMap' }], range: getDefaultTimeRange() } as any) + ); expect(response.data).toHaveLength(2); expect(response.data[0].name).toBe('Nodes'); @@ -106,11 +107,11 @@ describe('Tempo data source', () => { it('should handle json file upload', async () => { const ds = new TempoDatasource(defaultSettings); ds.uploadedJson = JSON.stringify(mockJson); - const response = await ds - .query({ + const response = await lastValueFrom( + ds.query({ targets: [{ queryType: 'upload', refId: 'A' }], } as any) - .toPromise(); + ); const field = response.data[0].fields[0]; expect(field.name).toBe('traceID'); expect(field.type).toBe(FieldType.string); diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index ff817d3a9e8..b8da1a7d2a9 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -1,4 +1,6 @@ import { groupBy } from 'lodash'; +import { from, lastValueFrom, merge, Observable, of, throwError } from 'rxjs'; +import { map, mergeMap, toArray } from 'rxjs/operators'; import { DataQuery, DataQueryRequest, @@ -9,10 +11,9 @@ import { LoadingState, } from '@grafana/data'; import { DataSourceWithBackend } from '@grafana/runtime'; + import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { from, merge, Observable, of, throwError } from 'rxjs'; -import { map, mergeMap, toArray } from 'rxjs/operators'; import { LokiOptions, LokiQuery } from '../loki/types'; import { transformTrace, transformTraceList, transformFromOTLP as transformFromOTEL } from './resultTransformer'; import { PrometheusDatasource } from '../prometheus/datasource'; @@ -119,7 +120,7 @@ export class TempoDatasource extends DataSourceWithBackend { // to test Tempo we send a dummy traceID and verify Tempo answers with 'trace not found' - const response = await super.query({ targets: [{ query: '0' }] } as any).toPromise(); + const response = await lastValueFrom(super.query({ targets: [{ query: '0' }] } as any)); const errorMessage = response.error?.message; if ( diff --git a/public/app/plugins/datasource/zipkin/datasource.test.ts b/public/app/plugins/datasource/zipkin/datasource.test.ts index 3d121e58963..ca3ae78e5da 100644 --- a/public/app/plugins/datasource/zipkin/datasource.test.ts +++ b/public/app/plugins/datasource/zipkin/datasource.test.ts @@ -1,6 +1,7 @@ +import { lastValueFrom, of } from 'rxjs'; import { DataSourceInstanceSettings, FieldType } from '@grafana/data'; + import { backendSrv } from 'app/core/services/backend_srv'; -import { of } from 'rxjs'; import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { ZipkinDatasource } from './datasource'; import mockJson from './mockJsonResponse.json'; @@ -31,11 +32,11 @@ describe('ZipkinDatasource', () => { it('should handle json file upload', async () => { const ds = new ZipkinDatasource(defaultSettings); ds.uploadedJson = JSON.stringify(mockJson); - const response = await ds - .query({ + const response = await lastValueFrom( + ds.query({ targets: [{ queryType: 'upload', refId: 'A' }], } as any) - .toPromise(); + ); const field = response.data[0].fields[0]; expect(field.name).toBe('traceID'); expect(field.type).toBe(FieldType.string); diff --git a/public/app/plugins/datasource/zipkin/datasource.ts b/public/app/plugins/datasource/zipkin/datasource.ts index 1de80cf39a7..cc839d95ee8 100644 --- a/public/app/plugins/datasource/zipkin/datasource.ts +++ b/public/app/plugins/datasource/zipkin/datasource.ts @@ -1,3 +1,6 @@ +import { lastValueFrom, Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { BackendSrvRequest, FetchResponse, getBackendSrv } from '@grafana/runtime'; import { DataQueryRequest, DataQueryResponse, @@ -6,9 +9,7 @@ import { FieldType, MutableDataFrame, } from '@grafana/data'; -import { BackendSrvRequest, FetchResponse, getBackendSrv } from '@grafana/runtime'; -import { Observable, of } from 'rxjs'; -import { map } from 'rxjs/operators'; + import { serializeParams } from '../../../core/utils/fetch'; import { apiPrefix } from './constants'; import { ZipkinQuery, ZipkinSpan } from './types'; @@ -40,7 +41,7 @@ export class ZipkinDatasource extends DataSourceApi { } async metadataRequest(url: string, params?: Record): Promise { - const res = await this.request(url, params, { hideFromInspector: true }).toPromise(); + const res = await lastValueFrom(this.request(url, params, { hideFromInspector: true })); return res.data; } diff --git a/public/app/plugins/panel/piechart/PieChartPanel.tsx b/public/app/plugins/panel/piechart/PieChartPanel.tsx index 7c9a5814241..4a7ef96d76d 100644 --- a/public/app/plugins/panel/piechart/PieChartPanel.tsx +++ b/public/app/plugins/panel/piechart/PieChartPanel.tsx @@ -15,12 +15,12 @@ import { PieChartLegendOptions, PieChartLegendValues, PieChartOptions } from './ import { Subscription } from 'rxjs'; import { LegendDisplayMode, + SeriesVisibilityChangeBehavior, usePanelContext, useTheme2, VizLayout, VizLegend, VizLegendItem, - SeriesVisibilityChangeBehavior, } from '@grafana/ui'; const defaultLegendOptions: PieChartLegendOptions = { @@ -140,9 +140,9 @@ function useSliceHighlightState() { setHighlightedTitle(undefined); }; - const subs = new Subscription() - .add(eventBus.getStream(DataHoverEvent).subscribe({ next: setHighlightedSlice })) - .add(eventBus.getStream(DataHoverClearEvent).subscribe({ next: resetHighlightedSlice })); + const subs = new Subscription(); + subs.add(eventBus.getStream(DataHoverEvent).subscribe({ next: setHighlightedSlice })); + subs.add(eventBus.getStream(DataHoverClearEvent).subscribe({ next: resetHighlightedSlice })); return () => { subs.unsubscribe(); diff --git a/scripts/ci-reference-docs-lint.sh b/scripts/ci-reference-docs-lint.sh index ef6e8add94e..09f54472c3f 100755 --- a/scripts/ci-reference-docs-lint.sh +++ b/scripts/ci-reference-docs-lint.sh @@ -28,7 +28,7 @@ if [ ! -d "$REPORT_PATH" ]; then fi fi -WARNINGS_COUNT="$(find "$REPORT_PATH" -type f -name \*.log -print0 | xargs -0 grep -o "Warning: " | wc -l | xargs)" +WARNINGS_COUNT="$(find "$REPORT_PATH" -type f -name \*.log -print0 | xargs -0 grep -o "Warning:.*(ae-\|Warning:.*(tsdoc-" | wc -l | xargs)" WARNINGS_COUNT_LIMIT=1074 if [ "$WARNINGS_COUNT" -gt $WARNINGS_COUNT_LIMIT ]; then diff --git a/yarn.lock b/yarn.lock index 627b59ef406..dc57ab0dcf4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -92,6 +92,15 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/generator@^7.10.5": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15" + integrity sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ== + dependencies: + "@babel/types" "^7.15.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.13.9", "@babel/generator@^7.14.5", "@babel/generator@^7.4.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" @@ -181,7 +190,7 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-function-name@^7.0.0", "@babel/helper-function-name@^7.14.5": +"@babel/helper-function-name@^7.0.0", "@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== @@ -282,13 +291,18 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-split-export-declaration@^7.14.5": +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== dependencies: "@babel/types" "^7.14.5" +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.14.9": + version "7.14.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" + integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== + "@babel/helper-validator-identifier@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" @@ -332,6 +346,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== +"@babel/parser@^7.10.5": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862" + integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA== + +"@babel/parser@~7.10.3": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" + integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e" @@ -1211,6 +1235,21 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@~7.10.3": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" + integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/parser" "^7.10.5" + "@babel/types" "^7.10.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.14.5", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" @@ -1219,6 +1258,23 @@ "@babel/helper-validator-identifier" "^7.14.5" to-fast-properties "^2.0.0" +"@babel/types@^7.10.5", "@babel/types@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" + integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + to-fast-properties "^2.0.0" + +"@babel/types@~7.10.3": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" + integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -5252,10 +5308,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== -"@types/stacktrace-js@^0.0.32": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/stacktrace-js/-/stacktrace-js-0.0.32.tgz#d23e4a36a5073d39487fbea8234cc6186862d389" - integrity sha512-SdxmlrHfO0BxgbBP9HZWMUo2rima8lwMjPiWm6S0dyKkDa5CseamktFhXg8umu3TPVBkSlX6ZoB5uUDJK89yvg== +"@types/stacktrace-js@^0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/stacktrace-js/-/stacktrace-js-0.0.33.tgz#9b027370ca161b89798f308af77438802546cb39" + integrity sha512-aqJ6QM9QThNL4dHBhwl1f9B0oDqiREkYLn9RldghUKsGeFWWGlCsqsRWxbh+hDvvmptMFqc4aIfFIGz9BBu8Qg== "@types/systemjs@^0.20.6": version "0.20.6" @@ -6921,6 +6977,15 @@ before-after-hook@^2.0.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== +bent@~7.3.6: + version "7.3.12" + resolved "https://registry.yarnpkg.com/bent/-/bent-7.3.12.tgz#e0a2775d4425e7674c64b78b242af4f49da6b035" + integrity sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w== + dependencies: + bytesish "^0.4.1" + caseless "~0.12.0" + is-stream "^2.0.0" + better-opn@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" @@ -7267,6 +7332,11 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytesish@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/bytesish/-/bytesish-0.4.4.tgz#f3b535a0f1153747427aee27256748cff92347e6" + integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ== + cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" @@ -7576,6 +7646,14 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@~4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chance@^1.0.10: version "1.1.4" resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.4.tgz#d8743bf8e40bb05e024c305ca1ff441195eb23db" @@ -12028,6 +12106,18 @@ glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glo once "^1.3.0" path-is-absolute "^1.0.0" +glob@~7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -18655,6 +18745,14 @@ prompts@^2.0.1, prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" +prompts@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.4" + promzard@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" @@ -20608,23 +20706,37 @@ rx-lite@^3.1.2: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -rxjs-spy@^7.5.1: - version "7.5.1" - resolved "https://registry.yarnpkg.com/rxjs-spy/-/rxjs-spy-7.5.1.tgz#1a9ef50bc8d7dd00d9ecf3c54c00929231eaf319" - integrity sha512-dJ9mO4HvW2r16PsU15Qsc0RVkG7pFrfyCNTGx3vrxWje3kIgZ6QjMVnWblQxbniZ32lwLk/2x9+D2O6GhgXV/w== +rxjs-report-usage@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/rxjs-report-usage/-/rxjs-report-usage-1.0.5.tgz#8de220b385f3f65a163e75cfcc2566b1545c9b3d" + integrity sha512-jZeg+TTkvP8kAv0tIQj3WOuIhYLi+Ig9mG8DCc+nJHQ1ObJr8IaeNPbJmXDRfHvH3MKQMBzboY4RbQ6jWt6cIg== + dependencies: + "@babel/parser" "~7.10.3" + "@babel/traverse" "~7.10.3" + "@babel/types" "~7.10.3" + bent "~7.3.6" + chalk "~4.1.0" + glob "~7.1.6" + prompts "~2.3.2" + +rxjs-spy@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/rxjs-spy/-/rxjs-spy-8.0.0.tgz#53f11d3b14d9abbfa437ca7cfa758497f633f04a" + integrity sha512-McjTZjTlCacHb2CKGS6vC4Sflb94UEpsEnKZjCydKZY7LQ0ywvnVGkRVwpqSfdna4/XXmgtcNCvhDVJ4P7I92w== dependencies: "@types/circular-json" "^0.4.0" - "@types/stacktrace-js" "^0.0.32" + "@types/stacktrace-js" "^0.0.33" circular-json "^0.5.0" error-stack-parser "^2.0.1" + rxjs-report-usage "^1.0.4" stacktrace-gps "^3.0.2" -rxjs@6.6.3: - version "6.6.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" - integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== +rxjs@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" + integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw== dependencies: - tslib "^1.9.0" + tslib "~2.1.0" rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.7: version "6.6.7" @@ -21069,7 +21181,7 @@ sinon@8.1.1: nise "^3.0.1" supports-color "^7.1.0" -sisteransi@^1.0.5: +sisteransi@^1.0.4, sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== @@ -22635,6 +22747,11 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"