grafana/public/app/features/dashboard/services/PublicDashboardDataSource.ts
2023-06-14 17:35:01 -03:00

168 lines
4.8 KiB
TypeScript

import { catchError, from, Observable, of, switchMap } from 'rxjs';
import {
AnnotationQuery,
DataQuery,
DataQueryRequest,
DataQueryResponse,
TestDataSourceResponse,
DataSourceApi,
DataSourceJsonData,
DataSourcePluginMeta,
DataSourceRef,
toDataFrame,
} from '@grafana/data';
import { BackendDataSourceResponse, getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
import { GrafanaQueryType } from '../../../plugins/datasource/grafana/types';
import { MIXED_DATASOURCE_NAME } from '../../../plugins/datasource/mixed/MixedDataSource';
import { GRAFANA_DATASOURCE_NAME } from '../../alerting/unified/utils/datasource';
export const PUBLIC_DATASOURCE = '-- Public --';
export const DEFAULT_INTERVAL = '1min';
export class PublicDashboardDataSource extends DataSourceApi<DataQuery, DataSourceJsonData, {}> {
constructor(datasource: DataSourceRef | string | DataSourceApi | null) {
let meta = {} as DataSourcePluginMeta;
if (PublicDashboardDataSource.isMixedDatasource(datasource)) {
meta.mixed = true;
}
super({
name: 'public-ds',
id: 0,
type: 'public-ds',
meta,
uid: PublicDashboardDataSource.resolveUid(datasource),
jsonData: {},
access: 'proxy',
readOnly: true,
});
this.interval = PublicDashboardDataSource.resolveInterval(datasource);
this.annotations = {
prepareQuery(anno: AnnotationQuery): DataQuery | undefined {
return { ...anno, queryType: GrafanaQueryType.Annotations, refId: 'anno' };
},
};
}
/**
* Get the datasource uid based on the many types a datasource can be.
*/
private static resolveUid(datasource: DataSourceRef | string | DataSourceApi | null): string {
if (typeof datasource === 'string') {
return datasource;
}
return datasource?.uid ?? PUBLIC_DATASOURCE;
}
private static isMixedDatasource(datasource: DataSourceRef | string | DataSourceApi | null): boolean {
if (typeof datasource === 'string' || datasource == null) {
return false;
}
return datasource?.uid === MIXED_DATASOURCE_NAME;
}
private static resolveInterval(datasource: DataSourceRef | string | DataSourceApi | null): string {
if (typeof datasource === 'string' || datasource == null) {
return DEFAULT_INTERVAL;
}
const interval = 'interval' in datasource ? datasource.interval : undefined;
return interval ?? DEFAULT_INTERVAL;
}
/**
* Ideally final -- any other implementation may not work as expected
*/
query(request: DataQueryRequest<DataQuery>): Observable<DataQueryResponse> {
const {
intervalMs,
maxDataPoints,
requestId,
publicDashboardAccessToken,
panelId,
queryCachingTTL,
range: { from: fromRange, to: toRange },
} = request;
let queries: DataQuery[];
// Return early if no queries exist
if (!request.targets.length) {
return of({ data: [] });
}
// Its an annotations query
// Currently, annotations requests come in one at a time, so there will only be one target
const target = request.targets[0];
if (target.queryType === GrafanaQueryType.Annotations) {
if (target?.datasource?.uid === GRAFANA_DATASOURCE_NAME) {
return from(this.getAnnotations(request));
}
return of({ data: [] });
}
// Its a datasource query
else {
const body = {
intervalMs,
maxDataPoints,
queryCachingTTL,
timeRange: {
from: fromRange.valueOf().toString(),
to: toRange.valueOf().toString(),
timezone: this.getBrowserTimezone(),
},
};
return getBackendSrv()
.fetch<BackendDataSourceResponse>({
url: `/api/public/dashboards/${publicDashboardAccessToken}/panels/${panelId}/query`,
method: 'POST',
data: body,
requestId,
})
.pipe(
switchMap((raw) => {
return of(toDataQueryResponse(raw, queries));
}),
catchError((err) => {
return of(toDataQueryResponse(err));
})
);
}
}
async getAnnotations(request: DataQueryRequest<DataQuery>): Promise<DataQueryResponse> {
const {
publicDashboardAccessToken: accessToken,
range: { to, from },
} = request;
const params = {
from: from.valueOf(),
to: to.valueOf(),
};
const annotations = accessToken
? await getBackendSrv().get(`/api/public/dashboards/${accessToken}/annotations`, params)
: [];
return { data: [toDataFrame(annotations)] };
}
testDatasource(): Promise<TestDataSourceResponse> {
return Promise.resolve({ message: '', status: '' });
}
// Try to get the browser timezone otherwise return blank
getBrowserTimezone(): string {
return window.Intl?.DateTimeFormat().resolvedOptions()?.timeZone || '';
}
}