@grafana/runtime
Version:
Grafana Runtime Library
1,587 lines (1,547 loc) • 61.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var data = require('@grafana/data');
var jsxRuntime = require('react/jsx-runtime');
var H = require('history');
var React = require('react');
var rxjs = require('rxjs');
var ui = require('@grafana/ui');
var lodash = require('lodash');
var reactUse = require('react-use');
var faroWebSdk = require('@grafana/faro-web-sdk');
var operators = require('rxjs/operators');
var e2eSelectors = require('@grafana/e2e-selectors');
var Skeleton = require('react-loading-skeleton');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
function _interopNamespaceCompat(e) {
if (e && typeof e === 'object' && 'default' in e) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var H__namespace = /*#__PURE__*/_interopNamespaceCompat(H);
var React__default = /*#__PURE__*/_interopDefaultCompat(React);
var Skeleton__default = /*#__PURE__*/_interopDefaultCompat(Skeleton);
function isFetchError(e) {
return typeof e === "object" && e !== null && "status" in e && "data" in e;
}
let singletonInstance$8;
const setBackendSrv = (instance) => {
singletonInstance$8 = instance;
};
const getBackendSrv = () => singletonInstance$8;
let singletonInstance$7;
function setDataSourceSrv(instance) {
singletonInstance$7 = instance;
}
function getDataSourceSrv() {
return singletonInstance$7;
}
let singletonInstance$6;
function setLocationSrv(instance) {
singletonInstance$6 = instance;
}
function getLocationSrv() {
return singletonInstance$6;
}
var EchoEventType = /* @__PURE__ */ ((EchoEventType2) => {
EchoEventType2["Performance"] = "performance";
EchoEventType2["MetaAnalytics"] = "meta-analytics";
EchoEventType2["Pageview"] = "pageview";
EchoEventType2["Interaction"] = "interaction";
EchoEventType2["ExperimentView"] = "experimentview";
EchoEventType2["GrafanaJavascriptAgent"] = "grafana-javascript-agent";
return EchoEventType2;
})(EchoEventType || {});
let singletonInstance$5;
function setEchoSrv(instance) {
if (singletonInstance$5 instanceof FakeEchoSrv) {
for (const item of singletonInstance$5.buffer) {
instance.addEvent(item.event, item.meta);
}
}
singletonInstance$5 = instance;
}
function getEchoSrv() {
if (!singletonInstance$5) {
singletonInstance$5 = new FakeEchoSrv();
}
return singletonInstance$5;
}
const registerEchoBackend = (backend) => {
getEchoSrv().addBackend(backend);
};
class FakeEchoSrv {
constructor() {
this.buffer = [];
}
flush() {
this.buffer = [];
}
addBackend(backend) {
}
addEvent(event, meta) {
this.buffer.push({ event, meta });
}
}
let singletonInstance$4;
const setTemplateSrv = (instance) => {
singletonInstance$4 = instance;
};
const getTemplateSrv = () => singletonInstance$4;
let singletonInstance$3;
const setGrafanaLiveSrv = (instance) => {
singletonInstance$3 = instance;
};
const getGrafanaLiveSrv = () => singletonInstance$3;
class GrafanaBootConfig {
constructor(options2) {
this.publicDashboardsEnabled = true;
this.snapshotEnabled = true;
this.datasources = {};
this.panels = {};
this.apps = {};
this.auth = {};
this.minRefreshInterval = "";
this.appUrl = "";
this.appSubUrl = "";
this.namespace = "default";
this.windowTitlePrefix = "";
this.externalUserMngLinkUrl = "";
this.externalUserMngLinkName = "";
this.externalUserMngInfo = "";
this.externalUserMngAnalytics = false;
this.externalUserMngAnalyticsParams = "";
this.allowOrgCreate = false;
this.feedbackLinksEnabled = true;
this.disableLoginForm = false;
this.defaultDatasource = "";
// UID
this.authProxyEnabled = false;
this.exploreEnabled = false;
this.queryHistoryEnabled = false;
this.helpEnabled = false;
this.profileEnabled = false;
this.newsFeedEnabled = true;
this.ldapEnabled = false;
this.jwtHeaderName = "";
this.jwtUrlLogin = false;
this.sigV4AuthEnabled = false;
this.azureAuthEnabled = false;
this.secureSocksDSProxyEnabled = false;
this.samlEnabled = false;
this.samlName = "";
this.autoAssignOrg = true;
this.verifyEmailEnabled = false;
this.oauth = {};
this.rbacEnabled = true;
this.disableUserSignUp = false;
this.loginHint = "";
this.passwordHint = "";
this.loginError = void 0;
this.viewersCanEdit = false;
this.disableSanitizeHtml = false;
this.trustedTypesDefaultPolicyEnabled = false;
this.cspReportOnlyEnabled = false;
this.liveEnabled = true;
this.liveMessageSizeLimit = 65536;
this.featureToggles = {};
this.anonymousEnabled = false;
this.anonymousDeviceLimit = void 0;
this.licenseInfo = {};
this.rendererAvailable = false;
this.rendererVersion = "";
this.rendererDefaultImageWidth = 1e3;
this.rendererDefaultImageHeight = 500;
this.rendererDefaultImageScale = 1;
this.supportBundlesEnabled = false;
this.http2Enabled = false;
this.grafanaJavascriptAgent = {
enabled: false,
customEndpoint: "",
apiKey: "",
allInstrumentationsEnabled: false,
errorInstrumentalizationEnabled: true,
consoleInstrumentalizationEnabled: false,
webVitalsInstrumentalizationEnabled: false,
tracingInstrumentalizationEnabled: false
};
this.pluginCatalogURL = "https://grafana.com/grafana/plugins/";
this.pluginAdminEnabled = true;
this.pluginAdminExternalManageEnabled = false;
this.pluginCatalogHiddenPlugins = [];
this.pluginCatalogManagedPlugins = [];
this.pluginCatalogPreinstalledPlugins = [];
this.pluginsCDNBaseURL = "";
this.expressionsEnabled = false;
this.awsAllowedAuthProviders = [];
this.awsAssumeRoleEnabled = false;
this.azure = {
managedIdentityEnabled: false,
workloadIdentityEnabled: false,
userIdentityEnabled: false,
userIdentityFallbackCredentialsEnabled: false,
azureEntraPasswordCredentialsEnabled: false
};
this.caching = {
enabled: false
};
this.unifiedAlertingEnabled = false;
this.unifiedAlerting = {
minInterval: "",
alertStateHistoryBackend: void 0,
alertStateHistoryPrimary: void 0,
recordingRulesEnabled: false,
defaultRecordingRulesTargetDatasourceUID: void 0
};
this.recordedQueries = {
enabled: true
};
this.featureHighlights = {
enabled: false
};
this.reporting = {
enabled: true
};
this.analytics = {
enabled: true
};
this.googleAnalytics4SendManualPageViews = false;
this.analyticsConsoleReporting = false;
this.dashboardPerformanceMetrics = [];
this.panelSeriesLimit = 0;
this.sqlConnectionLimits = {
maxOpenConns: 100,
maxIdleConns: 100,
connMaxLifetime: 14400
};
this.defaultDatasourceManageAlertsUiToggle = true;
this.defaultAllowRecordingRulesTargetAlertsUiToggle = true;
this.enableFrontendSandboxForPlugins = [];
this.cloudMigrationPollIntervalMs = 2e3;
this.exploreDefaultTimeOffset = "1h";
this.bootData = options2.bootData;
const defaults = {
datasources: {},
windowTitlePrefix: "Grafana - ",
panels: {},
playlist_timespan: "1m",
unsaved_changes_warning: true,
appUrl: "",
appSubUrl: "",
buildInfo: {
version: "1.0",
commit: "1",
env: "production"
},
viewersCanEdit: false,
disableSanitizeHtml: false
};
lodash.merge(this, defaults, options2);
this.buildInfo = options2.buildInfo || defaults.buildInfo;
if (this.dateFormats) {
data.systemDateFormats.update(this.dateFormats);
}
overrideFeatureTogglesFromUrl(this);
overrideFeatureTogglesFromLocalStorage(this);
this.theme2 = data.getThemeById(this.bootData.user.theme);
this.bootData.user.lightTheme = this.theme2.isLight;
this.theme = this.theme2.v1;
this.regionalFormat = options2.bootData.user.regionalFormat;
}
}
function overrideFeatureTogglesFromLocalStorage(config2) {
const featureToggles = config2.featureToggles;
const localStorageKey = "grafana.featureToggles";
const localStorageValue = window.localStorage.getItem(localStorageKey);
if (localStorageValue) {
const features = localStorageValue.split(",");
for (const feature of features) {
const [featureName, featureValue] = feature.split("=");
const toggleState = featureValue === "true" || featureValue === "1";
featureToggles[featureName] = toggleState;
console.log(`Setting feature toggle ${featureName} = ${toggleState} via localstorage`);
}
}
}
function overrideFeatureTogglesFromUrl(config2) {
if (window.location.href.indexOf("__feature") === -1) {
return;
}
const isDevelopment = config2.buildInfo.env === "development";
const safeRuntimeFeatureFlags = /* @__PURE__ */ new Set(["queryServiceFromUI", "dashboardSceneSolo"]);
const params = new URLSearchParams(window.location.search);
params.forEach((value, key) => {
if (key.startsWith("__feature.")) {
const featureToggles = config2.featureToggles;
const featureName = key.substring(10);
const toggleState = value === "true" || value === "";
if (toggleState !== featureToggles[key]) {
if (isDevelopment || safeRuntimeFeatureFlags.has(featureName)) {
featureToggles[featureName] = toggleState;
console.log(`Setting feature toggle ${featureName} = ${toggleState} via url`);
} else {
console.log(`Unable to change feature toggle ${featureName} via url in production.`);
}
}
}
});
}
let bootData = window.grafanaBootData;
if (!bootData) {
if (process.env.NODE_ENV !== "test") {
console.error("window.grafanaBootData was not set by the time config was initialized");
}
bootData = {
settings: {},
user: {},
navTree: []
};
}
const options = bootData.settings;
options.bootData = bootData;
const config = new GrafanaBootConfig(options);
class HistoryWrapper {
constructor(history) {
var _a;
this.history = history || (process.env.NODE_ENV === "test" ? H__namespace.createMemoryHistory({ initialEntries: ["/"] }) : H__namespace.createBrowserHistory({ basename: (_a = config.appSubUrl) != null ? _a : "/" }));
this.locationObservable = new rxjs.BehaviorSubject(this.history.location);
this.history.listen((location) => {
this.locationObservable.next(location);
});
this.partial = this.partial.bind(this);
this.push = this.push.bind(this);
this.replace = this.replace.bind(this);
this.getSearch = this.getSearch.bind(this);
this.getHistory = this.getHistory.bind(this);
this.getLocation = this.getLocation.bind(this);
}
getLocationObservable() {
return this.locationObservable.asObservable();
}
getHistory() {
return this.history;
}
getSearch() {
return new URLSearchParams(this.history.location.search);
}
partial(query, replace) {
const currentLocation = this.history.location;
const newQuery = this.getSearchObject();
for (const key in query) {
if (query[key] === null || query[key] === void 0) {
delete newQuery[key];
} else {
newQuery[key] = query[key];
}
}
const updatedUrl = data.urlUtil.renderUrl(currentLocation.pathname, newQuery);
if (replace) {
this.history.replace(updatedUrl, this.history.location.state);
} else {
this.history.push(updatedUrl, this.history.location.state);
}
}
push(location) {
this.history.push(location);
}
replace(location) {
this.history.replace(location);
}
reload() {
var _a;
const prevState = (_a = this.history.location.state) == null ? void 0 : _a.routeReloadCounter;
this.history.replace({
...this.history.location,
state: { routeReloadCounter: prevState ? prevState + 1 : 1 }
});
}
getLocation() {
return this.history.location;
}
getSearchObject() {
return locationSearchToObject(this.history.location.search);
}
/** @deprecated use partial, push or replace instead */
update(options) {
data.deprecationWarning("LocationSrv", "update", "partial, push or replace");
if (options.partial && options.query) {
this.partial(options.query, options.partial);
} else {
const newLocation = {
pathname: options.path
};
if (options.query) {
newLocation.search = data.urlUtil.toUrlParams(options.query);
}
if (options.replace) {
this.replace(newLocation);
} else {
this.push(newLocation);
}
}
}
}
function locationSearchToObject(search) {
let queryString = typeof search === "number" ? String(search) : search;
if (queryString.length > 0) {
if (queryString.startsWith("?")) {
return data.urlUtil.parseKeyValue(queryString.substring(1));
}
return data.urlUtil.parseKeyValue(queryString);
}
return {};
}
exports.locationService = new HistoryWrapper();
const setLocationService = (location) => {
if (process.env.NODE_ENV !== "test") {
throw new Error("locationService can be only overriden in test environment");
}
exports.locationService = location;
};
const navigationLog = ui.createLogger("Router");
const navigationLogger = navigationLog.logger;
ui.attachDebugger("location", exports.locationService, navigationLog);
const LocationServiceContext = React__default.default.createContext(void 0);
function useLocationService() {
const service = React.useContext(LocationServiceContext);
if (!service) {
throw new Error("useLocationService must be used within a LocationServiceProvider");
}
return service;
}
const LocationServiceProvider = ({
service,
children
}) => {
return /* @__PURE__ */ jsxRuntime.jsx(LocationServiceContext.Provider, { value: service, children });
};
class RefreshEvent extends data.BusEventBase {
}
RefreshEvent.type = "refresh";
class ThemeChangedEvent extends data.BusEventWithPayload {
}
ThemeChangedEvent.type = "theme-changed";
class TimeRangeUpdatedEvent extends data.BusEventWithPayload {
}
TimeRangeUpdatedEvent.type = "time-range-updated";
class CopyPanelEvent extends data.BusEventWithPayload {
}
CopyPanelEvent.type = "copy-panel";
let singletonInstance$2;
function setAppEvents(instance) {
singletonInstance$2 = instance;
}
function getAppEvents() {
return singletonInstance$2;
}
let singleton$3;
function setPluginComponentHook(hook) {
if (singleton$3 && process.env.NODE_ENV !== "test") {
throw new Error("setPluginComponentHook() function should only be called once, when Grafana is starting.");
}
singleton$3 = hook;
}
function usePluginComponent(componentId) {
if (!singleton$3) {
throw new Error("setPluginComponentHook(options) can only be used after the Grafana instance has started.");
}
return singleton$3(componentId);
}
let singleton$2;
function setPluginComponentsHook(hook) {
if (singleton$2 && process.env.NODE_ENV !== "test") {
throw new Error("setPluginComponentsHook() function should only be called once, when Grafana is starting.");
}
singleton$2 = hook;
}
function usePluginComponents(options) {
if (!singleton$2) {
throw new Error("setPluginComponentsHook(options) can only be used after the Grafana instance has started.");
}
return singleton$2(options);
}
let singleton$1;
function setPluginLinksHook(hook) {
if (singleton$1 && process.env.NODE_ENV !== "test") {
throw new Error("setPluginLinksHook() function should only be called once, when Grafana is starting.");
}
singleton$1 = hook;
}
function usePluginLinks(options) {
if (!singleton$1) {
throw new Error("setPluginLinksHook(options) can only be used after the Grafana instance has started.");
}
return singleton$1(options);
}
let singleton;
function setPluginFunctionsHook(hook) {
if (singleton && process.env.NODE_ENV !== "test") {
throw new Error("setUsePluginFunctionsHook() function should only be called once, when Grafana is starting.");
}
singleton = hook;
}
function usePluginFunctions(options) {
if (!singleton) {
throw new Error("usePluginFunctions(options) can only be used after the Grafana instance has started.");
}
return singleton(options);
}
function getObservablePluginLinks(options) {
{
throw new Error("getObservablePluginLinks() can only be used after the Grafana instance has started.");
}
}
function getObservablePluginComponents(options) {
{
throw new Error("getObservablePluginComponents() can only be used after the Grafana instance has started.");
}
}
function isPluginExtensionLink(extension) {
if (!extension) {
return false;
}
return extension.type === data.PluginExtensionTypes.link && ("path" in extension || "onClick" in extension);
}
function isPluginExtensionComponent(extension) {
if (!extension) {
return false;
}
return extension.type === data.PluginExtensionTypes.component && "component" in extension;
}
function getLimitedComponentsToRender({
props,
components,
limit,
pluginId
}) {
if (!components.length) {
return null;
}
const renderedComponents = [];
for (const Component of components) {
const { meta } = Component;
if (pluginId && typeof pluginId === "string" && pluginId !== meta.pluginId) {
continue;
}
if (pluginId && Array.isArray(pluginId) && !pluginId.includes(meta.pluginId)) {
continue;
}
if (pluginId instanceof RegExp && !pluginId.test(meta.pluginId)) {
continue;
}
if (limit === void 0) {
renderedComponents.push(Component);
continue;
}
if (React__default.default.createElement(Component, props) !== null) {
renderedComponents.push(Component);
}
if (renderedComponents.length >= limit) {
break;
}
}
return renderedComponents;
}
function renderLimitedComponents({
props,
components,
limit,
pluginId
}) {
const limitedComponents = getLimitedComponentsToRender({ props, components, limit, pluginId });
if (!(limitedComponents == null ? void 0 : limitedComponents.length)) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: limitedComponents.map((Component) => /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props }, Component.meta.id)) });
}
let singletonInstance$1 = null;
function setCurrentUser(instance) {
if (singletonInstance$1) {
throw new Error("User should only be set once, when Grafana is starting.");
}
singletonInstance$1 = instance;
}
function getCurrentUser() {
if (!singletonInstance$1) {
throw new Error("User can only be used after Grafana instance has started.");
}
return singletonInstance$1;
}
class RuntimeDataSource extends data.DataSourceApi {
constructor(pluginId, uid) {
const instanceSettings = {
name: "RuntimeDataSource-" + pluginId,
uid,
type: pluginId,
id: 1,
readOnly: true,
jsonData: {},
access: "direct",
meta: {
id: pluginId,
name: "RuntimeDataSource-" + pluginId,
type: data.PluginType.datasource,
info: {
author: {
name: ""
},
description: "",
links: [],
logos: {
large: "",
small: ""
},
screenshots: [],
updated: "",
version: ""
},
module: "",
baseUrl: ""
}
};
super(instanceSettings);
this.instanceSettings = instanceSettings;
}
testDatasource() {
return Promise.resolve({
status: "success",
message: ""
});
}
}
const ScopesContext = React.createContext(void 0);
function useScopes() {
var _a;
const context = React.useContext(ScopesContext);
reactUse.useObservable((_a = context == null ? void 0 : context.stateObservable) != null ? _a : new rxjs.Observable(), context == null ? void 0 : context.state);
return React.useMemo(() => {
return context ? {
state: context.state,
stateObservable: context.stateObservable,
changeScopes: context.changeScopes,
setReadOnly: context.setReadOnly,
setEnabled: context.setEnabled
} : void 0;
}, [context, context == null ? void 0 : context.state]);
}
var MetaAnalyticsEventName = /* @__PURE__ */ ((MetaAnalyticsEventName2) => {
MetaAnalyticsEventName2["DashboardView"] = "dashboard-view";
MetaAnalyticsEventName2["DataRequest"] = "data-request";
return MetaAnalyticsEventName2;
})(MetaAnalyticsEventName || {});
const isPageviewEvent = (event) => {
return Boolean(event.payload.page);
};
const isInteractionEvent = (event) => {
return Boolean(event.payload.interactionName);
};
const isExperimentViewEvent = (event) => {
return Boolean(event.payload.experimentId);
};
async function loadPluginCss(options) {
try {
const cssPath = config.bootData.user.theme === "light" ? options.light : options.dark;
return window.System.import(cssPath);
} catch (err) {
console.error(err);
}
}
let pluginImportUtils;
function setPluginImportUtils(utils) {
if (pluginImportUtils) {
throw new Error("pluginImportUtils should only be set once, when Grafana is starting.");
}
pluginImportUtils = utils;
}
function getPluginImportUtils() {
if (!pluginImportUtils) {
throw new Error("pluginImportUtils can only be used after Grafana instance has started.");
}
return pluginImportUtils;
}
const reportMetaAnalytics = (payload) => {
getEchoSrv().addEvent({
type: EchoEventType.MetaAnalytics,
payload
});
};
const reportPageview = () => {
var _a;
const location = exports.locationService.getLocation();
const page = `${(_a = config.appSubUrl) != null ? _a : ""}${location.pathname}${location.search}${location.hash}`;
getEchoSrv().addEvent({
type: EchoEventType.Pageview,
payload: {
page
}
});
};
const reportInteraction = (interactionName, properties) => {
if (config.reportingStaticContext && config.reportingStaticContext instanceof Object) {
properties = { ...properties, ...config.reportingStaticContext };
}
getEchoSrv().addEvent({
type: EchoEventType.Interaction,
payload: {
interactionName,
properties
}
});
};
const reportExperimentView = (id, group, variant) => {
getEchoSrv().addEvent({
type: EchoEventType.ExperimentView,
payload: {
experimentId: id,
experimentGroup: group,
experimentVariant: variant
}
});
};
const featureEnabled = (feature) => {
const { enabledFeatures } = config.licenseInfo;
return enabledFeatures && enabledFeatures[feature];
};
function logInfo(message, contexts) {
if (config.grafanaJavascriptAgent.enabled) {
faroWebSdk.faro.api.pushLog([message], {
level: faroWebSdk.LogLevel.INFO,
context: contexts
});
}
}
function logWarning(message, contexts) {
if (config.grafanaJavascriptAgent.enabled) {
faroWebSdk.faro.api.pushLog([message], {
level: faroWebSdk.LogLevel.WARN,
context: contexts
});
}
}
function logDebug(message, contexts) {
if (config.grafanaJavascriptAgent.enabled) {
faroWebSdk.faro.api.pushLog([message], {
level: faroWebSdk.LogLevel.DEBUG,
context: contexts
});
}
}
function logError(err, contexts) {
if (config.grafanaJavascriptAgent.enabled) {
faroWebSdk.faro.api.pushError(err, {
context: contexts
});
}
}
function logMeasurement(type, values, context) {
if (config.grafanaJavascriptAgent.enabled) {
faroWebSdk.faro.api.pushMeasurement(
{
type,
values
},
{ context }
);
}
}
function createMonitoringLogger(source, defaultContext) {
const createFullContext = (contexts) => ({
source,
...defaultContext,
...contexts
});
return {
/**
* Logs a debug message with optional additional context.
* @param {string} message - The debug message to be logged.
* @param {LogContext} [contexts] - Optional additional context to be included.
*/
logDebug: (message, contexts) => logDebug(message, createFullContext(contexts)),
/**
* Logs an informational message with optional additional context.
* @param {string} message - The informational message to be logged.
* @param {LogContext} [contexts] - Optional additional context to be included.
*/
logInfo: (message, contexts) => logInfo(message, createFullContext(contexts)),
/**
* Logs a warning message with optional additional context.
* @param {string} message - The warning message to be logged.
* @param {LogContext} [contexts] - Optional additional context to be included.
*/
logWarning: (message, contexts) => logWarning(message, createFullContext(contexts)),
/**
* Logs an error with optional additional context.
* @param {Error} error - The error object to be logged.
* @param {LogContext} [contexts] - Optional additional context to be included.
*/
logError: (error, contexts) => logError(error, createFullContext(contexts)),
/**
* Logs an measurement with optional additional context.
* @param {MeasurementEvent} measurement - The measurement object to be recorded.
* @param {LogContext} [contexts] - Optional additional context to be included.
*/
logMeasurement: (type, measurement, contexts) => logMeasurement(type, measurement, createFullContext(contexts))
};
}
function toDataQueryError(err) {
var _a, _b, _c;
const error = err || {};
if (!error.message) {
if (typeof err === "string") {
return { message: err };
}
let message = "Query error";
if (error.message) {
message = error.message;
} else if (error.data && error.data.message && ((_a = error.data) == null ? void 0 : _a.message) !== "Query data error") {
message = error.data.message;
} else if (((_b = error == null ? void 0 : error.data) == null ? void 0 : _b.message) === "Query data error" && ((_c = error == null ? void 0 : error.data) == null ? void 0 : _c.error)) {
message = error.data.error;
} else if (error.data && error.data.error) {
message = error.data.error;
} else if (error.status) {
message = `Query error: ${error.status} ${error.statusText}`;
}
error.message = message;
}
return error;
}
const cachedResponseNotice = { severity: "info", text: "Cached response" };
function toDataQueryResponse(res, queries) {
var _a, _b, _c, _d;
const rsp = { data: [], state: data.LoadingState.Done };
const traceId = "traceId" in res ? res.traceId : void 0;
if (traceId != null) {
rsp.traceIds = [traceId];
}
const fetchResponse = res;
if ((_a = fetchResponse.data) == null ? void 0 : _a.results) {
const results = fetchResponse.data.results;
const refIDs = (queries == null ? void 0 : queries.length) ? queries.map((q) => q.refId) : Object.keys(results);
const cachedResponse = isCachedResponse(fetchResponse);
const data$1 = [];
for (const refId of refIDs) {
const dr = results[refId];
if (!dr) {
continue;
}
dr.refId = refId;
data$1.push(dr);
}
for (const dr of data$1) {
if (dr.error) {
const errorObj = {
refId: dr.refId,
message: dr.error,
status: dr.status
};
if (traceId != null) {
errorObj.traceId = traceId;
}
if (!rsp.error) {
rsp.error = { ...errorObj };
}
if (rsp.errors) {
rsp.errors.push({ ...errorObj });
} else {
rsp.errors = [{ ...errorObj }];
}
rsp.state = data.LoadingState.Error;
}
if ((_b = dr.frames) == null ? void 0 : _b.length) {
for (let js of dr.frames) {
if (cachedResponse) {
js = addCacheNotice(js);
}
const df = data.dataFrameFromJSON(js);
if (!df.refId) {
df.refId = dr.refId;
}
rsp.data.push(df);
}
continue;
}
if ((_c = dr.series) == null ? void 0 : _c.length) {
for (const s of dr.series) {
if (!s.refId) {
s.refId = dr.refId;
}
rsp.data.push(data.toDataFrame(s));
}
}
if ((_d = dr.tables) == null ? void 0 : _d.length) {
for (const s of dr.tables) {
if (!s.refId) {
s.refId = dr.refId;
}
rsp.data.push(data.toDataFrame(s));
}
}
}
}
if (fetchResponse.status && fetchResponse.status !== 200) {
if (rsp.state !== data.LoadingState.Error) {
rsp.state = data.LoadingState.Error;
}
if (!rsp.error) {
rsp.error = toDataQueryError(res);
}
}
return rsp;
}
function isCachedResponse(res) {
const headers = res == null ? void 0 : res.headers;
if (!headers || !headers.get) {
return false;
}
return headers.get("X-Cache") === "HIT";
}
function addCacheNotice(frame) {
var _a, _b, _c, _d, _e, _f;
return {
...frame,
schema: {
...frame.schema,
fields: [...(_b = (_a = frame.schema) == null ? void 0 : _a.fields) != null ? _b : []],
meta: {
...(_c = frame.schema) == null ? void 0 : _c.meta,
notices: [...(_f = (_e = (_d = frame.schema) == null ? void 0 : _d.meta) == null ? void 0 : _e.notices) != null ? _f : [], cachedResponseNotice],
isCachedResponse: true
}
}
};
}
function frameToMetricFindValue(frame) {
if (!frame || !frame.length) {
return [];
}
const values = [];
let field = frame.fields.find((f) => f.type === data.FieldType.string);
if (!field) {
field = frame.fields.find((f) => f.type !== data.FieldType.time);
}
if (field) {
for (let i = 0; i < field.values.length; i++) {
values.push({ text: "" + field.values[i] });
}
}
return values;
}
function publicDashboardQueryHandler(request) {
const {
intervalMs,
maxDataPoints,
requestId,
panelId,
queryCachingTTL,
range: { from: fromRange, to: toRange }
} = request;
if (!request.targets.length) {
return rxjs.of({ data: [] });
}
const body = {
intervalMs,
maxDataPoints,
queryCachingTTL,
timeRange: {
from: fromRange.valueOf().toString(),
to: toRange.valueOf().toString(),
timezone: request.timezone
}
};
return getBackendSrv().fetch({
url: `/api/public/dashboards/${config.publicDashboardAccessToken}/panels/${panelId}/query`,
method: "POST",
data: body,
requestId
}).pipe(
rxjs.switchMap((raw) => {
return rxjs.of(toDataQueryResponse(raw, request.targets));
}),
rxjs.catchError((err) => {
return rxjs.of(toDataQueryResponse(err));
})
);
}
const baseURL = `/apis/userstorage.grafana.app/v0alpha1/namespaces/${config.namespace}/user-storage`;
async function apiRequest(requestOptions) {
try {
const { data: responseData, ...meta } = await rxjs.lastValueFrom(
getBackendSrv().fetch({
...requestOptions,
url: baseURL + requestOptions.url,
data: requestOptions.body,
showErrorAlert: false
})
);
return { data: responseData, meta };
} catch (error) {
return requestOptions.manageError ? requestOptions.manageError(error) : { error };
}
}
class UserStorage {
constructor(service) {
this.service = service;
this.userUID = config.bootData.user.uid === "" ? config.bootData.user.id.toString() : config.bootData.user.uid;
this.resourceName = `${service}:${this.userUID}`;
this.canUseUserStorage = config.bootData.user.isSignedIn;
}
async init() {
if (this.storageSpec !== void 0) {
return;
}
const userStorage = await apiRequest({
url: `/${this.resourceName}`,
method: "GET",
manageError: (error) => {
if (lodash.get(error, "status") === 404) {
this.storageSpec = null;
return { error: null };
}
return { error };
}
});
if ("error" in userStorage) {
return userStorage.error;
}
this.storageSpec = userStorage.data.spec;
return;
}
async getItem(key) {
if (!this.canUseUserStorage) {
return localStorage.getItem(`${this.resourceName}:${key}`);
}
await this.init();
if (!this.storageSpec) {
return localStorage.getItem(`${this.resourceName}:${key}`);
}
return this.storageSpec.data[key];
}
async setItem(key, value) {
if (!this.canUseUserStorage) {
localStorage.setItem(`${this.resourceName}:${key}`, value);
return;
}
const newData = { data: { [key]: value } };
const error = await this.init();
if (error) {
localStorage.setItem(`${this.resourceName}:${key}`, value);
return;
}
if (!this.storageSpec) {
await apiRequest({
url: `/`,
method: "POST",
body: {
metadata: { name: this.resourceName, labels: { user: this.userUID, service: this.service } },
spec: newData
},
manageError: (error2) => {
localStorage.setItem(`${this.resourceName}:${key}`, value);
return { error: error2 };
}
});
this.storageSpec = newData;
return;
}
this.storageSpec.data[key] = value;
await apiRequest({
headers: { "Content-Type": "application/merge-patch+json" },
url: `/${this.resourceName}`,
method: "PATCH",
body: { spec: newData },
manageError: (error2) => {
localStorage.setItem(`${this.resourceName}:${key}`, value);
return { error: error2 };
}
});
}
}
function usePluginUserStorage() {
const context = data.usePluginContext();
if (!context) {
throw new Error(`No PluginContext found. The usePluginUserStorage() hook can only be used from a plugin.`);
}
return new UserStorage(context == null ? void 0 : context.meta.id);
}
const ExpressionDatasourceRef = Object.freeze({
type: "__expr__",
uid: "__expr__",
name: "Expression"
});
function isExpressionReference(ref) {
if (!ref) {
return false;
}
const v = typeof ref === "string" ? ref : ref.type;
return v === ExpressionDatasourceRef.type || v === ExpressionDatasourceRef.name || v === "-100";
}
class HealthCheckError extends Error {
constructor(message, details) {
super(message);
this.details = details;
this.name = "HealthCheckError";
}
}
var HealthStatus = /* @__PURE__ */ ((HealthStatus2) => {
HealthStatus2["Unknown"] = "UNKNOWN";
HealthStatus2["OK"] = "OK";
HealthStatus2["Error"] = "ERROR";
return HealthStatus2;
})(HealthStatus || {});
class DataSourceWithBackend extends data.DataSourceApi {
constructor(instanceSettings) {
super(instanceSettings);
/**
* Optionally override the streaming behavior
*/
this.streamOptionsProvider = standardStreamOptionsProvider;
this.userStorage = new UserStorage(instanceSettings.type);
}
/**
* Ideally final -- any other implementation may not work as expected
*/
query(request) {
var _a;
if (config.publicDashboardAccessToken) {
return publicDashboardQueryHandler(request);
}
const { intervalMs, maxDataPoints, queryCachingTTL, range, requestId, hideFromInspector = false } = request;
let targets = request.targets;
let hasExpr = false;
const pluginIDs = /* @__PURE__ */ new Set();
const dsUIDs = /* @__PURE__ */ new Set();
const queries = targets.map((q) => {
var _a2, _b, _c;
let datasource = this.getRef();
let datasourceId = this.id;
let shouldApplyTemplateVariables = true;
if (isExpressionReference(q.datasource)) {
hasExpr = true;
return {
...q,
datasource: ExpressionDatasourceRef
};
}
if (q.datasource) {
const ds = getDataSourceSrv().getInstanceSettings(q.datasource, request.scopedVars);
if (!ds) {
throw new Error(`Unknown Datasource: ${JSON.stringify(q.datasource)}`);
}
const dsRef = (_a2 = ds.rawRef) != null ? _a2 : data.getDataSourceRef(ds);
const dsId = ds.id;
if (dsRef.uid !== datasource.uid || datasourceId !== dsId) {
datasource = dsRef;
datasourceId = dsId;
shouldApplyTemplateVariables = false;
}
}
if ((_b = datasource.type) == null ? void 0 : _b.length) {
pluginIDs.add(datasource.type);
}
if ((_c = datasource.uid) == null ? void 0 : _c.length) {
dsUIDs.add(datasource.uid);
}
return {
...shouldApplyTemplateVariables ? this.applyTemplateVariables(q, request.scopedVars, request.filters) : q,
datasource,
datasourceId,
// deprecated!
intervalMs,
maxDataPoints,
queryCachingTTL
};
});
if (!queries.length) {
return rxjs.of({ data: [] });
}
const body = {
queries,
from: range == null ? void 0 : range.from.valueOf().toString(),
to: range == null ? void 0 : range.to.valueOf().toString()
};
const headers = (_a = request.headers) != null ? _a : {};
headers["X-Plugin-Id" /* PluginID */] = Array.from(pluginIDs).join(", ");
headers["X-Datasource-Uid" /* DatasourceUID */] = Array.from(dsUIDs).join(", ");
let url = "/api/ds/query?ds_type=" + this.type;
if (config.featureToggles.queryServiceFromExplore && request.app === data.CoreApp.Explore) {
const isQueryServiceEnabled = config.featureToggles.queryService;
const isExperimentalAPIsEnabled = config.featureToggles.grafanaAPIServerWithExperimentalAPIs;
if (!isQueryServiceEnabled && !isExperimentalAPIsEnabled) {
console.warn("feature toggle queryServiceFromExplore also requires the queryService to be running");
} else {
url = `/apis/query.grafana.app/v0alpha1/namespaces/${config.namespace}/query?ds_type=${this.type}`;
}
}
if (config.featureToggles.queryServiceFromUI) {
if (!(config.featureToggles.queryService || config.featureToggles.grafanaAPIServerWithExperimentalAPIs)) {
console.warn("feature toggle queryServiceFromUI also requires the queryService to be running");
} else {
url = `/apis/query.grafana.app/v0alpha1/namespaces/${config.namespace}/query?ds_type=${this.type}`;
}
}
if (hasExpr) {
headers["X-Grafana-From-Expr" /* FromExpression */] = "true";
url += "&expression=true";
}
if (requestId) {
url += `&requestId=${requestId}`;
}
if (request.dashboardUID) {
headers["X-Dashboard-Uid" /* DashboardUID */] = request.dashboardUID;
if (request.dashboardTitle) {
headers["X-Dashboard-Title" /* DashboardTitle */] = request.dashboardTitle;
}
if (request.panelId) {
headers["X-Panel-Id" /* PanelID */] = `${request.panelId}`;
}
if (request.panelName) {
headers["X-Panel-Title" /* PanelTitle */] = request.panelName;
}
}
if (request.panelPluginId) {
headers["X-Panel-Plugin-Id" /* PanelPluginId */] = `${request.panelPluginId}`;
}
if (request.queryGroupId) {
headers["X-Query-Group-Id" /* QueryGroupID */] = `${request.queryGroupId}`;
}
if (request.skipQueryCache) {
headers["X-Cache-Skip" /* SkipQueryCache */] = "true";
}
return getBackendSrv().fetch({
url,
method: "POST",
data: body,
requestId,
hideFromInspector,
headers
}).pipe(
operators.switchMap((raw) => {
var _a2;
const rsp = toDataQueryResponse(raw, queries);
if (((_a2 = rsp.data) == null ? void 0 : _a2.length) && rsp.data.find((f) => {
var _a3;
return (_a3 = f.meta) == null ? void 0 : _a3.channel;
})) {
return toStreamingDataResponse(rsp, request, this.streamOptionsProvider);
}
return rxjs.of(rsp);
}),
operators.catchError((err) => {
return rxjs.of(toDataQueryResponse(err));
})
);
}
/** Get request headers with plugin ID+UID set */
getRequestHeaders() {
const headers = {};
headers["X-Plugin-Id" /* PluginID */] = this.type;
headers["X-Datasource-Uid" /* DatasourceUID */] = this.uid;
return headers;
}
/**
* Apply template variables for explore
*/
interpolateVariablesInQueries(queries, scopedVars, filters) {
return queries.map((q) => this.applyTemplateVariables(q, scopedVars, filters));
}
/**
* Override to apply template variables and adhoc filters. The result is usually also `TQuery`, but sometimes this can
* be used to modify the query structure before sending to the backend.
*
* NOTE: if you do modify the structure or use template variables, alerting queries may not work
* as expected
*
* @virtual
*/
applyTemplateVariables(query, scopedVars, filters) {
return query;
}
/**
* Make a GET request to the datasource resource path
*/
async getResource(path, params, options) {
const headers = this.getRequestHeaders();
const result = await rxjs.lastValueFrom(
getBackendSrv().fetch({
...options,
method: "GET",
headers: (options == null ? void 0 : options.headers) ? { ...options.headers, ...headers } : headers,
params: params != null ? params : options == null ? void 0 : options.params,
url: `/api/datasources/uid/${this.uid}/resources/${path}`
})
);
return result.data;
}
/**
* Send a POST request to the datasource resource path
*/
async postResource(path, data, options) {
const headers = this.getRequestHeaders();
const result = await rxjs.lastValueFrom(
getBackendSrv().fetch({
...options,
method: "POST",
headers: (options == null ? void 0 : options.headers) ? { ...options.headers, ...headers } : headers,
data: data != null ? data : { ...data },
url: `/api/datasources/uid/${this.uid}/resources/${path}`
})
);
return result.data;
}
/**
* Run the datasource healthcheck
*/
async callHealthCheck() {
return rxjs.lastValueFrom(
getBackendSrv().fetch({
method: "GET",
url: `/api/datasources/uid/${this.uid}/health`,
showErrorAlert: false,
headers: this.getRequestHeaders()
})
).then((v) => v.data).catch((err) => {
var _a, _b, _c, _d, _e;
let properties = {
plugin_id: ((_a = this.meta) == null ? void 0 : _a.id) || "",
plugin_version: ((_c = (_b = this.meta) == null ? void 0 : _b.info) == null ? void 0 : _c.version) || "",
datasource_healthcheck_status: ((_d = err == null ? void 0 : err.data) == null ? void 0 : _d.status) || "error",
datasource_healthcheck_message: ((_e = err == null ? void 0 : err.data) == null ? void 0 : _e.message) || ""
};
reportInteraction("datasource_health_check_completed", properties);
return err.data;
});
}
/**
* Checks the plugin health
* see public/app/features/datasources/state/actions.ts for what needs to be returned here
*/
async testDatasource() {
return this.callHealthCheck().then((res) => {
if (res.status === "OK" /* OK */) {
return {
status: "success",
message: res.message
};
}
return Promise.reject({
status: "error",
message: res.message,
error: new HealthCheckError(res.message, res.details)
});
});
}
}
function toStreamingDataResponse(rsp, req, getter) {
var _a;
const live = getGrafanaLiveSrv();
if (!live) {
return rxjs.of(rsp);
}
const staticdata = [];
const streams = [];
for (const f of rsp.data) {
const addr = data.parseLiveChannelAddress((_a = f.meta) == null ? void 0 : _a.channel);
if (addr) {
const frame = f;
streams.push(
live.getDataStream({
addr,
buffer: getter(req, frame),
frame: data.dataFrameToJSON(f)
})
);
} else {
staticdata.push(f);
}
}
if (staticdata.length) {
streams.push(rxjs.of({ ...rsp, data: staticdata }));
}
if (streams.length === 1) {
return streams[0];
}
return rxjs.merge(...streams);
}
const standardStreamOptionsProvider = (request, frame) => {
var _a, _b;
const opts = {
maxLength: (_a = request.maxDataPoints) != null ? _a : 500,
action: data.StreamingFrameAction.Append
};
if (((_b = request.rangeRaw) == null ? void 0 : _b.to) === "now") {
opts.maxDelta = request.range.to.valueOf() - request.range.from.valueOf();
}
return opts;
};
DataSourceWithBackend = data.makeClassES5Compatible(DataSourceWithBackend);
let PanelRenderer = () => {
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: "PanelRenderer can only be used after Grafana instance has been started." });
};
let PanelDataErrorView = ({ message }) => {
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
"Unable to render data: ",
message,
"."
] });
};
let factory;
const setQueryRunnerFactory = (instance) => {
if (factory) {
throw new Error("Runner should only be set when Grafana is starting.");
}
factory = instance;
};
const createQueryRunner = () => {
if (!factory) {
throw new Error("`createQueryRunner` can only be used after Grafana instance has started.");
}
return factory();
};
let runRequest;
function setRunRequest(fn) {
if (runRequest && process.env.NODE_ENV !== "test") {
throw new Error("runRequest function should only be set once, when Grafana is starting.");
}
runRequest = fn;
}
function getRunRequest() {
if (!runRequest) {
throw new Error("getRunRequest can only be used after Grafana instance has started.");
}
return runRequest;
}
let PluginPage = ({ children }) => {
return /* @__PURE__ */ jsxRuntime.jsx("div", { children });
};
class DataSourcePicker extends React.PureComponent {
constructor(props) {
super(props);
this.dataSourceSrv = getDataSourceSrv();
this.state = {};
this.onChange = (item, actionMeta) => {
if (actionMeta.action === "clear" && this.props.onClear) {
this.props.onClear();
return;
}
const dsSettings = this.dataSourceSrv.getInstanceSettings(item.value);
if (dsSettings) {
this.props.onChange(dsSettings);
this.setState({ error: void 0 });
}
};
}
componentDidMount() {
const { current } = this.props;
const dsSettings = this.dataSourceSrv.getInstanceSettings(current);
if (!dsSettings) {
this.setState({ error: "Could not find data source " + current });
}
}
getCurrentValue() {
const { current, hideTextValue, noDefault } = this.props;
if (!current && noDefault) {
return;
}
const ds = this.dataSourceSrv.getInstanceSettings(current);
if (ds) {
return {
label: ds.name.slice(0, 37),
value: ds.uid,
imgUrl: ds.meta.info.logos.small,
hideText: hideTextValue,
meta: ds.meta
};
}
const uid = data.getDataSourceUID(current);
if (uid === ExpressionDatasourceRef.uid || uid === ExpressionDatasourceRef.name) {
return { label: uid, value: uid, hideText: hideTextValue };
}
return {
label: (uid != null ? uid : "no name") + " - not found",
value: uid != null ? uid : void 0,
imgUrl: "",
hideText: hideTextValue
};
}
getDataSourceOptions() {
const { alerting, tracing, metrics, mixed, dashboard, variables, annotations, pluginId, type, filter, logs } = this.props;
const options = this.dataSourceSrv.getList({
alerting,
tracing,
metrics,
logs,
dashboard,
mixed,
variables,
annotations,
pluginId,
filter,
type
}).map((ds) => ({
value: ds.name,
label: `${ds.name}${ds.isDefault ? " (default)" : ""}`,
imgUrl: ds.meta.info.logos.small,
meta: ds.meta
}));
return options;
}
render() {
const {
autoFocus,
onBlur,
onClear,
openMenuOnFocus,
placeholder,
width,
inputId,
disabled = false,
isLoading = false
} = this.props;
const { error } = this.state;
const options = this.getDataSourceOptions();
const value = this.getCurrentValue();
const isClearable = typeof onClear === "function";
return /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
"aria-label": "Data source picker select container",
"data-testid": e2eSelectors.selectors.components.DataSourcePicker.container,
children: /* @__PURE__ */ jsxRuntime.jsx(
ui.Select,
{
isLoading,
disabled,
"aria-label": "Select a data source",
"data-testid": e2eSelectors.selectors.components.DataSourcePicker.inputV2,
inputId: inputId || "data-source-picker",
className: "ds-picker select-container",
isMulti: false,
isClearable,
backspaceRemovesValue: false,
onChange: this.onChange,
options,
autoFocus,
onBlur,
width,
openMenuOnFocus,
maxMenuHeight: 500,
placeholder,
noOptionsMessage: "No datasources found",
value: value != null ? value : null,
invalid: Boolean(error) || Boolean(this.props.invalid),
getOptionLabel: (o) => {
if (o.meta && data.isUnsignedPluginSignature(o.meta.signature) && o !== value) {
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { alignItems: "center", justifyContent: "space-between", children: [