diff --git a/common/constants/shared.ts b/common/constants/shared.ts index c24e76fcc0..c1f1993a6e 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ import CSS from 'csstype'; -import { IField } from '../../common/types/explorer'; // Client route export const PPL_BASE = '/api/ppl'; @@ -27,6 +26,14 @@ export const observabilityID = 'observability-dashboards'; export const observabilityTitle = 'Observability'; export const observabilityPluginOrder = 6000; +export const observabilityApplicationsID = 'observability-applications'; +export const observabilityApplicationsTitle = 'Application Analytics'; +export const observabilityApplicationsPluginOrder = 5090; + +export const observabilityEventsID = 'observability-events'; +export const observabilityEventsTitle = 'Event Analytics'; +export const observabilityEventsPluginOrder = 5091; + // Shared Constants export const SQL_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/search-plugins/sql/index/'; export const PPL_DOCUMENTATION_URL = diff --git a/public/components/app.tsx b/public/components/app.tsx index d7b863eb00..d549233c0c 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -3,30 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { I18nProvider } from '@osd/i18n/react'; import React from 'react'; import { Provider } from 'react-redux'; -import { HashRouter, Route, Switch } from 'react-router-dom'; +import { HashRouter } from 'react-router-dom'; import { QueryManager } from 'common/query_manager'; import { CoreStart } from '../../../../src/core/public'; -import { observabilityID, observabilityTitle } from '../../common/constants/shared'; import store from '../framework/redux/store'; import { AppPluginStartDependencies } from '../types'; -import { Home as ApplicationAnalyticsHome } from './application_analytics/home'; -import { Home as CustomPanelsHome } from './custom_panels/home'; -import { EventAnalytics } from './event_analytics'; -import { Main as NotebooksHome } from './notebooks/components/main'; -import { Home as TraceAnalyticsHome } from './trace_analytics/home'; -import { Home as MetricsHome } from './metrics/index'; +import { AppRoutesWrapper } from './routes_wrapper'; interface ObservabilityAppDeps { - CoreStartProp: CoreStart; - DepsStart: AppPluginStartDependencies; + coreStart: CoreStart; + depsStart: AppPluginStartDependencies; pplService: any; dslService: any; savedObjects: any; timestampUtils: any; queryManager: QueryManager; + startPage?: String; } // for cypress to test redux store @@ -35,133 +29,28 @@ if (window.Cypress) { } export const App = ({ - CoreStartProp, - DepsStart, + coreStart, + depsStart, pplService, dslService, savedObjects, timestampUtils, queryManager, + startPage, }: ObservabilityAppDeps) => { - const { chrome, http, notifications } = CoreStartProp; - const parentBreadcrumb = { - text: observabilityTitle, - href: `${observabilityID}#/`, - }; - - const customPanelBreadcrumb = { - text: 'Operational panels', - href: '#/operational_panels/', - }; - return ( - - <> - - { - chrome.setBreadcrumbs([ - parentBreadcrumb, - { text: 'Metrics analytics', href: '#/metrics_analytics/' }, - ]); - return ( - - ); - }} - /> - { - return ( - - ); - }} - /> - ( - - )} - /> - { - chrome.setBreadcrumbs([parentBreadcrumb, customPanelBreadcrumb]); - return ( - - ); - }} - /> - ( - - )} - /> - { - return ( - - ); - }} - /> - - - + {' '} ); diff --git a/public/components/application_analytics/helpers/utils.tsx b/public/components/application_analytics/helpers/utils.tsx index a9df996b16..c0539df8ba 100644 --- a/public/components/application_analytics/helpers/utils.tsx +++ b/public/components/application_analytics/helpers/utils.tsx @@ -41,6 +41,8 @@ import { init as initPatterns, remove as removePatterns, } from '../../event_analytics/redux/slices/patterns_slice'; +import { from } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; // Name validation export const isNameValid = (name: string, existingNames: string[]) => { @@ -182,6 +184,10 @@ export const initializeTabData = async (dispatch: Dispatch, tabId: string, }); }; +export const fetchAppsList = (http: HttpSetup) => { + return from(http.get(`${APP_ANALYTICS_API_PREFIX}/`)).pipe(mergeMap((res) => res.data)); +}; + export const fetchPanelsVizIdList = async (http: HttpSetup, appPanelId: string) => { return await http .get(`${CUSTOM_PANELS_API_PREFIX}/panels/${appPanelId}`) diff --git a/public/components/custom_panels/helpers/utils.tsx b/public/components/custom_panels/helpers/utils.tsx index 0523391f23..a8d01f8f46 100644 --- a/public/components/custom_panels/helpers/utils.tsx +++ b/public/components/custom_panels/helpers/utils.tsx @@ -20,10 +20,14 @@ import { Visualization } from '../../visualizations/visualization'; import { getVizContainerProps } from '../../../components/visualizations/charts/helpers'; import { QueryManager } from '../../../../common/query_manager'; import { getDefaultVisConfig } from '../../event_analytics/utils'; +import { from } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { HttpSetup } from '../../src/core/target/types/public'; /* * "Utils" This file contains different reused functions in operational panels * + * fetchPanelsList - Get list of Observability Panel from Custom Panels API * isNameValid - Validates string to length > 0 and < 50 * convertDateTime - Converts input datetime string to required format * mergeLayoutAndVisualizations - Function to merge current panel layout into the visualizations list @@ -35,6 +39,16 @@ import { getDefaultVisConfig } from '../../event_analytics/utils'; * displayVisualization - Function to render the visualzation based of its type */ +export const fetchPanelsList = (http: HttpSetup) => { + return from(http.get(`${CUSTOM_PANELS_API_PREFIX}/panels`)).pipe( + map((res) => res.panels), + catchError((err) => { + console.error('Issue in fetching the operational panels', err.body.message); + return from([]); + }) + ); +}; + // Name validation 0>Name<=50 export const isNameValid = (name: string) => { return name.length >= 50 || name.length === 0 ? false : true; diff --git a/public/components/index.tsx b/public/components/index.tsx index eec21a18b2..997167f88b 100644 --- a/public/components/index.tsx +++ b/public/components/index.tsx @@ -18,17 +18,19 @@ export const Observability = ( dslService: any, savedObjects: any, timestampUtils: any, - queryManager: QueryManager + queryManager: QueryManager, + startPage?: String ) => { ReactDOM.render( , AppMountParametersProp.element ); diff --git a/public/plugin.ts b/public/plugin.ts index 1e3189e578..10617843a7 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -3,8 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; import { + AppMountParameters, + CoreSetup, + CoreStart, + DEFAULT_APP_CATEGORIES, + Plugin, PluginInitializerContext, +} from '../../../src/core/public'; +import { + observabilityApplicationsID, + observabilityApplicationsPluginOrder, + observabilityApplicationsTitle, + observabilityEventsID, + observabilityEventsPluginOrder, + observabilityEventsTitle, observabilityID, observabilityPluginOrder, observabilityTitle, @@ -17,9 +29,19 @@ import { AppPluginStartDependencies, ObservabilitySetup, ObservabilityStart } fr import { convertLegacyNotebooksUrl } from './components/notebooks/components/helpers/legacy_route_helpers'; import { convertLegacyTraceAnalyticsUrl } from './components/trace_analytics/components/common/legacy_route_helpers'; import { uiSettingsService } from '../common/utils'; +import { DashboardCreatorFn } from '../../../src/plugins/opensearch_dashboards_react/public/context/types'; +import { fetchPanelsList } from './components/custom_panels/helpers/utils'; +import { fetchAppsList } from './components/application_analytics/helpers/utils'; import { QueryManager } from '../common/query_manager'; +import { DashboardSetup } from '../../../src/plugins/dashboard/public'; +import { DashboardListItem } from '../../../src/plugins/dashboard/common/types'; +import { concat, from } from 'rxjs'; +import { catchError, map, mergeMap } from 'rxjs/operators'; + export class ObservabilityPlugin implements Plugin { - public setup(core: CoreSetup): ObservabilitySetup { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, { dashboard }: { dashboard: DashboardSetup }): ObservabilitySetup { uiSettingsService.init(core.uiSettings, core.notifications); // redirect legacy notebooks URL to current URL under observability @@ -32,34 +54,114 @@ export class ObservabilityPlugin implements Plugin { + return from(fetchAppsList(core.http)).pipe( + map(convertAppAnalyticToDashboardListItem), + catchError((err) => { + console.error('Issue in fetching the app analytics list', err); + return from([]); + }) + ); + }; + + // Fetches all saved Custom Panels + const fetchObservabilityPanels = () => { + return from(fetchPanelsList(core.http)).pipe( + mergeMap((item) => item), + map(convertPanelToDashboardListItem), + catchError((err) => { + console.error('Issue in fetching the operational panels', err); + return from([]); + }) + ); + }; + + const convertPanelToDashboardListItem = (item: any): DashboardListItem => { + return { + id: item.id, + title: item.name, + type: 'Observability Panel', + description: '...', + url: `observability-dashboards#/operational_panels/${item.id}`, + listType: 'observabiliity-panel', + }; + }; + + const convertAppAnalyticToDashboardListItem = (item: any): DashboardListItem => { + return { + id: item.id, + title: item.name, + type: 'Observability Application', + description: item.description, + url: `observability-dashboards#/application_analytics/${item.id}`, + listType: 'observability-application', + }; + }; + + const id: string = this.initializerContext.opaqueId.description!; + + const dashboardAppAnalytics = fetchApplicationAnalytics(); + const dashboardObservabilityPanels = fetchObservabilityPanels(); + const combinedDashboardList = concat(dashboardAppAnalytics, dashboardObservabilityPanels); + + const createAppAnalytics: DashboardCreatorFn = () => { + window.location = core.http.basePath.prepend( + '/app/observability-dashboards#/application_analytics/create' + ); + }; + + dashboard.registerDashboardListSource(id, () => combinedDashboardList); + dashboard.registerDashboardItemCreator({ + id: 'observaility-application', + defaultText: 'Analytics Application', + creatorFn: createAppAnalytics, + }); + + const appMountWithStartPage = (startPage?: string) => async (params: AppMountParameters) => { + const { Observability } = await import('./components/index'); + const [coreStart, depsStart] = await core.getStartServices(); + const pplService = new PPLService(coreStart.http); + const dslService = new DSLService(coreStart.http); + const savedObjects = new SavedObjects(coreStart.http); + const timestampUtils = new TimestampUtils(dslService); + const qm = new QueryManager(); + + return Observability( + coreStart, + depsStart as AppPluginStartDependencies, + params, + pplService, + dslService, + savedObjects, + timestampUtils, + qm, + startPage + ); + }; + + core.application.register({ + id: observabilityApplicationsID, + title: observabilityApplicationsTitle, + category: DEFAULT_APP_CATEGORIES.observability, + order: observabilityApplicationsPluginOrder, + mount: appMountWithStartPage('/application_analytics'), + }); + + core.application.register({ + id: observabilityEventsID, + title: observabilityEventsTitle, + category: DEFAULT_APP_CATEGORIES.observability, + order: observabilityEventsPluginOrder, + mount: appMountWithStartPage('/event_analytics'), + }); + core.application.register({ id: observabilityID, title: observabilityTitle, - category: { - id: 'opensearch', - label: 'OpenSearch Plugins', - order: 2000, - }, + category: DEFAULT_APP_CATEGORIES.plugins, order: observabilityPluginOrder, - async mount(params: AppMountParameters) { - const { Observability } = await import('./components/index'); - const [coreStart, depsStart] = await core.getStartServices(); - const pplService = new PPLService(coreStart.http); - const dslService = new DSLService(coreStart.http); - const savedObjects = new SavedObjects(coreStart.http); - const timestampUtils = new TimestampUtils(dslService, pplService); - const qm = new QueryManager(); - return Observability( - coreStart, - depsStart as AppPluginStartDependencies, - params, - pplService, - dslService, - savedObjects, - timestampUtils, - qm - ); - }, + mount: appMountWithStartPage(), }); // Return methods that should be available to other plugins