Skip to content

Commit

Permalink
[Synthetics] Status overview embeddable (#188807)
Browse files Browse the repository at this point in the history
## Summary

Added status overview embeddable !!


https://github.com/user-attachments/assets/27499ecf-549f-43a6-a16b-22a44db36814

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
shahzad31 and kibanamachine committed Jul 22, 2024
1 parent bc42310 commit eb71438
Show file tree
Hide file tree
Showing 45 changed files with 739 additions and 338 deletions.
7 changes: 5 additions & 2 deletions x-pack/plugins/observability_solution/synthetics/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"embeddable",
"discover",
"dataViews",
"dashboard",
"encryptedSavedObjects",
"exploratoryView",
"features",
Expand All @@ -31,7 +32,9 @@
"triggersActionsUi",
"usageCollection",
"bfetch",
"unifiedSearch"
"uiActions",
"unifiedSearch",
"presentationUtil"
],
"optionalPlugins": [
"cloud",
Expand All @@ -53,7 +56,7 @@
"observability",
"spaces",
"indexLifecycleManagement",
"unifiedDocViewer"
"unifiedDocViewer",
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const SYNTHETICS_OVERVIEW_EMBEDDABLE = 'SYNTHETICS_OVERVIEW_EMBEDDABLE';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { CoreSetup } from '@kbn/core-lifecycle-browser';

import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-browser/src';
import { createStatusOverviewPanelAction } from './ui_actions/create_overview_panel_action';
import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin';
import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from './constants';

export const registerSyntheticsEmbeddables = (
core: CoreSetup<ClientPluginsStart, unknown>,
pluginsSetup: ClientPluginsSetup
) => {
pluginsSetup.embeddable.registerReactEmbeddableFactory(
SYNTHETICS_OVERVIEW_EMBEDDABLE,
async () => {
const { getStatusOverviewEmbeddableFactory } = await import(
'./status_overview/status_overview_embeddable_factory'
);
return getStatusOverviewEmbeddableFactory(core.getStartServices);
}
);

const { uiActions, cloud, serverless } = pluginsSetup;

// Initialize actions
const addOverviewPanelAction = createStatusOverviewPanelAction();

core.getStartServices().then(([_, pluginsStart]) => {
pluginsStart.dashboard.registerDashboardPanelPlacementSetting(
SYNTHETICS_OVERVIEW_EMBEDDABLE,
() => {
return { width: 10, height: 8 };
}
);
});

// Assign triggers
// Only register these actions in stateful kibana, and the serverless observability project
if (Boolean((serverless && cloud?.serverless.projectType === 'observability') || !serverless)) {
uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addOverviewPanelAction);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { Subject } from 'rxjs';
import { OverviewStatus } from '../../synthetics/components/monitors_page/overview/overview/overview_status';
import { SyntheticsEmbeddableContext } from '../synthetics_embeddable_context';

export const StatusOverviewComponent = ({ reload$ }: { reload$: Subject<boolean> }) => {
return (
<SyntheticsEmbeddableContext>
<OverviewStatus reload$={reload$} />
</SyntheticsEmbeddableContext>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';

import React, { useEffect } from 'react';
import { DefaultEmbeddableApi, ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import {
initializeTitles,
useBatchedPublishingSubjects,
fetch$,
PublishesWritablePanelTitle,
PublishesPanelTitle,
SerializedTitles,
} from '@kbn/presentation-publishing';
import { BehaviorSubject, Subject } from 'rxjs';
import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser';
import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from '../constants';
import { ClientPluginsStart } from '../../../plugin';
import { StatusOverviewComponent } from './status_overview_component';

export const getOverviewPanelTitle = () =>
i18n.translate('xpack.synthetics.statusOverview.displayName', {
defaultMessage: 'Synthetics Status Overview',
});

export type OverviewEmbeddableState = SerializedTitles;

export type StatusOverviewApi = DefaultEmbeddableApi<OverviewEmbeddableState> &
PublishesWritablePanelTitle &
PublishesPanelTitle;

export const getStatusOverviewEmbeddableFactory = (
getStartServices: StartServicesAccessor<ClientPluginsStart>
) => {
const factory: ReactEmbeddableFactory<
OverviewEmbeddableState,
OverviewEmbeddableState,
StatusOverviewApi
> = {
type: SYNTHETICS_OVERVIEW_EMBEDDABLE,
deserializeState: (state) => {
return state.rawState as OverviewEmbeddableState;
},
buildEmbeddable: async (state, buildApi, uuid, parentApi) => {
const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state);
const defaultTitle$ = new BehaviorSubject<string | undefined>(getOverviewPanelTitle());
const reload$ = new Subject<boolean>();

const api = buildApi(
{
...titlesApi,
defaultPanelTitle: defaultTitle$,
serializeState: () => {
return {
rawState: {
...serializeTitles(),
},
};
},
},
{
...titleComparators,
}
);

const fetchSubscription = fetch$(api)
.pipe()
.subscribe((next) => {
reload$.next(next.isReload);
});

return {
api,
Component: () => {
const [] = useBatchedPublishingSubjects();

useEffect(() => {
return () => {
fetchSubscription.unsubscribe();
};
}, []);
return (
<div
data-shared-item="" // TODO: Remove data-shared-item and data-rendering-count as part of https://github.com/elastic/kibana/issues/179376
>
<StatusOverviewComponent reload$={reload$} />
</div>
);
},
};
},
};
return factory;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { createBrowserHistory } from 'history';
import { EuiPanel } from '@elastic/eui';
import { Router } from '@kbn/shared-ux-router';
import { SyntheticsSharedContext } from '../synthetics/contexts/synthetics_shared_context';
import { SyntheticsEmbeddableStateContextProvider } from '../synthetics/contexts/synthetics_embeddable_context';
import { getSyntheticsAppProps } from '../synthetics/render_app';
import { SyntheticsSettingsContextProvider } from '../synthetics/contexts';

export const SyntheticsEmbeddableContext: React.FC<{ search?: string }> = ({
search,
children,
}) => {
const props = getSyntheticsAppProps();

return (
<SyntheticsSharedContext {...props}>
<SyntheticsEmbeddableStateContextProvider>
<Router history={createBrowserHistory()}>
<SyntheticsSettingsContextProvider {...props}>
<EuiPanel hasShadow={false}>{children}</EuiPanel>
</SyntheticsSettingsContextProvider>
</Router>
</SyntheticsEmbeddableStateContextProvider>
</SyntheticsSharedContext>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { apiIsPresentationContainer } from '@kbn/presentation-containers';
import {
IncompatibleActionError,
type UiActionsActionDefinition,
} from '@kbn/ui-actions-plugin/public';
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from '../constants';

export const COMMON_SYNTHETICS_GROUPING = [
{
id: 'synthetics',
getDisplayName: () =>
i18n.translate('xpack.synthetics.common.constants.grouping.legacy', {
defaultMessage: 'Synthetics',
}),
getIconType: () => {
return 'online';
},
},
];
export const ADD_SYNTHETICS_OVERVIEW_ACTION_ID = 'CREATE_SYNTHETICS_OVERVIEW_EMBEDDABLE';

export function createStatusOverviewPanelAction(): UiActionsActionDefinition<EmbeddableApiContext> {
return {
id: ADD_SYNTHETICS_OVERVIEW_ACTION_ID,
grouping: COMMON_SYNTHETICS_GROUPING,
order: 30,
getIconType: () => 'online',
isCompatible: async ({ embeddable }) => {
return apiIsPresentationContainer(embeddable);
},
execute: async ({ embeddable }) => {
if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError();
try {
embeddable.addNewPanel({
panelType: SYNTHETICS_OVERVIEW_EMBEDDABLE,
});
} catch (e) {
return Promise.reject();
}
},
getDisplayName: () =>
i18n.translate('xpack.synthetics.syntheticsEmbeddable.ariaLabel', {
defaultMessage: 'Synthetics Overview',
}),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import { useFetcher } from '@kbn/observability-shared-plugin/public';
import { useSessionStorage } from 'react-use';
import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { ClientPluginsStart } from '../../../../../plugin';
import { selectDynamicSettings } from '../../../state/settings';
import {
selectSyntheticsAlerts,
selectSyntheticsAlertsLoaded,
} from '../../../state/alert_rules/selectors';
import { selectMonitorListState } from '../../../state';
import { getDynamicSettingsAction } from '../../../state/settings/actions';
import { useSyntheticsSettingsContext, useSyntheticsStartPlugins } from '../../../contexts';
import { useSyntheticsSettingsContext } from '../../../contexts';
import { ConfigKey } from '../../../../../../common/runtime_types';

export const AlertingCallout = ({ isAlertingEnabled }: { isAlertingEnabled?: boolean }) => {
Expand All @@ -40,7 +42,8 @@ export const AlertingCallout = ({ isAlertingEnabled }: { isAlertingEnabled?: boo
loaded: monitorsLoaded,
} = useSelector(selectMonitorListState);

const syntheticsLocators = useSyntheticsStartPlugins()?.share?.url.locators;
const syntheticsLocators = useKibana<ClientPluginsStart>().services.share?.url.locators;

const locator = syntheticsLocators?.get(syntheticsSettingsLocatorID);

const { data: url } = useFetcher(() => {
Expand Down
Loading

0 comments on commit eb71438

Please sign in to comment.