@grafana/runtime
Version:
Grafana Runtime Library
1,548 lines (1,511 loc) • 71.6 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 instance;
function setAngularLoader(v) {
instance = v;
}
function getAngularLoader() {
return instance;
}
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 singleton$6;
const setLegacyAngularInjector = (instance) => {
singleton$6 = instance;
};
const getLegacyAngularInjector = () => singleton$6;
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.newPanelTitle = "";
this.externalUserMngLinkUrl = "";
this.externalUserMngLinkName = "";
this.externalUserMngInfo = "";
this.externalUserMngAnalytics = false;
this.externalUserMngAnalyticsParams = "";
this.allowOrgCreate = false;
this.feedbackLinksEnabled = true;
this.disableLoginForm = false;
this.defaultDatasource = "";
// UID
this.angularSupportEnabled = false;
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 = undefined;
this.viewersCanEdit = false;
this.editorsCanAdmin = false;
this.disableSanitizeHtml = false;
this.trustedTypesDefaultPolicyEnabled = false;
this.cspReportOnlyEnabled = false;
this.liveEnabled = true;
this.featureToggles = {};
this.anonymousEnabled = false;
this.anonymousDeviceLimit = undefined;
this.licenseInfo = {};
this.rendererAvailable = false;
this.rendererVersion = "";
this.rendererDefaultImageWidth = 1e3;
this.rendererDefaultImageHeight = 500;
this.rendererDefaultImageScale = 1;
this.secretsManagerPluginEnabled = false;
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: undefined,
alertStateHistoryPrimary: undefined
};
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.sqlConnectionLimits = {
maxOpenConns: 100,
maxIdleConns: 100,
connMaxLifetime: 14400
};
this.defaultDatasourceManageAlertsUiToggle = true;
this.enableFrontendSandboxForPlugins = [];
this.cloudMigrationFeedbackURL = "";
this.cloudMigrationPollIntervalMs = 2e3;
this.exploreDefaultTimeOffset = "1h";
this.bootData = options2.bootData;
const defaults = {
datasources: {},
windowTitlePrefix: "Grafana - ",
panels: {},
newPanelTitle: "Panel Title",
playlist_timespan: "1m",
unsaved_changes_warning: true,
appUrl: "",
appSubUrl: "",
buildInfo: {
version: "1.0",
commit: "1",
env: "production"
},
viewersCanEdit: false,
editorsCanAdmin: 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);
if (this.featureToggles.disableAngular) {
this.angularSupportEnabled = false;
}
this.theme2 = data.getThemeById(this.bootData.user.theme);
this.bootData.user.lightTheme = this.theme2.isLight;
this.theme = this.theme2.v1;
}
}
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.`);
}
}
}
});
}
const bootData = window.grafanaBootData || {
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] === undefined) {
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 ? undefined : _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(undefined);
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;
}
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 ALLOW_ROUTES = [
/(^\/d\/)/,
// dashboards
/^\/explore/,
// explore + explore metrics
/^\/a\/[^\/]+/,
// app plugins
/^\/alerting/
];
class SidecarService_EXPERIMENTAL {
constructor(mainLocationService2) {
// If true we don't close the sidecar when user navigates to another app or part of Grafana from where the sidecar
// was opened.
this.follow = false;
this.mainOnAllowedRoute = false;
this._initialContext = new rxjs.BehaviorSubject(undefined);
this.mainLocationService = mainLocationService2;
this.sidecarLocationService = new HistoryWrapper(
createLocationStorageHistory({ storageKey: "grafana.sidecar.history" })
);
this.handleMainLocationChanges();
}
assertFeatureEnabled() {
if (!config.featureToggles.appSidecar) {
console.warn("The `appSidecar` feature toggle is not enabled, doing nothing.");
return false;
}
return true;
}
updateMainLocationWhenOpened() {
var _a;
const pathname = this.mainLocationService.getLocation().pathname;
for (const route of ALLOW_ROUTES) {
const match = (_a = pathname.match(route)) == null ? undefined : _a[0];
if (match) {
this.mainLocationWhenOpened = match;
return;
}
}
}
/**
* Every time the main location changes we check if we should keep the sidecar open or close it based on list
* of allowed routes and also based on the follow flag when opening the app.
*/
handleMainLocationChanges() {
this.mainOnAllowedRoute = ALLOW_ROUTES.some(
(prefix) => this.mainLocationService.getLocation().pathname.match(prefix)
);
this.mainLocationService.getLocationObservable().subscribe((location) => {
this.mainOnAllowedRoute = ALLOW_ROUTES.some((prefix) => location.pathname.match(prefix));
if (!this.activePluginId) {
return;
}
if (!this.mainOnAllowedRoute) {
this.closeApp();
return;
}
const isTheSameLocation = Boolean(
this.mainLocationWhenOpened && location.pathname.startsWith(this.mainLocationWhenOpened)
);
if (!(isTheSameLocation || this.follow)) {
this.closeApp();
}
});
}
/**
* Get current app id of the app in sidecar. This is most probably provisional. In the future
* this should be driven by URL addressing so that routing for the apps don't change. Useful just internally
* to decide which app to render.
*
* @experimental
*/
get activePluginIdObservable() {
return this.sidecarLocationService.getLocationObservable().pipe(
rxjs.map((val) => {
return getPluginIdFromUrl((val == null ? undefined : val.pathname) || "");
})
);
}
/**
* Get initial context which is whatever data was passed when calling the 'openApp' function. This is meant as
* a way for the app to initialize it's state based on some context that is passed to it from the primary app.
*
* @experimental
*/
get initialContextObservable() {
return this._initialContext.asObservable();
}
// Get the current value of the subject, this is needed if we want the value immediately. For example if used in
// hook in react with useObservable first render would return undefined even if the behaviourSubject has some
// value which will be emitted in the next tick and thus next rerender.
get initialContext() {
return this._initialContext.getValue();
}
/**
* @experimental
*/
get activePluginId() {
return getPluginIdFromUrl(this.sidecarLocationService.getLocation().pathname);
}
getLocationService() {
return this.sidecarLocationService;
}
/**
* Opens an app in a sidecar. You can also pass some context object that will be then available to the app.
* @deprecated
* @experimental
*/
openApp(pluginId, context) {
if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) {
return;
}
this._initialContext.next(context);
this.openAppV3({ pluginId, follow: false });
}
/**
* Opens an app in a sidecar. You can also relative path inside the app to open.
* @deprecated
* @experimental
*/
openAppV2(pluginId, path) {
this.openAppV3({ pluginId, path, follow: false });
}
/**
* Opens an app in a sidecar. You can also relative path inside the app to open.
* @param options.pluginId Plugin ID of the app to open
* @param options.path Relative path inside the app to open
* @param options.follow If true, the sidecar will stay open even if the main location change to another app or
* Grafana section
*
* @experimental
*/
openAppV3(options) {
if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) {
return;
}
this.follow = options.follow || false;
this.updateMainLocationWhenOpened();
this.sidecarLocationService.push({ pathname: `/a/${options.pluginId}${options.path || ""}` });
reportInteraction("sidecar_service_open_app", { pluginId: options.pluginId, follow: options.follow });
}
/**
* @experimental
*/
closeApp() {
if (!this.assertFeatureEnabled()) {
return;
}
this.follow = false;
this.mainLocationWhenOpened = undefined;
this._initialContext.next(undefined);
this.sidecarLocationService.replace({ pathname: "/" });
reportInteraction("sidecar_service_close_app");
}
/**
* This is mainly useful inside an app extensions which are executed outside the main app context but can work
* differently depending on whether their app is currently rendered or not.
*
* This is also true only in case a sidecar is opened. In other cases, just to check if a single app is opened
* probably does not make sense.
*
* This means these are the states and the result of this function:
* Single app is opened: false (may seem strange from considering the function name, but the main point of
* this is to recognize when the app needs to do specific alteration in context of running next to second app)
* 2 apps are opened and pluginId is the one in the main window: true
* 2 apps are opened and pluginId is the one in the sidecar window: true
* 2 apps are opened and pluginId is not one of those: false
*
* @experimental
*/
isAppOpened(pluginId) {
if (!this.assertFeatureEnabled()) {
return false;
}
const result = !!(this.activePluginId && (this.activePluginId === pluginId || getMainAppPluginId() === pluginId));
reportInteraction("sidecar_service_is_app_opened", { pluginId, isOpened: result });
return result;
}
}
const pluginIdUrlRegex = /a\/([^\/]+)/;
function getPluginIdFromUrl(url) {
var _a;
return (_a = url.match(pluginIdUrlRegex)) == null ? undefined : _a[1];
}
function getMainAppPluginId() {
const { pathname } = exports.locationService.getLocation();
let mainApp = getPluginIdFromUrl(pathname);
if (!mainApp && pathname.match(/\/explore/)) {
mainApp = "explore";
}
if (!mainApp && pathname.match(/\/d\//)) {
mainApp = "dashboards";
}
return mainApp || "unknown";
}
function createLocationStorageHistory(options) {
const storedLocation = localStorage.getItem(options.storageKey);
const initialEntry = storedLocation ? JSON.parse(storedLocation) : "/";
const locationSubject = new rxjs.BehaviorSubject(initialEntry);
const memoryHistory = H__namespace.createMemoryHistory({ initialEntries: [initialEntry] });
let currentLocation = memoryHistory.location;
function maybeUpdateLocation() {
if (memoryHistory.location !== currentLocation) {
localStorage.setItem(
options.storageKey,
JSON.stringify(lodash.pick(memoryHistory.location, "pathname", "search", "hash"))
);
currentLocation = memoryHistory.location;
locationSubject.next(memoryHistory.location);
}
}
return {
...memoryHistory,
// Getter aren't destructured as getter but as values, so they have to be still here even though we are not
// modifying them.
get index() {
return memoryHistory.index;
},
get entries() {
return memoryHistory.entries;
},
get length() {
return memoryHistory.length;
},
get action() {
return memoryHistory.action;
},
get location() {
return memoryHistory.location;
},
push(location, state) {
memoryHistory.push(location, state);
maybeUpdateLocation();
},
replace(location, state) {
memoryHistory.replace(location, state);
maybeUpdateLocation();
},
go(n) {
memoryHistory.go(n);
maybeUpdateLocation();
},
goBack() {
memoryHistory.goBack();
maybeUpdateLocation();
},
goForward() {
memoryHistory.goForward();
maybeUpdateLocation();
},
getLocationObservable() {
return locationSubject.asObservable();
}
};
}
const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL(exports.locationService);
const SidecarContext_EXPERIMENTAL = React.createContext(
sidecarServiceSingleton_EXPERIMENTAL
);
function useSidecar_EXPERIMENTAL() {
const service = React.useContext(SidecarContext_EXPERIMENTAL);
if (!service) {
throw new Error("No SidecarContext found");
}
const initialContext = reactUse.useObservable(service.initialContextObservable, service.initialContext);
const activePluginId = reactUse.useObservable(service.activePluginIdObservable, service.activePluginId);
const locationService = service.getLocationService();
return {
activePluginId,
initialContext,
locationService,
// TODO: currently this allows anybody to open any app, in the future we should probably scope this to the
// current app but that means we will need to incorporate this better into the plugin platform APIs which
// we will do once the functionality is reasonably stable
openApp: (pluginId, context) => {
return service.openApp(pluginId, context);
},
openAppV2: (pluginId, path) => {
return service.openAppV2(pluginId, path);
},
openAppV3: (options) => {
return service.openAppV3(options);
},
closeApp: () => service.closeApp(),
isAppOpened: (pluginId) => {
return service.isAppOpened(pluginId);
}
};
}
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;
}
let singleton$5;
function setPluginExtensionGetter(instance) {
if (singleton$5 && process.env.NODE_ENV !== "test") {
throw new Error("setPluginExtensionGetter() function should only be called once, when Grafana is starting.");
}
singleton$5 = instance;
}
function getPluginExtensionGetter() {
if (!singleton$5) {
throw new Error("getPluginExtensionGetter() can only be used after the Grafana instance has started.");
}
return singleton$5;
}
const getPluginExtensions = (options) => getPluginExtensionGetter()(options);
const getPluginLinkExtensions = (options) => {
const { extensions } = getPluginExtensions(options);
return {
extensions: extensions.filter(isPluginExtensionLink)
};
};
const getPluginComponentExtensions = (options) => {
const { extensions } = getPluginExtensions(options);
const componentExtensions = extensions.filter(isPluginExtensionComponent);
return {
extensions: componentExtensions
};
};
let singleton$4;
function setPluginExtensionsHook(hook) {
if (singleton$4 && process.env.NODE_ENV !== "test") {
throw new Error("setPluginExtensionsHook() function should only be called once, when Grafana is starting.");
}
singleton$4 = hook;
}
function usePluginExtensions(options) {
if (!singleton$4) {
throw new Error("usePluginExtensions(options) can only be used after the Grafana instance has started.");
}
return singleton$4(options);
}
function usePluginLinkExtensions(options) {
const { extensions, isLoading } = usePluginExtensions(options);
return React.useMemo(() => {
return {
extensions: extensions.filter(isPluginExtensionLink),
isLoading
};
}, [extensions, isLoading]);
}
function usePluginComponentExtensions(options) {
const { extensions, isLoading } = usePluginExtensions(options);
return React.useMemo(
() => ({
extensions: extensions.filter(isPluginExtensionComponent),
isLoading
}),
[extensions, isLoading]
);
}
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);
}
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(undefined);
function useScopes() {
var _a;
const context = React.useContext(ScopesContext);
reactUse.useObservable((_a = context == null ? undefined : context.stateObservable) != null ? _a : new rxjs.Observable(), context == null ? undefined : context.state);
return context ? {
state: context.state,
stateObservable: context.stateObservable,
changeScopes: context.changeScopes,
setReadOnly: context.setReadOnly,
setEnabled: context.setEnabled
} : undefined;
}
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 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 ? undefined : _a.message) !== "Query data error") {
message = error.data.message;
} else if (((_b = error == null ? undefined : error.data) == null ? undefined : _b.message) === "Query data error" && ((_c = error == null ? undefined : error.data) == null ? undefined : _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 : undefined;
if (traceId != null) {
rsp.traceIds = [traceId];
}
const fetchResponse = res;
if ((_a = fetchResponse.data) == null ? undefined : _a.results) {
const results = fetchResponse.data.results;
const refIDs = (queries == null ? undefined : 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 ? undefined : _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 ? undefined : _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 ? undefined : _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 ? undefined : 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 ? undefined : _a.fields) != null ? _b : []],
meta: {
...(_c = frame.schema) == null ? undefined : _c.meta,
notices: [...(_f = (_e = (_d = frame.schema) == null ? undefined : _d.meta) == null ? undefined : _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 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;
}
/**
* 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 ? undefined : _b.length) {
pluginIDs.add(datasource.type);
}
if ((_c = datasource.uid) == null ? undefined : _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 ? undefined : range.from.valueOf().toString(),
to: range == null ? undefined : range.to.valueOf().toString()
};
if (config.featureToggles.queryOverLive) {
return getGrafanaLiveSrv().getQueryData({
request,
body
});
}
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.queryServiceFromUI) {
if (!(config.featureToggles.queryService || config.featureToggles.grafanaAPIServerWithExperimentalAPIs)) {
console.warn("feature toggle queryServiceFromUI also requires the queryService to be running");
} else {
if (!hasExpr && dsUIDs.size === 1) ;
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.panelId) {
headers["X-Panel-Id" /* PanelID */] = `${request.panelId}`;
}
}
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 ? undefined : _a2.length) && rsp.data.find((f) => {
var _a3;
return (_a3 = f.meta) == null ? undefined : _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 ? undefined : options.headers) ? { ...options.headers, ...headers } : headers,
params: params != null ? params : options == null ? undefined : 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 ? undefined : 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 ? undefined : _a.id) || "",
plugin_version: ((_c = (_b = this.meta) == null ? undefined : _b.info) == null ? undefined : _c.version) || "",
datasource_healthcheck_status: ((_d = err == null ? undefined : err.data) == null ? undefined : _d.status) || "error",
datasource_healthcheck_message: ((_e = err == null ? undefined : err.data) == null ? undefined : _e.messag