123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- import {
- AppEvents,
- DataSourceApi,
- DataSourceInstanceSettings,
- DataSourceRef,
- DataSourceSelectItem,
- ScopedVars,
- } from '@grafana/data';
- import {
- GetDataSourceListFilters,
- DataSourceSrv as DataSourceService,
- getDataSourceSrv as getDataSourceService,
- TemplateSrv,
- getTemplateSrv,
- getLegacyAngularInjector,
- getBackendSrv,
- } from '@grafana/runtime';
- import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
- import appEvents from 'app/core/app_events';
- import config from 'app/core/config';
- import {
- dataSource as expressionDatasource,
- ExpressionDatasourceUID,
- instanceSettings as expressionInstanceSettings,
- } from 'app/features/expressions/ExpressionDatasource';
- import { DataSourceVariableModel } from '../variables/types';
- import { importDataSourcePlugin } from './plugin_loader';
- export class DatasourceSrv implements DataSourceService {
- private datasources: Record<string, DataSourceApi> = {}; // UID
- private settingsMapByName: Record<string, DataSourceInstanceSettings> = {};
- private settingsMapByUid: Record<string, DataSourceInstanceSettings> = {};
- private settingsMapById: Record<string, DataSourceInstanceSettings> = {};
- private defaultName = ''; // actually UID
- constructor(private templateSrv: TemplateSrv = getTemplateSrv()) {}
- init(settingsMapByName: Record<string, DataSourceInstanceSettings>, defaultName: string) {
- this.datasources = {};
- this.settingsMapByUid = {};
- this.settingsMapByName = settingsMapByName;
- this.defaultName = defaultName;
- for (const dsSettings of Object.values(settingsMapByName)) {
- if (!dsSettings.uid) {
- dsSettings.uid = dsSettings.name; // -- Grafana --, -- Mixed etc
- }
- this.settingsMapByUid[dsSettings.uid] = dsSettings;
- this.settingsMapById[dsSettings.id] = dsSettings;
- }
- // Preload expressions
- this.datasources[ExpressionDatasourceRef.type] = expressionDatasource as any;
- this.datasources[ExpressionDatasourceUID] = expressionDatasource as any;
- this.settingsMapByUid[ExpressionDatasourceRef.uid] = expressionInstanceSettings;
- this.settingsMapByUid[ExpressionDatasourceUID] = expressionInstanceSettings;
- }
- getDataSourceSettingsByUid(uid: string): DataSourceInstanceSettings | undefined {
- return this.settingsMapByUid[uid];
- }
- getInstanceSettings(
- ref: string | null | undefined | DataSourceRef,
- scopedVars?: ScopedVars
- ): DataSourceInstanceSettings | undefined {
- const isstring = typeof ref === 'string';
- let nameOrUid = isstring ? (ref as string) : ((ref as any)?.uid as string | undefined);
- if (nameOrUid === 'default' || nameOrUid === null || nameOrUid === undefined) {
- if (!isstring && ref) {
- const type = (ref as any)?.type as string;
- if (type === ExpressionDatasourceRef.type) {
- return expressionDatasource.instanceSettings;
- } else if (type) {
- console.log('FIND Default instance for datasource type?', ref);
- }
- }
- return this.settingsMapByUid[this.defaultName] ?? this.settingsMapByName[this.defaultName];
- }
- // Complex logic to support template variable data source names
- // For this we just pick the current or first data source in the variable
- if (nameOrUid[0] === '$') {
- const interpolatedName = this.templateSrv.replace(nameOrUid, scopedVars, variableInterpolation);
- let dsSettings;
- if (interpolatedName === 'default') {
- dsSettings = this.settingsMapByName[this.defaultName];
- } else {
- dsSettings = this.settingsMapByUid[interpolatedName] ?? this.settingsMapByName[interpolatedName];
- }
- if (!dsSettings) {
- return undefined;
- }
- // Return an instance with un-interpolated values for name and uid
- return {
- ...dsSettings,
- isDefault: false,
- name: nameOrUid,
- uid: nameOrUid,
- rawRef: { type: dsSettings.type, uid: dsSettings.uid },
- };
- }
- return this.settingsMapByUid[nameOrUid] ?? this.settingsMapByName[nameOrUid];
- }
- get(ref?: string | DataSourceRef | null, scopedVars?: ScopedVars): Promise<DataSourceApi> {
- let nameOrUid = typeof ref === 'string' ? (ref as string) : ((ref as any)?.uid as string | undefined);
- if (!nameOrUid) {
- return this.get(this.defaultName);
- }
- // Check if nameOrUid matches a uid and then get the name
- const byName = this.settingsMapByName[nameOrUid];
- if (byName) {
- nameOrUid = byName.uid;
- }
- // This check is duplicated below, this is here mainly as performance optimization to skip interpolation
- if (this.datasources[nameOrUid]) {
- return Promise.resolve(this.datasources[nameOrUid]);
- }
- // Interpolation here is to support template variable in data source selection
- nameOrUid = this.templateSrv.replace(nameOrUid, scopedVars, variableInterpolation);
- if (nameOrUid === 'default' && this.defaultName !== 'default') {
- return this.get(this.defaultName);
- }
- if (this.datasources[nameOrUid]) {
- return Promise.resolve(this.datasources[nameOrUid]);
- }
- return this.loadDatasource(nameOrUid);
- }
- async loadDatasource(key: string): Promise<DataSourceApi<any, any>> {
- if (this.datasources[key]) {
- return Promise.resolve(this.datasources[key]);
- }
- // find the metadata
- const instanceSettings = this.settingsMapByUid[key] ?? this.settingsMapByName[key] ?? this.settingsMapById[key];
- if (!instanceSettings) {
- return Promise.reject({ message: `Datasource ${key} was not found` });
- }
- try {
- const dsPlugin = await importDataSourcePlugin(instanceSettings.meta);
- // check if its in cache now
- if (this.datasources[key]) {
- return this.datasources[key];
- }
- // If there is only one constructor argument it is instanceSettings
- const useAngular = dsPlugin.DataSourceClass.length !== 1;
- let instance: DataSourceApi<any, any>;
- if (useAngular) {
- instance = getLegacyAngularInjector().instantiate(dsPlugin.DataSourceClass, {
- instanceSettings,
- });
- } else {
- instance = new dsPlugin.DataSourceClass(instanceSettings);
- }
- instance.components = dsPlugin.components;
- // Some old plugins does not extend DataSourceApi so we need to manually patch them
- if (!(instance instanceof DataSourceApi)) {
- const anyInstance = instance as any;
- anyInstance.name = instanceSettings.name;
- anyInstance.id = instanceSettings.id;
- anyInstance.type = instanceSettings.type;
- anyInstance.meta = instanceSettings.meta;
- anyInstance.uid = instanceSettings.uid;
- (instance as any).getRef = DataSourceApi.prototype.getRef;
- }
- // store in instance cache
- this.datasources[key] = instance;
- this.datasources[instance.uid] = instance;
- return instance;
- } catch (err) {
- appEvents.emit(AppEvents.alertError, [instanceSettings.name + ' plugin failed', err.toString()]);
- return Promise.reject({ message: `Datasource: ${key} was not found` });
- }
- }
- getAll(): DataSourceInstanceSettings[] {
- return Object.values(this.settingsMapByName);
- }
- getList(filters: GetDataSourceListFilters = {}): DataSourceInstanceSettings[] {
- const base = Object.values(this.settingsMapByName).filter((x) => {
- if (x.meta.id === 'grafana' || x.meta.id === 'mixed' || x.meta.id === 'dashboard') {
- return false;
- }
- if (filters.metrics && !x.meta.metrics) {
- return false;
- }
- if (filters.tracing && !x.meta.tracing) {
- return false;
- }
- if (filters.logs && x.meta.category !== 'logging' && !x.meta.logs) {
- return false;
- }
- if (filters.annotations && !x.meta.annotations) {
- return false;
- }
- if (filters.alerting && !x.meta.alerting) {
- return false;
- }
- if (filters.pluginId && x.meta.id !== filters.pluginId) {
- return false;
- }
- if (filters.filter && !filters.filter(x)) {
- return false;
- }
- if (filters.type && (Array.isArray(filters.type) ? !filters.type.includes(x.type) : filters.type !== x.type)) {
- return false;
- }
- if (
- !filters.all &&
- x.meta.metrics !== true &&
- x.meta.annotations !== true &&
- x.meta.tracing !== true &&
- x.meta.logs !== true &&
- x.meta.alerting !== true
- ) {
- return false;
- }
- return true;
- });
- if (filters.variables) {
- for (const variable of this.templateSrv.getVariables().filter((variable) => variable.type === 'datasource')) {
- const dsVar = variable as DataSourceVariableModel;
- const first = dsVar.current.value === 'default' ? this.defaultName : dsVar.current.value;
- const dsName = first as unknown as string;
- const dsSettings = this.settingsMapByName[dsName];
- if (dsSettings) {
- const key = `$\{${variable.name}\}`;
- base.push({
- ...dsSettings,
- name: key,
- uid: key,
- });
- }
- }
- }
- const sorted = base.sort((a, b) => {
- if (a.name.toLowerCase() > b.name.toLowerCase()) {
- return 1;
- }
- if (a.name.toLowerCase() < b.name.toLowerCase()) {
- return -1;
- }
- return 0;
- });
- if (!filters.pluginId && !filters.alerting) {
- if (filters.mixed) {
- const mixedInstanceSettings = this.getInstanceSettings('-- Mixed --');
- if (mixedInstanceSettings) {
- base.push(mixedInstanceSettings);
- }
- }
- if (filters.dashboard) {
- const dashboardInstanceSettings = this.getInstanceSettings('-- Dashboard --');
- if (dashboardInstanceSettings) {
- base.push(dashboardInstanceSettings);
- }
- }
- if (!filters.tracing) {
- const grafanaInstanceSettings = this.getInstanceSettings('-- Grafana --');
- if (grafanaInstanceSettings) {
- base.push(grafanaInstanceSettings);
- }
- }
- }
- return sorted;
- }
- /**
- * @deprecated use getList
- * */
- getExternal(): DataSourceInstanceSettings[] {
- return this.getList();
- }
- /**
- * @deprecated use getList
- * */
- getAnnotationSources() {
- return this.getList({ annotations: true, variables: true }).map((x) => {
- return {
- name: x.name,
- value: x.name,
- meta: x.meta,
- };
- });
- }
- /**
- * @deprecated use getList
- * */
- getMetricSources(options?: { skipVariables?: boolean }): DataSourceSelectItem[] {
- return this.getList({ metrics: true, variables: !options?.skipVariables }).map((x) => {
- return {
- name: x.name,
- value: x.name,
- meta: x.meta,
- };
- });
- }
- async reload() {
- const settings = await getBackendSrv().get('/api/frontend/settings');
- config.datasources = settings.datasources;
- config.defaultDatasource = settings.defaultDatasource;
- this.init(settings.datasources, settings.defaultDatasource);
- }
- }
- export function variableInterpolation(value: any[]) {
- if (Array.isArray(value)) {
- return value[0];
- }
- return value;
- }
- export const getDatasourceSrv = (): DatasourceSrv => {
- return getDataSourceService() as DatasourceSrv;
- };
|