@grafana/runtime
Version:
Grafana Runtime Library
1 lines • 18.7 kB
Source Map (JSON)
{"version":3,"file":"SidecarService_EXPERIMENTAL.mjs","sources":["../../../src/services/SidecarService_EXPERIMENTAL.ts"],"sourcesContent":["import * as H from 'history';\nimport { pick } from 'lodash';\nimport { BehaviorSubject, map, Observable } from 'rxjs';\n\nimport { reportInteraction } from '../analytics/utils';\nimport { config } from '../config';\n\nimport { HistoryWrapper, locationService as mainLocationService, LocationService } from './LocationService';\n\n// Only allow sidecar to be opened on these routes. It does not seem to make sense to keep the sidecar opened on\n// config/admin pages for example.\n// At this moment let's be restrictive about where the sidecar can show and add more routes if there is a need.\nconst ALLOW_ROUTES = [\n /(^\\/d\\/)/, // dashboards\n /^\\/explore/, // explore + explore metrics\n /^\\/a\\/[^\\/]+/, // app plugins\n /^\\/alerting/,\n];\n\n/**\n * This is a service that handles state and operation of a sidecar feature (sideview to render a second app in grafana).\n * At this moment this is highly experimental and if used should be understood to break easily with newer versions.\n * None of this functionality works without a feature toggle `appSidecar` being enabled.\n *\n * Right now this being in a single service is more of a practical tradeoff for easier isolation in the future these\n * APIs may be integrated into other services or features like app extensions, plugin system etc.\n *\n * @experimental\n */\nexport class SidecarService_EXPERIMENTAL {\n private _initialContext: BehaviorSubject<unknown | undefined>;\n\n private sidecarLocationService: LocationService;\n private mainLocationService: LocationService;\n\n // If true we don't close the sidecar when user navigates to another app or part of Grafana from where the sidecar\n // was opened.\n private follow = false;\n\n // Keep track of where the sidecar was originally opened for autoclose behaviour.\n private mainLocationWhenOpened: string | undefined;\n\n private mainOnAllowedRoute = false;\n\n constructor(mainLocationService: LocationService) {\n this._initialContext = new BehaviorSubject<unknown | undefined>(undefined);\n this.mainLocationService = mainLocationService;\n this.sidecarLocationService = new HistoryWrapper(\n createLocationStorageHistory({ storageKey: 'grafana.sidecar.history' })\n );\n this.handleMainLocationChanges();\n }\n\n private assertFeatureEnabled() {\n if (!config.featureToggles.appSidecar) {\n console.warn('The `appSidecar` feature toggle is not enabled, doing nothing.');\n return false;\n }\n\n return true;\n }\n\n private updateMainLocationWhenOpened() {\n const pathname = this.mainLocationService.getLocation().pathname;\n for (const route of ALLOW_ROUTES) {\n const match = pathname.match(route)?.[0];\n if (match) {\n this.mainLocationWhenOpened = match;\n return;\n }\n }\n }\n\n /**\n * Every time the main location changes we check if we should keep the sidecar open or close it based on list\n * of allowed routes and also based on the follow flag when opening the app.\n */\n private handleMainLocationChanges() {\n this.mainOnAllowedRoute = ALLOW_ROUTES.some((prefix) =>\n this.mainLocationService.getLocation().pathname.match(prefix)\n );\n\n this.mainLocationService.getLocationObservable().subscribe((location) => {\n this.mainOnAllowedRoute = ALLOW_ROUTES.some((prefix) => location.pathname.match(prefix));\n\n if (!this.activePluginId) {\n return;\n }\n\n if (!this.mainOnAllowedRoute) {\n this.closeApp();\n return;\n }\n\n // We check if we moved to some other app or part of grafana from where we opened the sidecar.\n const isTheSameLocation = Boolean(\n this.mainLocationWhenOpened && location.pathname.startsWith(this.mainLocationWhenOpened)\n );\n\n if (!(isTheSameLocation || this.follow)) {\n this.closeApp();\n }\n });\n }\n\n /**\n * Get current app id of the app in sidecar. This is most probably provisional. In the future\n * this should be driven by URL addressing so that routing for the apps don't change. Useful just internally\n * to decide which app to render.\n *\n * @experimental\n */\n get activePluginIdObservable() {\n return this.sidecarLocationService.getLocationObservable().pipe(\n map((val) => {\n return getPluginIdFromUrl(val?.pathname || '');\n })\n );\n }\n\n /**\n * Get initial context which is whatever data was passed when calling the 'openApp' function. This is meant as\n * a way for the app to initialize it's state based on some context that is passed to it from the primary app.\n *\n * @experimental\n */\n get initialContextObservable() {\n return this._initialContext.asObservable();\n }\n\n // Get the current value of the subject, this is needed if we want the value immediately. For example if used in\n // hook in react with useObservable first render would return undefined even if the behaviourSubject has some\n // value which will be emitted in the next tick and thus next rerender.\n get initialContext() {\n return this._initialContext.getValue();\n }\n\n /**\n * @experimental\n */\n get activePluginId() {\n return getPluginIdFromUrl(this.sidecarLocationService.getLocation().pathname);\n }\n\n getLocationService() {\n return this.sidecarLocationService;\n }\n\n /**\n * Opens an app in a sidecar. You can also pass some context object that will be then available to the app.\n * @deprecated\n * @experimental\n */\n openApp(pluginId: string, context?: unknown) {\n if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) {\n return;\n }\n this._initialContext.next(context);\n this.openAppV3({ pluginId, follow: false });\n }\n\n /**\n * Opens an app in a sidecar. You can also relative path inside the app to open.\n * @deprecated\n * @experimental\n */\n openAppV2(pluginId: string, path?: string) {\n this.openAppV3({ pluginId, path, follow: false });\n }\n\n /**\n * Opens an app in a sidecar. You can also relative path inside the app to open.\n * @param options.pluginId Plugin ID of the app to open\n * @param options.path Relative path inside the app to open\n * @param options.follow If true, the sidecar will stay open even if the main location change to another app or\n * Grafana section\n *\n * @experimental\n */\n openAppV3(options: { pluginId: string; path?: string; follow?: boolean }) {\n if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) {\n return;\n }\n\n this.follow = options.follow || false;\n\n this.updateMainLocationWhenOpened();\n this.sidecarLocationService.push({ pathname: `/a/${options.pluginId}${options.path || ''}` });\n reportInteraction('sidecar_service_open_app', { pluginId: options.pluginId, follow: options.follow });\n }\n\n /**\n * @experimental\n */\n closeApp() {\n if (!this.assertFeatureEnabled()) {\n return;\n }\n\n this.follow = false;\n this.mainLocationWhenOpened = undefined;\n this._initialContext.next(undefined);\n this.sidecarLocationService.replace({ pathname: '/' });\n\n reportInteraction('sidecar_service_close_app');\n }\n\n /**\n * This is mainly useful inside an app extensions which are executed outside the main app context but can work\n * differently depending on whether their app is currently rendered or not.\n *\n * This is also true only in case a sidecar is opened. In other cases, just to check if a single app is opened\n * probably does not make sense.\n *\n * This means these are the states and the result of this function:\n * Single app is opened: false (may seem strange from considering the function name, but the main point of\n * this is to recognize when the app needs to do specific alteration in context of running next to second app)\n * 2 apps are opened and pluginId is the one in the main window: true\n * 2 apps are opened and pluginId is the one in the sidecar window: true\n * 2 apps are opened and pluginId is not one of those: false\n *\n * @experimental\n */\n isAppOpened(pluginId: string) {\n if (!this.assertFeatureEnabled()) {\n return false;\n }\n\n const result = !!(this.activePluginId && (this.activePluginId === pluginId || getMainAppPluginId() === pluginId));\n reportInteraction('sidecar_service_is_app_opened', { pluginId, isOpened: result });\n return result;\n }\n}\n\nconst pluginIdUrlRegex = /a\\/([^\\/]+)/;\nfunction getPluginIdFromUrl(url: string) {\n return url.match(pluginIdUrlRegex)?.[1];\n}\n\n// The app plugin that is \"open\" in the main Grafana view\nfunction getMainAppPluginId() {\n // TODO: not great but we have to get a handle on the other locationService used for the main view and easiest way\n // right now is through this global singleton\n const { pathname } = mainLocationService.getLocation();\n\n // A naive way to sort of simulate core features being an app and having an appID\n let mainApp = getPluginIdFromUrl(pathname);\n if (!mainApp && pathname.match(/\\/explore/)) {\n mainApp = 'explore';\n }\n\n if (!mainApp && pathname.match(/\\/d\\//)) {\n mainApp = 'dashboards';\n }\n\n return mainApp || 'unknown';\n}\n\ntype LocalStorageHistoryOptions = {\n storageKey: string;\n};\n\ninterface LocationStorageHistory extends H.MemoryHistory {\n getLocationObservable(): Observable<H.Location | undefined>;\n}\n\n/**\n * Simple wrapper over the memory history that persists the location in the localStorage.\n *\n * @param options\n */\nfunction createLocationStorageHistory(options: LocalStorageHistoryOptions): LocationStorageHistory {\n const storedLocation = localStorage.getItem(options.storageKey);\n const initialEntry = storedLocation ? JSON.parse(storedLocation) : '/';\n const locationSubject = new BehaviorSubject<H.Location | undefined>(initialEntry);\n const memoryHistory = H.createMemoryHistory({ initialEntries: [initialEntry] });\n\n let currentLocation = memoryHistory.location;\n\n function maybeUpdateLocation() {\n if (memoryHistory.location !== currentLocation) {\n localStorage.setItem(\n options.storageKey,\n JSON.stringify(pick(memoryHistory.location, 'pathname', 'search', 'hash'))\n );\n currentLocation = memoryHistory.location;\n locationSubject.next(memoryHistory.location);\n }\n }\n\n // This creates a sort of proxy over the memory location just to add the localStorage persistence and the location\n // observer. We could achieve the same effect by a listener but that would create a memory leak as there would be no\n // reasonable way to unsubcribe the listener later on.\n // Another issue is that react router for some reason does not care about proper `this` binding and just calls these\n // as normal functions. So if this were to be a class we would still need to bind each of these methods to the\n // instance so at that moment this just seems easier.\n return {\n ...memoryHistory,\n // Getter aren't destructured as getter but as values, so they have to be still here even though we are not\n // modifying them.\n get index() {\n return memoryHistory.index;\n },\n get entries() {\n return memoryHistory.entries;\n },\n get length() {\n return memoryHistory.length;\n },\n get action() {\n return memoryHistory.action;\n },\n get location() {\n return memoryHistory.location;\n },\n push(location: H.Path | H.LocationDescriptor<H.LocationState>, state?: H.LocationState) {\n memoryHistory.push(location, state);\n maybeUpdateLocation();\n },\n replace(location: H.Path | H.LocationDescriptor<H.LocationState>, state?: H.LocationState) {\n memoryHistory.replace(location, state);\n maybeUpdateLocation();\n },\n go(n: number) {\n memoryHistory.go(n);\n maybeUpdateLocation();\n },\n goBack() {\n memoryHistory.goBack();\n maybeUpdateLocation();\n },\n goForward() {\n memoryHistory.goForward();\n maybeUpdateLocation();\n },\n getLocationObservable() {\n return locationSubject.asObservable();\n },\n };\n}\n\nexport const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL(mainLocationService);\n"],"names":["mainLocationService"],"mappings":"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"}