diff --git a/dashboards-observability/public/components/application_analytics/components/application.tsx b/dashboards-observability/public/components/application_analytics/components/application.tsx index 3c0af6d13..0b32d7499 100644 --- a/dashboards-observability/public/components/application_analytics/components/application.tsx +++ b/dashboards-observability/public/components/application_analytics/components/application.tsx @@ -24,9 +24,11 @@ import DSLService from 'public/services/requests/dsl'; import PPLService from 'public/services/requests/ppl'; import SavedObjects from 'public/services/saved_objects/event_analytics/saved_objects'; import TimestampUtils from 'public/services/timestamp/timestamp'; -import React, { ReactChild, useState } from 'react'; +import React, { ReactChild, useEffect, useState } from 'react'; import { isEmpty, uniqueId } from 'lodash'; import { + ApplicationType, + APP_ANALYTICS_API_PREFIX, TAB_CONFIG_ID_TXT_PFX, TAB_CONFIG_TITLE, TAB_LOG_ID_TXT_PFX, @@ -70,14 +72,45 @@ interface AppDetailProps extends AppAnalyticsComponentDeps { notifications: NotificationsStart; } - export function Application(props: AppDetailProps) { - const { pplService, dslService, timestampUtils, savedObjects, http, notifications } = props; + const { pplService, dslService, timestampUtils, savedObjects, http, notifications, appId, chrome, parentBreadcrumb } = props; + const [application, setApplication] = useState(); const [selectedTabId, setSelectedTab] = useState(TAB_OVERVIEW_ID); const handleContentTabClick = (selectedTab: IQueryTab) => setSelectedTab(selectedTab.id); const history = useHistory(); const [toasts, setToasts] = useState>([]); + // Fetch application by id + const fetchAppById = async (appId: string) => { + return http + .get(`${APP_ANALYTICS_API_PREFIX}/${appId}`) + .then((res) => { + setApplication(res.application); + }) + .catch((err) => { + setToast('Error occurred while fetching application', 'danger'); + console.error(err); + }) + } + + useEffect(() => { + fetchAppById(appId); + }, [appId]); + + useEffect(() => { + chrome.setBreadcrumbs([ + parentBreadcrumb, + { + text: 'Application analytics', + href: '#/application_analytics', + }, + { + text: application?.name || '', + href: `${parentBreadcrumb.href}${appId}`, + }, + ]); + }, [appId, application?.name]); + const setToast = (title: string, color = 'success', text?: ReactChild, side?: string) => { if (!text) text = ''; setToasts([...toasts, { id: new Date().toISOString(), title, text, color } as Toast]); @@ -98,20 +131,20 @@ export function Application(props: AppDetailProps) { const getOverview = () => { return ( - + ); }; const getService = () => { return ( - + ); }; const getTrace = () => { return ( <> - + -

my-app1

+

{application?.name || ''}

diff --git a/dashboards-observability/public/components/application_analytics/components/create.tsx b/dashboards-observability/public/components/application_analytics/components/create.tsx index 17f21e7cd..8749846b1 100644 --- a/dashboards-observability/public/components/application_analytics/components/create.tsx +++ b/dashboards-observability/public/components/application_analytics/components/create.tsx @@ -97,7 +97,7 @@ export const CreateApp = (props: CreateAppProps) => { }; const onCreate = () => { - createApp(state.name, state.description, query, selectedServices, selectedTraces) + createApp(state.name, state.description, query, selectedServices, selectedTraces); } const onCancel = () => { diff --git a/dashboards-observability/public/components/application_analytics/home.tsx b/dashboards-observability/public/components/application_analytics/home.tsx index 5f847406f..35be5732a 100644 --- a/dashboards-observability/public/components/application_analytics/home.tsx +++ b/dashboards-observability/public/components/application_analytics/home.tsx @@ -38,7 +38,7 @@ export interface AppAnalyticsComponentDeps extends TraceAnalyticsComponentDeps { export const Home = (props: HomeProps) => { const { pplService, dslService, timestampUtils, savedObjects, parentBreadcrumb, http, chrome, notifications } = props; - const [applicationData, setApplicationData] = useState>([]); + const [applicationList, setApplicationList] = useState>([]); const [toasts, setToasts] = useState>([]); const [indicesExist, setIndicesExist] = useState(true); const storedFilters = sessionStorage.getItem('AppAnalyticsFilters'); @@ -94,10 +94,10 @@ export const Home = (props: HomeProps) => { return http .get(`${APP_ANALYTICS_API_PREFIX}/`) .then((res) => { - setApplicationData(res.data); + setApplicationList(res.data); }) .catch((err) => { - setToast('Error occured while fetching applications', 'danger'); + setToast('Error occurred while fetching applications', 'danger'); console.error(err); }) } @@ -124,7 +124,9 @@ export const Home = (props: HomeProps) => { }) .then((res) => { setToast(`Application "${name}" successfully created!`); - window.location.assign(`${parentBreadcrumb.href}${res.newAppId}`) + setFiltersWithStorage([]); + setQueryWithStorage(''); + window.location.assign(`${parentBreadcrumb.href}application_analytics/${res.newAppId}`) }) .catch((err) => { setToast(`Error occurred while creating new application "${name}"`, 'danger'); @@ -149,8 +151,8 @@ export const Home = (props: HomeProps) => { body: JSON.stringify(requestBody) }) .then((res) => { - setApplicationData((prevApplicationData) => { - const newApplicationData = [...prevApplicationData]; + setApplicationList((prevApplicationList) => { + const newApplicationData = [...prevApplicationList]; const renamedApplication = newApplicationData.find( (application) => application.id === appId ); @@ -170,8 +172,8 @@ export const Home = (props: HomeProps) => { return http .delete(`${APP_ANALYTICS_API_PREFIX}/${appList.join(',')}`) .then((res) => { - setApplicationData((prevApplicationData) => { - return prevApplicationData.filter((app) => !appList.includes(app.id)) + setApplicationList((prevApplicationList) => { + return prevApplicationList.filter((app) => !appList.includes(app.id)) }); const message = toastMessage || `Application${appList.length > 1 ? 's' : ''} successfully deleted!`; @@ -201,7 +203,7 @@ export const Home = (props: HomeProps) => { { + fetchApps = async (client: ILegacyScopedClusterClient) => { try { const response = await client.callAsCurrentUser('observability.getObject', { objectType: 'application', @@ -25,6 +25,18 @@ export class AppAnalyticsAdaptor { } } + // Fetch application by id + fetchAppById = async (client: ILegacyScopedClusterClient, appId: string) => { + try { + const response = await client.callAsCurrentUser('observability.getObjectById', { + objectId: appId, + }); + return response.observabilityObjectList[0]; + } catch (err: any) { + throw new Error('Fetch Application By Id Error: ' + err); + } + } + // Create a new application createNewApp = async ( client: ILegacyScopedClusterClient, diff --git a/dashboards-observability/server/routes/application_analytics/app_analytics_router.ts b/dashboards-observability/server/routes/application_analytics/app_analytics_router.ts index edcf8b3c0..e22f7eb92 100644 --- a/dashboards-observability/server/routes/application_analytics/app_analytics_router.ts +++ b/dashboards-observability/server/routes/application_analytics/app_analytics_router.ts @@ -48,6 +48,42 @@ export function registerAppAnalyticsRouter(router: IRouter) { } } ) + + // Fetch application by id + router.get( + { + path: `${API_PREFIX}/{appId}`, + validate: { + params: schema.object({ + appId: schema.string(), + }), + }, + }, + async( + context, + request, + response + ): Promise> => { + const opensearchClient: ILegacyScopedClusterClient = context.observability_plugin.observabilityClient.asScoped( + request + ); + try { + const appObject = await appAnalyticsBackend.fetchAppById( + opensearchClient, + request.params.appId + ); + return response.ok({ + body: appObject + }); + } catch (err: any) { + console.error('Error occurred while fetching application', err); + return response.custom({ + statusCode: err.statusCode || 500, + body: err.message, + }); + } + } + ) // Create a new application router.post(