UNPKG

@grafana/runtime

Version:
1,548 lines (1,511 loc) • 71.6 kB
'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