mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
parent
870379fccb
commit
9d7917ce9e
48 changed files with 768 additions and 1115 deletions
|
@ -4,4 +4,51 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import './apps/kibana_app';
|
||||
// NP_TODO: This app.ts layer is needed until we migrate 100% to the NP.
|
||||
// This is so other plugins can import from our public/index file without trying to
|
||||
// actually mount and run our application. Once in the NP this won't be an issue
|
||||
// as the NP will look for an export named "plugin" and run that from the index file.
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { PluginInitializerContext } from 'kibana/public';
|
||||
import chrome from 'ui/chrome';
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
import uiRoutes from 'ui/routes';
|
||||
// @ts-ignore
|
||||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
import { plugin } from './new_platform_index';
|
||||
|
||||
const ROOT_ELEMENT_ID = 'react-infra-root';
|
||||
export { ROOT_ELEMENT_ID };
|
||||
|
||||
const { core, plugins } = npStart;
|
||||
const __LEGACY = {
|
||||
uiModules,
|
||||
uiRoutes,
|
||||
timezoneProvider,
|
||||
};
|
||||
// This will be moved to core.application.register when the new platform
|
||||
// migration is complete.
|
||||
// @ts-ignore
|
||||
chrome.setRootTemplate(`
|
||||
<main
|
||||
id="${ROOT_ELEMENT_ID}"
|
||||
class="infReactRoot"
|
||||
></main>
|
||||
`);
|
||||
|
||||
const checkForRoot = () => {
|
||||
return new Promise(resolve => {
|
||||
const ready = !!document.getElementById(ROOT_ELEMENT_ID);
|
||||
if (ready) {
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(() => resolve(checkForRoot()), 10);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
checkForRoot().then(() => {
|
||||
plugin({} as PluginInitializerContext).start(core, plugins, __LEGACY);
|
||||
});
|
||||
|
|
|
@ -6,16 +6,15 @@
|
|||
|
||||
import { createHashHistory } from 'history';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
|
||||
// TODO use theme provided from parentApp when kibana supports it
|
||||
import { EuiErrorBoundary } from '@elastic/eui';
|
||||
import { UICapabilitiesProvider } from 'ui/capabilities/react';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { EuiThemeProvider } from '../../../../common/eui_styled_components';
|
||||
import { InfraFrontendLibs } from '../lib/lib';
|
||||
import { PageRouter } from '../routes';
|
||||
|
@ -27,12 +26,10 @@ import {
|
|||
useUiSetting$,
|
||||
KibanaContextProvider,
|
||||
} from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
const { uiSettings } = npStart.core;
|
||||
|
||||
export async function startApp(libs: InfraFrontendLibs) {
|
||||
import { ROOT_ELEMENT_ID } from '../app';
|
||||
// NP_TODO: Type plugins
|
||||
export async function startApp(libs: InfraFrontendLibs, core: CoreStart, plugins: any) {
|
||||
const history = createHashHistory();
|
||||
|
||||
const libs$ = new BehaviorSubject(libs);
|
||||
const store = createStore({
|
||||
apolloClient: libs$.pipe(pluck('apolloClient')),
|
||||
|
@ -43,31 +40,35 @@ export async function startApp(libs: InfraFrontendLibs) {
|
|||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
return (
|
||||
<I18nContext>
|
||||
<UICapabilitiesProvider>
|
||||
<EuiErrorBoundary>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ReduxStateContextProvider>
|
||||
<ApolloProvider client={libs.apolloClient}>
|
||||
<ApolloClientContext.Provider value={libs.apolloClient}>
|
||||
<EuiThemeProvider darkMode={darkMode}>
|
||||
<HistoryContext.Provider value={history}>
|
||||
<PageRouter history={history} />
|
||||
</HistoryContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</ApolloClientContext.Provider>
|
||||
</ApolloProvider>
|
||||
</ReduxStateContextProvider>
|
||||
</ReduxStoreProvider>
|
||||
</EuiErrorBoundary>
|
||||
</UICapabilitiesProvider>
|
||||
</I18nContext>
|
||||
<core.i18n.Context>
|
||||
<EuiErrorBoundary>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ReduxStateContextProvider>
|
||||
<ApolloProvider client={libs.apolloClient}>
|
||||
<ApolloClientContext.Provider value={libs.apolloClient}>
|
||||
<EuiThemeProvider darkMode={darkMode}>
|
||||
<HistoryContext.Provider value={history}>
|
||||
<PageRouter history={history} />
|
||||
</HistoryContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</ApolloClientContext.Provider>
|
||||
</ApolloProvider>
|
||||
</ReduxStateContextProvider>
|
||||
</ReduxStoreProvider>
|
||||
</EuiErrorBoundary>
|
||||
</core.i18n.Context>
|
||||
);
|
||||
};
|
||||
|
||||
libs.framework.render(
|
||||
<KibanaContextProvider services={{ uiSettings }}>
|
||||
const node = await document.getElementById(ROOT_ELEMENT_ID);
|
||||
|
||||
const App = (
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<InfraPluginRoot />
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
|
||||
if (node) {
|
||||
ReactDOM.render(App, node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { compose } from '../lib/compose/testing_compose';
|
||||
import { startApp } from './start_app';
|
||||
startApp(compose());
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import isEqual from 'lodash/fp/isEqual';
|
||||
import React from 'react';
|
||||
|
||||
import { Badge } from 'ui/chrome/api/badge';
|
||||
import { Breadcrumb } from 'ui/chrome/api/breadcrumbs';
|
||||
|
||||
interface ExternalHeaderProps {
|
||||
breadcrumbs?: Breadcrumb[];
|
||||
setBreadcrumbs: (breadcrumbs: Breadcrumb[]) => void;
|
||||
badge: Badge | undefined;
|
||||
setBadge: (badge: Badge | undefined) => void;
|
||||
}
|
||||
|
||||
export class ExternalHeader extends React.Component<ExternalHeaderProps> {
|
||||
public componentDidMount() {
|
||||
this.setBreadcrumbs();
|
||||
this.setBadge();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: ExternalHeaderProps) {
|
||||
if (!isEqual(this.props.breadcrumbs, prevProps.breadcrumbs)) {
|
||||
this.setBreadcrumbs();
|
||||
}
|
||||
|
||||
if (!isEqual(this.props.badge, prevProps.badge)) {
|
||||
this.setBadge();
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private setBadge = () => {
|
||||
this.props.setBadge(this.props.badge);
|
||||
};
|
||||
|
||||
private setBreadcrumbs = () => {
|
||||
this.props.setBreadcrumbs(this.props.breadcrumbs || []);
|
||||
};
|
||||
}
|
|
@ -4,40 +4,51 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Breadcrumb } from 'ui/chrome/api/breadcrumbs';
|
||||
import { WithKibanaChrome } from '../../containers/with_kibana_chrome';
|
||||
import { ExternalHeader } from './external_header';
|
||||
import { ChromeBreadcrumb } from 'src/core/public';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface HeaderProps {
|
||||
breadcrumbs?: Breadcrumb[];
|
||||
breadcrumbs?: ChromeBreadcrumb[];
|
||||
readOnlyBadge?: boolean;
|
||||
}
|
||||
|
||||
export const Header = ({ breadcrumbs = [], readOnlyBadge = false }: HeaderProps) => (
|
||||
<WithKibanaChrome>
|
||||
{({ setBreadcrumbs, setBadge }) => (
|
||||
<ExternalHeader
|
||||
breadcrumbs={breadcrumbs}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
badge={
|
||||
readOnlyBadge
|
||||
? {
|
||||
text: i18n.translate('xpack.infra.header.badge.readOnly.text', {
|
||||
defaultMessage: 'Read only',
|
||||
}),
|
||||
tooltip: i18n.translate('xpack.infra.header.badge.readOnly.tooltip', {
|
||||
defaultMessage: 'Unable to change source configuration',
|
||||
}),
|
||||
iconType: 'glasses',
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
setBadge={setBadge}
|
||||
/>
|
||||
)}
|
||||
</WithKibanaChrome>
|
||||
);
|
||||
export const Header = ({ breadcrumbs = [], readOnlyBadge = false }: HeaderProps) => {
|
||||
const chrome = useKibana().services.chrome;
|
||||
|
||||
const badge = readOnlyBadge
|
||||
? {
|
||||
text: i18n.translate('xpack.infra.header.badge.readOnly.text', {
|
||||
defaultMessage: 'Read only',
|
||||
}),
|
||||
tooltip: i18n.translate('xpack.infra.header.badge.readOnly.tooltip', {
|
||||
defaultMessage: 'Unable to change source configuration',
|
||||
}),
|
||||
iconType: 'glasses',
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const setBreadcrumbs = useCallback(() => {
|
||||
return chrome?.setBreadcrumbs(breadcrumbs || []);
|
||||
}, [breadcrumbs, chrome]);
|
||||
|
||||
const setBadge = useCallback(() => {
|
||||
return chrome?.setBadge(badge);
|
||||
}, [badge, chrome]);
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumbs();
|
||||
setBadge();
|
||||
}, [setBreadcrumbs, setBadge]);
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumbs();
|
||||
}, [breadcrumbs, setBreadcrumbs]);
|
||||
|
||||
useEffect(() => {
|
||||
setBadge();
|
||||
}, [badge, setBadge]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import chrome from 'ui/chrome';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface HelpCenterContentProps {
|
||||
feedbackLink: string;
|
||||
|
@ -13,8 +13,10 @@ interface HelpCenterContentProps {
|
|||
}
|
||||
|
||||
export const HelpCenterContent: React.FC<HelpCenterContentProps> = ({ feedbackLink, appName }) => {
|
||||
const chrome = useKibana().services.chrome;
|
||||
|
||||
useEffect(() => {
|
||||
chrome.helpExtension.set({
|
||||
return chrome?.setHelpExtension({
|
||||
appName,
|
||||
links: [
|
||||
{
|
||||
|
@ -23,7 +25,7 @@ export const HelpCenterContent: React.FC<HelpCenterContentProps> = ({ feedbackLi
|
|||
},
|
||||
],
|
||||
});
|
||||
}, [feedbackLink, appName]);
|
||||
}, [feedbackLink, appName, chrome]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -8,10 +8,9 @@ import { EuiButton } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { encode } from 'rison-node';
|
||||
import chrome from 'ui/chrome';
|
||||
import { QueryString } from 'ui/utils/query_string';
|
||||
import url from 'url';
|
||||
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { TimeRange } from '../../../../common/http_api/shared/time_range';
|
||||
|
||||
export const AnalyzeInMlButton: React.FunctionComponent<{
|
||||
|
@ -19,7 +18,11 @@ export const AnalyzeInMlButton: React.FunctionComponent<{
|
|||
partition?: string;
|
||||
timeRange: TimeRange;
|
||||
}> = ({ jobId, partition, timeRange }) => {
|
||||
const pathname = chrome.addBasePath('/app/ml');
|
||||
const prependBasePath = useKibana().services.http?.basePath?.prepend;
|
||||
if (!prependBasePath) {
|
||||
return null;
|
||||
}
|
||||
const pathname = prependBasePath('/app/ml');
|
||||
const buttonLabel = (
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.analyzeInMlButtonLabel"
|
||||
|
|
|
@ -8,8 +8,7 @@ import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } f
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import url from 'url';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { InfraLogItem } from '../../../graphql/types';
|
||||
import { useVisibilityState } from '../../../utils/use_visibility_state';
|
||||
import { getTraceUrl } from '../../../../../apm/public/components/shared/Links/apm/ExternalLinks';
|
||||
|
@ -19,11 +18,18 @@ const UPTIME_FIELDS = ['container.id', 'host.ip', 'kubernetes.pod.uid'];
|
|||
export const LogEntryActionsMenu: React.FunctionComponent<{
|
||||
logItem: InfraLogItem;
|
||||
}> = ({ logItem }) => {
|
||||
const prependBasePath = useKibana().services.http?.basePath?.prepend;
|
||||
const { hide, isVisible, show } = useVisibilityState(false);
|
||||
|
||||
const uptimeLink = useMemo(() => getUptimeLink(logItem), [logItem]);
|
||||
const uptimeLink = useMemo(() => {
|
||||
const link = getUptimeLink(logItem);
|
||||
return prependBasePath && link ? prependBasePath(link) : link;
|
||||
}, [logItem, prependBasePath]);
|
||||
|
||||
const apmLink = useMemo(() => getAPMLink(logItem), [logItem]);
|
||||
const apmLink = useMemo(() => {
|
||||
const link = getAPMLink(logItem);
|
||||
return prependBasePath && link ? prependBasePath(link) : link;
|
||||
}, [logItem, prependBasePath]);
|
||||
|
||||
const menuItems = useMemo(
|
||||
() => [
|
||||
|
@ -56,7 +62,6 @@ export const LogEntryActionsMenu: React.FunctionComponent<{
|
|||
);
|
||||
|
||||
const hasMenuItems = useMemo(() => menuItems.length > 0, [menuItems]);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
|
@ -94,7 +99,7 @@ const getUptimeLink = (logItem: InfraLogItem) => {
|
|||
}
|
||||
|
||||
return url.format({
|
||||
pathname: chrome.addBasePath('/app/uptime'),
|
||||
pathname: '/app/uptime',
|
||||
hash: `/?search=(${searchExpressions.join(' OR ')})`,
|
||||
});
|
||||
};
|
||||
|
@ -123,7 +128,7 @@ const getAPMLink = (logItem: InfraLogItem) => {
|
|||
: { rangeFrom: 'now-1y', rangeTo: 'now' };
|
||||
|
||||
return url.format({
|
||||
pathname: chrome.addBasePath('/app/apm'),
|
||||
pathname: '/app/apm',
|
||||
hash: getTraceUrl({ traceId: traceIdEntry.value, rangeFrom, rangeTo }),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -17,8 +17,6 @@ import {
|
|||
} from '@elastic/charts';
|
||||
import { first, last } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
|
@ -36,6 +34,7 @@ import { MetricsExplorerNoMetrics } from './no_metrics';
|
|||
import { getChartTheme } from './helpers/get_chart_theme';
|
||||
import { useKibanaUiSetting } from '../../utils/use_kibana_ui_setting';
|
||||
import { calculateDomain } from './helpers/calculate_domain';
|
||||
import { useKibana, useUiSetting } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface Props {
|
||||
title?: string | null;
|
||||
|
@ -48,132 +47,130 @@ interface Props {
|
|||
source: SourceQuery.Query['source']['configuration'] | undefined;
|
||||
timeRange: MetricsExplorerTimeOptions;
|
||||
onTimeChange: (start: string, end: string) => void;
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const MetricsExplorerChart = injectUICapabilities(
|
||||
({
|
||||
source,
|
||||
options,
|
||||
chartOptions,
|
||||
series,
|
||||
title,
|
||||
onFilter,
|
||||
height = 200,
|
||||
width = '100%',
|
||||
timeRange,
|
||||
onTimeChange,
|
||||
uiCapabilities,
|
||||
}: Props) => {
|
||||
const { metrics } = options;
|
||||
const [dateFormat] = useKibanaUiSetting('dateFormat');
|
||||
const handleTimeChange = (from: number, to: number) => {
|
||||
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
|
||||
};
|
||||
const dateFormatter = useMemo(
|
||||
() =>
|
||||
series.rows.length > 0
|
||||
? niceTimeFormatter([first(series.rows).timestamp, last(series.rows).timestamp])
|
||||
: (value: number) => `${value}`,
|
||||
[series.rows]
|
||||
);
|
||||
const tooltipProps = {
|
||||
headerFormatter: useCallback(
|
||||
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
|
||||
[dateFormat]
|
||||
),
|
||||
};
|
||||
const yAxisFormater = useCallback(createFormatterForMetric(first(metrics)), [options]);
|
||||
const dataDomain = calculateDomain(series, metrics, chartOptions.stack);
|
||||
const domain =
|
||||
chartOptions.yAxisMode === MetricsExplorerYAxisMode.fromZero
|
||||
? { ...dataDomain, min: 0 }
|
||||
: dataDomain;
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
{options.groupBy ? (
|
||||
<EuiTitle size="xs">
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<ChartTitle>
|
||||
<EuiToolTip content={title}>
|
||||
<span>{title}</span>
|
||||
</EuiToolTip>
|
||||
</ChartTitle>
|
||||
<EuiFlexItem grow={false}>
|
||||
<MetricsExplorerChartContextMenu
|
||||
timeRange={timeRange}
|
||||
options={options}
|
||||
chartOptions={chartOptions}
|
||||
series={series}
|
||||
onFilter={onFilter}
|
||||
source={source}
|
||||
uiCapabilities={uiCapabilities}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiTitle>
|
||||
) : (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
export const MetricsExplorerChart = ({
|
||||
source,
|
||||
options,
|
||||
chartOptions,
|
||||
series,
|
||||
title,
|
||||
onFilter,
|
||||
height = 200,
|
||||
width = '100%',
|
||||
timeRange,
|
||||
onTimeChange,
|
||||
}: Props) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const isDarkMode = useUiSetting<boolean>('theme:darkMode');
|
||||
const { metrics } = options;
|
||||
const [dateFormat] = useKibanaUiSetting('dateFormat');
|
||||
const handleTimeChange = (from: number, to: number) => {
|
||||
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
|
||||
};
|
||||
const dateFormatter = useMemo(
|
||||
() =>
|
||||
series.rows.length > 0
|
||||
? niceTimeFormatter([first(series.rows).timestamp, last(series.rows).timestamp])
|
||||
: (value: number) => `${value}`,
|
||||
[series.rows]
|
||||
);
|
||||
const tooltipProps = {
|
||||
headerFormatter: useCallback(
|
||||
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
|
||||
[dateFormat]
|
||||
),
|
||||
};
|
||||
const yAxisFormater = useCallback(createFormatterForMetric(first(metrics)), [options]);
|
||||
const dataDomain = calculateDomain(series, metrics, chartOptions.stack);
|
||||
const domain =
|
||||
chartOptions.yAxisMode === MetricsExplorerYAxisMode.fromZero
|
||||
? { ...dataDomain, min: 0 }
|
||||
: dataDomain;
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
{options.groupBy ? (
|
||||
<EuiTitle size="xs">
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<ChartTitle>
|
||||
<EuiToolTip content={title}>
|
||||
<span>{title}</span>
|
||||
</EuiToolTip>
|
||||
</ChartTitle>
|
||||
<EuiFlexItem grow={false}>
|
||||
<MetricsExplorerChartContextMenu
|
||||
timeRange={timeRange}
|
||||
options={options}
|
||||
chartOptions={chartOptions}
|
||||
series={series}
|
||||
onFilter={onFilter}
|
||||
source={source}
|
||||
timeRange={timeRange}
|
||||
uiCapabilities={uiCapabilities}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiTitle>
|
||||
) : (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<MetricsExplorerChartContextMenu
|
||||
options={options}
|
||||
chartOptions={chartOptions}
|
||||
series={series}
|
||||
source={source}
|
||||
timeRange={timeRange}
|
||||
uiCapabilities={uiCapabilities}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<div className="infrastructureChart" style={{ height, width }}>
|
||||
{series.rows.length > 0 ? (
|
||||
<Chart>
|
||||
{metrics.map((metric, id) => (
|
||||
<MetricExplorerSeriesChart
|
||||
type={chartOptions.type}
|
||||
key={id}
|
||||
metric={metric}
|
||||
id={id}
|
||||
series={series}
|
||||
stack={chartOptions.stack}
|
||||
/>
|
||||
))}
|
||||
<Axis
|
||||
id={getAxisId('timestamp')}
|
||||
position={Position.Bottom}
|
||||
showOverlappingTicks={true}
|
||||
tickFormat={dateFormatter}
|
||||
/>
|
||||
<Axis
|
||||
id={getAxisId('values')}
|
||||
position={Position.Left}
|
||||
tickFormat={yAxisFormater}
|
||||
domain={domain}
|
||||
/>
|
||||
<Settings
|
||||
tooltip={tooltipProps}
|
||||
onBrushEnd={handleTimeChange}
|
||||
theme={getChartTheme(isDarkMode)}
|
||||
/>
|
||||
</Chart>
|
||||
) : options.metrics.length > 0 ? (
|
||||
<MetricsExplorerEmptyChart />
|
||||
) : (
|
||||
<MetricsExplorerNoMetrics />
|
||||
)}
|
||||
<div className="infrastructureChart" style={{ height, width }}>
|
||||
{series.rows.length > 0 ? (
|
||||
<Chart>
|
||||
{metrics.map((metric, id) => (
|
||||
<MetricExplorerSeriesChart
|
||||
type={chartOptions.type}
|
||||
key={id}
|
||||
metric={metric}
|
||||
id={id}
|
||||
series={series}
|
||||
stack={chartOptions.stack}
|
||||
/>
|
||||
))}
|
||||
<Axis
|
||||
id={getAxisId('timestamp')}
|
||||
position={Position.Bottom}
|
||||
showOverlappingTicks={true}
|
||||
tickFormat={dateFormatter}
|
||||
/>
|
||||
<Axis
|
||||
id={getAxisId('values')}
|
||||
position={Position.Left}
|
||||
tickFormat={yAxisFormater}
|
||||
domain={domain}
|
||||
/>
|
||||
<Settings
|
||||
tooltip={tooltipProps}
|
||||
onBrushEnd={handleTimeChange}
|
||||
theme={getChartTheme()}
|
||||
/>
|
||||
</Chart>
|
||||
) : options.metrics.length > 0 ? (
|
||||
<MetricsExplorerEmptyChart />
|
||||
) : (
|
||||
<MetricsExplorerNoMetrics />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ChartTitle = euiStyled.div`
|
||||
width: 100%
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
flex: 1 1 auto;
|
||||
margin: 12px;
|
||||
`;
|
||||
width: 100%
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
flex: 1 1 auto;
|
||||
margin: 12px;
|
||||
`;
|
||||
|
|
|
@ -8,13 +8,13 @@ import React from 'react';
|
|||
import { MetricsExplorerChartContextMenu, createNodeDetailLink } from './chart_context_menu';
|
||||
import { mount } from 'enzyme';
|
||||
import { options, source, timeRange, chartOptions } from '../../utils/fixtures/metrics_explorer';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { InfraNodeType } from '../../graphql/types';
|
||||
import DateMath from '@elastic/datemath';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { Capabilities } from 'src/core/public';
|
||||
|
||||
const series = { id: 'exmaple-01', rows: [], columns: [] };
|
||||
const uiCapabilities: UICapabilities = {
|
||||
const uiCapabilities: Capabilities = {
|
||||
navLinks: { show: false },
|
||||
management: { fake: { show: false } },
|
||||
catalogue: { show: false },
|
||||
|
|
|
@ -12,8 +12,8 @@ import {
|
|||
EuiContextMenuPanelDescriptor,
|
||||
EuiPopover,
|
||||
} from '@elastic/eui';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import DateMath from '@elastic/datemath';
|
||||
import { Capabilities } from 'src/core/public';
|
||||
import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
|
@ -31,7 +31,7 @@ interface Props {
|
|||
series: MetricsExplorerSeries;
|
||||
source?: SourceConfiguration;
|
||||
timeRange: MetricsExplorerTimeOptions;
|
||||
uiCapabilities: UICapabilities;
|
||||
uiCapabilities?: Capabilities;
|
||||
chartOptions: MetricsExplorerChartOptions;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ export const MetricsExplorerChartContextMenu = ({
|
|||
]
|
||||
: [];
|
||||
|
||||
const openInVisualize = uiCapabilities.visualize.show
|
||||
const openInVisualize = uiCapabilities?.visualize?.show
|
||||
? [
|
||||
{
|
||||
name: i18n.translate('xpack.infra.metricsExplorer.openInTSVB', {
|
||||
|
|
|
@ -8,14 +8,14 @@ import { EuiComboBox } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { IFieldType } from 'src/plugins/data/public';
|
||||
import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import { isDisplayable } from '../../utils/is_displayable';
|
||||
|
||||
interface Props {
|
||||
options: MetricsExplorerOptions;
|
||||
onChange: (groupBy: string | null) => void;
|
||||
fields: FieldType[];
|
||||
fields: IFieldType[];
|
||||
}
|
||||
|
||||
export const MetricsExplorerGroupBy = ({ options, onChange, fields }: Props) => {
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts';
|
||||
|
||||
export function getChartTheme(): Theme {
|
||||
const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode');
|
||||
export function getChartTheme(isDarkMode: boolean): Theme {
|
||||
return isDarkMode ? DARK_THEME : LIGHT_THEME;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { EuiComboBox } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { IFieldType } from 'src/plugins/data/public';
|
||||
import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette';
|
||||
import { MetricsExplorerMetric } from '../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
|
@ -18,7 +18,7 @@ interface Props {
|
|||
autoFocus?: boolean;
|
||||
options: MetricsExplorerOptions;
|
||||
onChange: (metrics: MetricsExplorerMetric[]) => void;
|
||||
fields: FieldType[];
|
||||
fields: IFieldType[];
|
||||
}
|
||||
|
||||
interface SelectedOption {
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
import { EuiButtonEmpty, EuiFlexGroup } from '@elastic/eui';
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useSavedView } from '../../hooks/use_saved_view';
|
||||
import { SavedViewCreateModal } from './create_modal';
|
||||
import { SavedViewListFlyout } from './view_list_flyout';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface Props<ViewState> {
|
||||
viewType: string;
|
||||
viewState: ViewState;
|
||||
|
@ -20,6 +21,7 @@ interface Props<ViewState> {
|
|||
}
|
||||
|
||||
export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
|
||||
const kibana = useKibana();
|
||||
const {
|
||||
views,
|
||||
saveView,
|
||||
|
@ -77,11 +79,11 @@ export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
|
|||
|
||||
useEffect(() => {
|
||||
if (errorOnCreate) {
|
||||
toastNotifications.addWarning(getErrorToast('create', errorOnCreate)!);
|
||||
kibana.notifications.toasts.warning(getErrorToast('create', errorOnCreate)!);
|
||||
} else if (errorOnFind) {
|
||||
toastNotifications.addWarning(getErrorToast('find', errorOnFind)!);
|
||||
kibana.notifications.toasts.warning(getErrorToast('find', errorOnFind)!);
|
||||
}
|
||||
}, [errorOnCreate, errorOnFind]);
|
||||
}, [errorOnCreate, errorOnFind, kibana]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -119,6 +121,7 @@ export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
|
|||
const getErrorToast = (type: 'create' | 'find', msg?: string) => {
|
||||
if (type === 'create') {
|
||||
return {
|
||||
toastLifeTimeMs: 3000,
|
||||
title:
|
||||
msg ||
|
||||
i18n.translate('xpack.infra.savedView.errorOnCreate.title', {
|
||||
|
@ -127,6 +130,7 @@ const getErrorToast = (type: 'create' | 'find', msg?: string) => {
|
|||
};
|
||||
} else if (type === 'find') {
|
||||
return {
|
||||
toastLifeTimeMs: 3000,
|
||||
title:
|
||||
msg ||
|
||||
i18n.translate('xpack.infra.savedView.findError.title', {
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
import { EuiButton, EuiComboBox, EuiForm, EuiFormRow } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { IFieldType } from 'src/plugins/data/public';
|
||||
|
||||
interface Props {
|
||||
onSubmit: (field: string) => void;
|
||||
fields: FieldType[];
|
||||
fields: IFieldType[];
|
||||
}
|
||||
|
||||
interface SelectedOption {
|
||||
|
|
|
@ -13,13 +13,12 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React from 'react';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { InfraNodeType } from '../../graphql/types';
|
||||
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
|
||||
import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to';
|
||||
import { createUptimeLink } from './lib/create_uptime_link';
|
||||
import { findInventoryModel } from '../../../common/inventory_models';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface Props {
|
||||
options: InfraWaffleMapOptions;
|
||||
|
@ -29,102 +28,99 @@ interface Props {
|
|||
nodeType: InfraNodeType;
|
||||
isPopoverOpen: boolean;
|
||||
closePopover: () => void;
|
||||
uiCapabilities: UICapabilities;
|
||||
popoverPosition: EuiPopoverProps['anchorPosition'];
|
||||
}
|
||||
|
||||
export const NodeContextMenu = injectUICapabilities(
|
||||
({
|
||||
options,
|
||||
currentTime,
|
||||
children,
|
||||
node,
|
||||
isPopoverOpen,
|
||||
closePopover,
|
||||
nodeType,
|
||||
uiCapabilities,
|
||||
popoverPosition,
|
||||
}: Props) => {
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
// Due to the changing nature of the fields between APM and this UI,
|
||||
// We need to have some exceptions until 7.0 & ECS is finalized. Reference
|
||||
// #26620 for the details for these fields.
|
||||
// TODO: This is tech debt, remove it after 7.0 & ECS migration.
|
||||
const apmField = nodeType === InfraNodeType.host ? 'host.hostname' : inventoryModel.fields.id;
|
||||
export const NodeContextMenu = ({
|
||||
options,
|
||||
currentTime,
|
||||
children,
|
||||
node,
|
||||
isPopoverOpen,
|
||||
closePopover,
|
||||
nodeType,
|
||||
popoverPosition,
|
||||
}: Props) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
// Due to the changing nature of the fields between APM and this UI,
|
||||
// We need to have some exceptions until 7.0 & ECS is finalized. Reference
|
||||
// #26620 for the details for these fields.
|
||||
// TODO: This is tech debt, remove it after 7.0 & ECS migration.
|
||||
const apmField = nodeType === InfraNodeType.host ? 'host.hostname' : inventoryModel.fields.id;
|
||||
|
||||
const nodeLogsMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
|
||||
defaultMessage: 'View logs',
|
||||
}),
|
||||
href: getNodeLogsUrl({
|
||||
nodeType,
|
||||
nodeId: node.id,
|
||||
time: currentTime,
|
||||
}),
|
||||
'data-test-subj': 'viewLogsContextMenuItem',
|
||||
};
|
||||
const nodeLogsMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
|
||||
defaultMessage: 'View logs',
|
||||
}),
|
||||
href: getNodeLogsUrl({
|
||||
nodeType,
|
||||
nodeId: node.id,
|
||||
time: currentTime,
|
||||
}),
|
||||
'data-test-subj': 'viewLogsContextMenuItem',
|
||||
};
|
||||
|
||||
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
|
||||
const nodeDetailMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', {
|
||||
defaultMessage: 'View metrics',
|
||||
}),
|
||||
href: getNodeDetailUrl({
|
||||
nodeType,
|
||||
nodeId: node.id,
|
||||
from: nodeDetailFrom,
|
||||
to: currentTime,
|
||||
}),
|
||||
};
|
||||
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
|
||||
const nodeDetailMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', {
|
||||
defaultMessage: 'View metrics',
|
||||
}),
|
||||
href: getNodeDetailUrl({
|
||||
nodeType,
|
||||
nodeId: node.id,
|
||||
from: nodeDetailFrom,
|
||||
to: currentTime,
|
||||
}),
|
||||
};
|
||||
|
||||
const apmTracesMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', {
|
||||
defaultMessage: 'View APM traces',
|
||||
}),
|
||||
href: `../app/apm#/traces?_g=()&kuery=${apmField}:"${node.id}"`,
|
||||
'data-test-subj': 'viewApmTracesContextMenuItem',
|
||||
};
|
||||
const apmTracesMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', {
|
||||
defaultMessage: 'View APM traces',
|
||||
}),
|
||||
href: `../app/apm#/traces?_g=()&kuery=${apmField}:"${node.id}"`,
|
||||
'data-test-subj': 'viewApmTracesContextMenuItem',
|
||||
};
|
||||
|
||||
const uptimeMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', {
|
||||
defaultMessage: 'View in Uptime',
|
||||
}),
|
||||
href: createUptimeLink(options, nodeType, node),
|
||||
};
|
||||
const uptimeMenuItem = {
|
||||
name: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', {
|
||||
defaultMessage: 'View in Uptime',
|
||||
}),
|
||||
href: createUptimeLink(options, nodeType, node),
|
||||
};
|
||||
|
||||
const showDetail = inventoryModel.crosslinkSupport.details;
|
||||
const showLogsLink =
|
||||
inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities.logs.show;
|
||||
const showAPMTraceLink =
|
||||
inventoryModel.crosslinkSupport.apm && uiCapabilities.apm && uiCapabilities.apm.show;
|
||||
const showUptimeLink =
|
||||
inventoryModel.crosslinkSupport.uptime &&
|
||||
([InfraNodeType.pod, InfraNodeType.container].includes(nodeType) || node.ip);
|
||||
const showDetail = inventoryModel.crosslinkSupport.details;
|
||||
const showLogsLink =
|
||||
inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities?.logs?.show;
|
||||
const showAPMTraceLink =
|
||||
inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show;
|
||||
const showUptimeLink =
|
||||
inventoryModel.crosslinkSupport.uptime &&
|
||||
([InfraNodeType.pod, InfraNodeType.container].includes(nodeType) || node.ip);
|
||||
|
||||
const items = [
|
||||
...(showLogsLink ? [nodeLogsMenuItem] : []),
|
||||
...(showDetail ? [nodeDetailMenuItem] : []),
|
||||
...(showAPMTraceLink ? [apmTracesMenuItem] : []),
|
||||
...(showUptimeLink ? [uptimeMenuItem] : []),
|
||||
];
|
||||
const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, title: '', items }];
|
||||
const items = [
|
||||
...(showLogsLink ? [nodeLogsMenuItem] : []),
|
||||
...(showDetail ? [nodeDetailMenuItem] : []),
|
||||
...(showAPMTraceLink ? [apmTracesMenuItem] : []),
|
||||
...(showUptimeLink ? [uptimeMenuItem] : []),
|
||||
];
|
||||
const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, title: '', items }];
|
||||
|
||||
// If there is nothing to show then we need to return the child as is
|
||||
if (items.length === 0) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
closePopover={closePopover}
|
||||
id={`${node.pathId}-popover`}
|
||||
isOpen={isPopoverOpen}
|
||||
button={children}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition={popoverPosition}
|
||||
>
|
||||
<EuiContextMenu initialPanelId={0} panels={panels} data-test-subj="nodeContextMenu" />
|
||||
</EuiPopover>
|
||||
);
|
||||
// If there is nothing to show then we need to return the child as is
|
||||
if (items.length === 0) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
closePopover={closePopover}
|
||||
id={`${node.pathId}-popover`}
|
||||
isOpen={isPopoverOpen}
|
||||
button={children}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition={popoverPosition}
|
||||
>
|
||||
<EuiContextMenu initialPanelId={0} panels={panels} data-test-subj="nodeContextMenu" />
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { IFieldType } from 'src/plugins/data/public';
|
||||
import { InfraNodeType, InfraSnapshotGroupbyInput } from '../../graphql/types';
|
||||
import { InfraGroupByOptions } from '../../lib/lib';
|
||||
import { CustomFieldPanel } from './custom_field_panel';
|
||||
|
@ -28,7 +28,7 @@ interface Props {
|
|||
groupBy: InfraSnapshotGroupbyInput[];
|
||||
onChange: (groupBy: InfraSnapshotGroupbyInput[]) => void;
|
||||
onChangeCustomOptions: (options: InfraGroupByOptions[]) => void;
|
||||
fields: FieldType[];
|
||||
fields: IFieldType[];
|
||||
customOptions: InfraGroupByOptions[];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as rt from 'io-ts';
|
|||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
import { getDatafeedId, getJobId } from '../../../../../common/log_analysis';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
|
@ -19,9 +19,8 @@ export const callDeleteJobs = async <JobType extends string>(
|
|||
jobTypes: JobType[]
|
||||
) => {
|
||||
// NOTE: Deleting the jobs via this API will delete the datafeeds at the same time
|
||||
const deleteJobsResponse = await kfetch({
|
||||
const deleteJobsResponse = await npStart.core.http.fetch('/api/ml/jobs/delete_jobs', {
|
||||
method: 'POST',
|
||||
pathname: '/api/ml/jobs/delete_jobs',
|
||||
body: JSON.stringify(
|
||||
deleteJobsRequestPayloadRT.encode({
|
||||
jobIds: jobTypes.map(jobType => getJobId(spaceId, sourceId, jobType)),
|
||||
|
@ -36,10 +35,9 @@ export const callDeleteJobs = async <JobType extends string>(
|
|||
};
|
||||
|
||||
export const callGetJobDeletionTasks = async () => {
|
||||
const jobDeletionTasksResponse = await kfetch({
|
||||
method: 'GET',
|
||||
pathname: '/api/ml/jobs/deleting_jobs_tasks',
|
||||
});
|
||||
const jobDeletionTasksResponse = await npStart.core.http.fetch(
|
||||
'/api/ml/jobs/deleting_jobs_tasks'
|
||||
);
|
||||
|
||||
return pipe(
|
||||
getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse),
|
||||
|
@ -53,9 +51,8 @@ export const callStopDatafeeds = async <JobType extends string>(
|
|||
jobTypes: JobType[]
|
||||
) => {
|
||||
// Stop datafeed due to https://github.com/elastic/kibana/issues/44652
|
||||
const stopDatafeedResponse = await kfetch({
|
||||
const stopDatafeedResponse = await npStart.core.http.fetch('/api/ml/jobs/stop_datafeeds', {
|
||||
method: 'POST',
|
||||
pathname: '/api/ml/jobs/stop_datafeeds',
|
||||
body: JSON.stringify(
|
||||
stopDatafeedsRequestPayloadRT.encode({
|
||||
datafeedIds: jobTypes.map(jobType => getDatafeedId(spaceId, sourceId, jobType)),
|
||||
|
|
|
@ -8,8 +8,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
|
|||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import * as rt from 'io-ts';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { jobCustomSettingsRT } from './ml_api_types';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
import { getJobId } from '../../../../../common/log_analysis';
|
||||
|
@ -19,9 +18,8 @@ export const callJobsSummaryAPI = async <JobType extends string>(
|
|||
sourceId: string,
|
||||
jobTypes: JobType[]
|
||||
) => {
|
||||
const response = await kfetch({
|
||||
const response = await npStart.core.http.fetch('/api/ml/jobs/jobs_summary', {
|
||||
method: 'POST',
|
||||
pathname: '/api/ml/jobs/jobs_summary',
|
||||
body: JSON.stringify(
|
||||
fetchJobStatusRequestPayloadRT.encode({
|
||||
jobIds: jobTypes.map(jobType => getJobId(spaceId, sourceId, jobType)),
|
||||
|
|
|
@ -8,15 +8,13 @@ import { fold } from 'fp-ts/lib/Either';
|
|||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import * as rt from 'io-ts';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
import { jobCustomSettingsRT } from './ml_api_types';
|
||||
|
||||
export const callGetMlModuleAPI = async (moduleId: string) => {
|
||||
const response = await kfetch({
|
||||
const response = await npStart.core.http.fetch(`/api/ml/modules/get_module/${moduleId}`, {
|
||||
method: 'GET',
|
||||
pathname: `/api/ml/modules/get_module/${moduleId}`,
|
||||
});
|
||||
|
||||
return pipe(
|
||||
|
|
|
@ -8,8 +8,7 @@ import { fold } from 'fp-ts/lib/Either';
|
|||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import * as rt from 'io-ts';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
import { getJobIdPrefix } from '../../../../../common/log_analysis';
|
||||
|
||||
|
@ -23,9 +22,8 @@ export const callSetupMlModuleAPI = async (
|
|||
jobOverrides: SetupMlModuleJobOverrides[] = [],
|
||||
datafeedOverrides: SetupMlModuleDatafeedOverrides[] = []
|
||||
) => {
|
||||
const response = await kfetch({
|
||||
const response = await npStart.core.http.fetch(`/api/ml/modules/setup/${moduleId}`, {
|
||||
method: 'POST',
|
||||
pathname: `/api/ml/modules/setup/${moduleId}`,
|
||||
body: JSON.stringify(
|
||||
setupMlModuleRequestPayloadRT.encode({
|
||||
start,
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import {
|
||||
LOG_ANALYSIS_VALIDATE_INDICES_PATH,
|
||||
ValidationIndicesFieldSpecification,
|
||||
|
@ -22,9 +21,8 @@ export const callValidateIndicesAPI = async (
|
|||
indices: string[],
|
||||
fields: ValidationIndicesFieldSpecification[]
|
||||
) => {
|
||||
const response = await kfetch({
|
||||
const response = await npStart.core.http.fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, {
|
||||
method: 'POST',
|
||||
pathname: LOG_ANALYSIS_VALIDATE_INDICES_PATH,
|
||||
body: JSON.stringify(validationIndicesRequestPayloadRT.encode({ data: { indices, fields } })),
|
||||
});
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
import createContainer from 'constate';
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
|
@ -27,10 +26,7 @@ export const useLogAnalysisCapabilities = () => {
|
|||
{
|
||||
cancelPreviousOn: 'resolution',
|
||||
createPromise: async () => {
|
||||
const rawResponse = await kfetch({
|
||||
method: 'GET',
|
||||
pathname: '/api/ml/ml_capabilities',
|
||||
});
|
||||
const rawResponse = await npStart.core.http.fetch('/api/ml/ml_capabilities');
|
||||
|
||||
return pipe(
|
||||
getMlCapabilitiesResponsePayloadRT.decode(rawResponse),
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { Badge } from 'ui/chrome/api/badge';
|
||||
import { Breadcrumb } from 'ui/chrome/api/breadcrumbs';
|
||||
import { RendererFunction } from '../utils/typed_react';
|
||||
|
||||
interface WithKibanaChromeProps {
|
||||
children: RendererFunction<
|
||||
{
|
||||
setBreadcrumbs: (newBreadcrumbs: Breadcrumb[]) => void;
|
||||
setBadge: (badge: Badge | undefined) => void;
|
||||
} & WithKibanaChromeState
|
||||
>;
|
||||
}
|
||||
|
||||
interface WithKibanaChromeState {
|
||||
basePath: string;
|
||||
}
|
||||
|
||||
export class WithKibanaChrome extends React.Component<
|
||||
WithKibanaChromeProps,
|
||||
WithKibanaChromeState
|
||||
> {
|
||||
public state: WithKibanaChromeState = {
|
||||
basePath: chrome.getBasePath(),
|
||||
};
|
||||
|
||||
public render() {
|
||||
return this.props.children({
|
||||
...this.state,
|
||||
setBreadcrumbs: chrome.breadcrumbs.set,
|
||||
setBadge: chrome.badge.set,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,57 +5,69 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { IHttpFetchError } from 'src/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KFetchError } from 'ui/kfetch/kfetch_error';
|
||||
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { useTrackedPromise } from '../utils/use_tracked_promise';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export function useHTTPRequest<Response>(
|
||||
pathname: string,
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD',
|
||||
body?: string,
|
||||
decode: (response: any) => Response = response => response
|
||||
) {
|
||||
const kibana = useKibana();
|
||||
const fetch = kibana.services.http?.fetch;
|
||||
const toasts = kibana.notifications.toasts;
|
||||
const [response, setResponse] = useState<Response | null>(null);
|
||||
const [error, setError] = useState<KFetchError | null>(null);
|
||||
const [error, setError] = useState<IHttpFetchError | null>(null);
|
||||
const [request, makeRequest] = useTrackedPromise(
|
||||
{
|
||||
cancelPreviousOn: 'resolution',
|
||||
createPromise: () =>
|
||||
kfetch({
|
||||
createPromise: () => {
|
||||
if (!fetch) {
|
||||
throw new Error('HTTP service is unavailable');
|
||||
}
|
||||
return fetch(pathname, {
|
||||
method,
|
||||
pathname,
|
||||
body,
|
||||
}),
|
||||
});
|
||||
},
|
||||
onResolve: resp => setResponse(decode(resp)),
|
||||
onReject: (e: unknown) => {
|
||||
const err = e as KFetchError;
|
||||
const err = e as IHttpFetchError;
|
||||
setError(err);
|
||||
toastNotifications.addWarning({
|
||||
toasts.warning({
|
||||
toastLifeTimeMs: 3000,
|
||||
title: i18n.translate('xpack.infra.useHTTPRequest.error.title', {
|
||||
defaultMessage: `Error while fetching resource`,
|
||||
}),
|
||||
text: toMountPoint(
|
||||
body: (
|
||||
<div>
|
||||
<h5>
|
||||
{i18n.translate('xpack.infra.useHTTPRequest.error.status', {
|
||||
defaultMessage: `Error`,
|
||||
})}
|
||||
</h5>
|
||||
{err.res?.statusText} ({err.res?.status})
|
||||
<h5>
|
||||
{i18n.translate('xpack.infra.useHTTPRequest.error.url', {
|
||||
defaultMessage: `URL`,
|
||||
})}
|
||||
</h5>
|
||||
{err.res?.url}
|
||||
{err.response ? (
|
||||
<>
|
||||
<h5>
|
||||
{i18n.translate('xpack.infra.useHTTPRequest.error.status', {
|
||||
defaultMessage: `Error`,
|
||||
})}
|
||||
</h5>
|
||||
{err.response?.statusText} ({err.response?.status})
|
||||
<h5>
|
||||
{i18n.translate('xpack.infra.useHTTPRequest.error.url', {
|
||||
defaultMessage: `URL`,
|
||||
})}
|
||||
</h5>
|
||||
{err.response?.url}
|
||||
</>
|
||||
) : (
|
||||
<h5>{err.message}</h5>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
});
|
||||
},
|
||||
},
|
||||
[pathname, body, method]
|
||||
[pathname, body, method, fetch, toasts]
|
||||
);
|
||||
|
||||
const loading = useMemo(() => {
|
||||
|
|
|
@ -4,4 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// NP_NOTE: Whilst we are in the transition period of the NP migration, this index file
|
||||
// is exclusively for our static code exports that other plugins (e.g. APM) use.
|
||||
// When we switch over to the real NP, and an export of "plugin" is expected and called,
|
||||
// we can do away with the middle "app.ts" layer. The "app.ts" layer is needed for now,
|
||||
// and needs to be situated differently to this index file, so that our code for setting the root template
|
||||
// and attempting to start the app doesn't try to run just because another plugin is importing from this file.
|
||||
|
||||
export { useTrackPageview } from './hooks/use_track_metric';
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { IModule, IScope } from 'angular';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { UIRoutes as KibanaUIRoutes } from 'ui/routes';
|
||||
|
||||
import {
|
||||
InfraBufferedKibanaServiceCall,
|
||||
InfraFrameworkAdapter,
|
||||
InfraKibanaAdapterServiceRefs,
|
||||
InfraKibanaUIConfig,
|
||||
InfraTimezoneProvider,
|
||||
InfraUiKibanaAdapterScope,
|
||||
} from '../../lib';
|
||||
|
||||
const ROOT_ELEMENT_ID = 'react-infra-root';
|
||||
const BREADCRUMBS_ELEMENT_ID = 'react-infra-breadcrumbs';
|
||||
|
||||
export class KibanaFramework implements InfraFrameworkAdapter {
|
||||
public appState: object;
|
||||
public kbnVersion?: string;
|
||||
public timezone?: string;
|
||||
|
||||
private adapterService: KibanaAdapterServiceProvider;
|
||||
private timezoneProvider: InfraTimezoneProvider;
|
||||
private rootComponent: React.ReactElement<any> | null = null;
|
||||
private breadcrumbsComponent: React.ReactElement<any> | null = null;
|
||||
|
||||
constructor(
|
||||
uiModule: IModule,
|
||||
uiRoutes: KibanaUIRoutes,
|
||||
timezoneProvider: InfraTimezoneProvider
|
||||
) {
|
||||
this.adapterService = new KibanaAdapterServiceProvider();
|
||||
this.timezoneProvider = timezoneProvider;
|
||||
this.appState = {};
|
||||
this.register(uiModule, uiRoutes);
|
||||
}
|
||||
|
||||
public setUISettings = (key: string, value: any) => {
|
||||
this.adapterService.callOrBuffer(({ config }) => {
|
||||
config.set(key, value);
|
||||
});
|
||||
};
|
||||
|
||||
public render = (component: React.ReactElement<any>) => {
|
||||
this.adapterService.callOrBuffer(() => (this.rootComponent = component));
|
||||
};
|
||||
|
||||
public renderBreadcrumbs = (component: React.ReactElement<any>) => {
|
||||
this.adapterService.callOrBuffer(() => (this.breadcrumbsComponent = component));
|
||||
};
|
||||
|
||||
private register = (adapterModule: IModule, uiRoutes: KibanaUIRoutes) => {
|
||||
adapterModule.provider('kibanaAdapter', this.adapterService);
|
||||
|
||||
adapterModule.directive('infraUiKibanaAdapter', () => ({
|
||||
controller: ($scope: InfraUiKibanaAdapterScope, $element: JQLite) => ({
|
||||
$onDestroy: () => {
|
||||
const targetRootElement = $element[0].querySelector(`#${ROOT_ELEMENT_ID}`);
|
||||
const targetBreadcrumbsElement = $element[0].querySelector(`#${ROOT_ELEMENT_ID}`);
|
||||
|
||||
if (targetRootElement) {
|
||||
ReactDOM.unmountComponentAtNode(targetRootElement);
|
||||
}
|
||||
|
||||
if (targetBreadcrumbsElement) {
|
||||
ReactDOM.unmountComponentAtNode(targetBreadcrumbsElement);
|
||||
}
|
||||
},
|
||||
$onInit: () => {
|
||||
$scope.topNavMenu = [];
|
||||
},
|
||||
$postLink: () => {
|
||||
$scope.$watchGroup(
|
||||
[
|
||||
() => this.breadcrumbsComponent,
|
||||
() => $element[0].querySelector(`#${BREADCRUMBS_ELEMENT_ID}`),
|
||||
],
|
||||
([breadcrumbsComponent, targetElement]) => {
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (breadcrumbsComponent) {
|
||||
ReactDOM.render(breadcrumbsComponent, targetElement);
|
||||
} else {
|
||||
ReactDOM.unmountComponentAtNode(targetElement);
|
||||
}
|
||||
}
|
||||
);
|
||||
$scope.$watchGroup(
|
||||
[() => this.rootComponent, () => $element[0].querySelector(`#${ROOT_ELEMENT_ID}`)],
|
||||
([rootComponent, targetElement]) => {
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootComponent) {
|
||||
ReactDOM.render(rootComponent, targetElement);
|
||||
} else {
|
||||
ReactDOM.unmountComponentAtNode(targetElement);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
}),
|
||||
scope: true,
|
||||
template: `
|
||||
<main
|
||||
id="${ROOT_ELEMENT_ID}"
|
||||
class="infReactRoot"
|
||||
></main>
|
||||
`,
|
||||
}));
|
||||
|
||||
adapterModule.run(
|
||||
(
|
||||
config: InfraKibanaUIConfig,
|
||||
kbnVersion: string,
|
||||
Private: <Provider>(provider: Provider) => Provider,
|
||||
// @ts-ignore: inject kibanaAdapter to force eager instatiation
|
||||
kibanaAdapter: any
|
||||
) => {
|
||||
this.timezone = Private(this.timezoneProvider)();
|
||||
this.kbnVersion = kbnVersion;
|
||||
}
|
||||
);
|
||||
|
||||
uiRoutes.enable();
|
||||
|
||||
uiRoutes.otherwise({
|
||||
reloadOnSearch: false,
|
||||
template:
|
||||
'<infra-ui-kibana-adapter style="display: flex; align-items: stretch; flex: 1 0 0%;"></infra-ui-kibana-adapter>',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
class KibanaAdapterServiceProvider {
|
||||
public serviceRefs: InfraKibanaAdapterServiceRefs | null = null;
|
||||
public bufferedCalls: Array<InfraBufferedKibanaServiceCall<InfraKibanaAdapterServiceRefs>> = [];
|
||||
|
||||
public $get($rootScope: IScope, config: InfraKibanaUIConfig) {
|
||||
this.serviceRefs = {
|
||||
config,
|
||||
rootScope: $rootScope,
|
||||
};
|
||||
|
||||
this.applyBufferedCalls(this.bufferedCalls);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public callOrBuffer(serviceCall: (serviceRefs: InfraKibanaAdapterServiceRefs) => void) {
|
||||
if (this.serviceRefs !== null) {
|
||||
this.applyBufferedCalls([serviceCall]);
|
||||
} else {
|
||||
this.bufferedCalls.push(serviceCall);
|
||||
}
|
||||
}
|
||||
|
||||
public applyBufferedCalls(
|
||||
bufferedCalls: Array<InfraBufferedKibanaServiceCall<InfraKibanaAdapterServiceRefs>>
|
||||
) {
|
||||
if (!this.serviceRefs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.serviceRefs.rootScope.$apply(() => {
|
||||
bufferedCalls.forEach(serviceCall => {
|
||||
if (!this.serviceRefs) {
|
||||
return;
|
||||
}
|
||||
return serviceCall(this.serviceRefs);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { InfraFrameworkAdapter } from '../../lib';
|
||||
|
||||
export class InfraTestingFrameworkAdapter implements InfraFrameworkAdapter {
|
||||
public appState?: object;
|
||||
public kbnVersion?: string;
|
||||
public timezone?: string;
|
||||
|
||||
constructor() {
|
||||
this.appState = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return;
|
||||
}
|
||||
public renderBreadcrumbs() {
|
||||
return;
|
||||
}
|
||||
public setUISettings() {
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -16,13 +16,13 @@ import {
|
|||
export class InfraKibanaObservableApiAdapter implements InfraObservableApi {
|
||||
private basePath: string;
|
||||
private defaultHeaders: {
|
||||
[headerName: string]: string;
|
||||
[headerName: string]: boolean | string;
|
||||
};
|
||||
|
||||
constructor({ basePath, xsrfToken }: { basePath: string; xsrfToken: string }) {
|
||||
constructor({ basePath }: { basePath: string }) {
|
||||
this.basePath = basePath;
|
||||
this.defaultHeaders = {
|
||||
'kbn-version': xsrfToken,
|
||||
'kbn-xsrf': true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import 'ui/autoload/all';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import chrome from 'ui/chrome';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import { uiModules } from 'ui/modules';
|
||||
import uiRoutes from 'ui/routes';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
|
||||
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import ApolloClient from 'apollo-client';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
import { HttpLink } from 'apollo-link-http';
|
||||
import { withClientState } from 'apollo-link-state';
|
||||
import { InfraFrontendLibs } from '../lib';
|
||||
import introspectionQueryResultData from '../../graphql/introspection.json';
|
||||
import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter';
|
||||
import { InfraKibanaObservableApiAdapter } from '../adapters/observable_api/kibana_observable_api';
|
||||
|
||||
export function compose(): InfraFrontendLibs {
|
||||
const cache = new InMemoryCache({
|
||||
addTypename: false,
|
||||
fragmentMatcher: new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
}),
|
||||
});
|
||||
|
||||
const observableApi = new InfraKibanaObservableApiAdapter({
|
||||
basePath: chrome.getBasePath(),
|
||||
xsrfToken: chrome.getXsrfToken(),
|
||||
});
|
||||
|
||||
const graphQLOptions = {
|
||||
cache,
|
||||
link: ApolloLink.from([
|
||||
withClientState({
|
||||
cache,
|
||||
resolvers: {},
|
||||
}),
|
||||
new HttpLink({
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'kbn-xsrf': chrome.getXsrfToken(),
|
||||
},
|
||||
uri: `${chrome.getBasePath()}/api/infra/graphql`,
|
||||
}),
|
||||
]),
|
||||
};
|
||||
|
||||
const apolloClient = new ApolloClient(graphQLOptions);
|
||||
|
||||
const infraModule = uiModules.get('app/infa');
|
||||
|
||||
const framework = new KibanaFramework(infraModule, uiRoutes, timezoneProvider);
|
||||
|
||||
const libs: InfraFrontendLibs = {
|
||||
apolloClient,
|
||||
framework,
|
||||
observableApi,
|
||||
};
|
||||
return libs;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import 'ui/autoload/all';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import chrome from 'ui/chrome';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import { uiModules } from 'ui/modules';
|
||||
import uiRoutes from 'ui/routes';
|
||||
// @ts-ignore: path dynamic for kibana
|
||||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import ApolloClient from 'apollo-client';
|
||||
import { SchemaLink } from 'apollo-link-schema';
|
||||
import { addMockFunctionsToSchema, makeExecutableSchema } from 'graphql-tools';
|
||||
import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter';
|
||||
import { InfraKibanaObservableApiAdapter } from '../adapters/observable_api/kibana_observable_api';
|
||||
import { InfraFrontendLibs } from '../lib';
|
||||
|
||||
export function compose(): InfraFrontendLibs {
|
||||
const infraModule = uiModules.get('app/infa');
|
||||
const observableApi = new InfraKibanaObservableApiAdapter({
|
||||
basePath: chrome.getBasePath(),
|
||||
xsrfToken: chrome.getXsrfToken(),
|
||||
});
|
||||
const framework = new KibanaFramework(infraModule, uiRoutes, timezoneProvider);
|
||||
const typeDefs = `
|
||||
Query {}
|
||||
`;
|
||||
|
||||
const mocks = {
|
||||
Mutation: () => undefined,
|
||||
Query: () => undefined,
|
||||
};
|
||||
|
||||
const schema = makeExecutableSchema({ typeDefs });
|
||||
addMockFunctionsToSchema({
|
||||
mocks,
|
||||
schema,
|
||||
});
|
||||
|
||||
const cache = new InMemoryCache((window as any).__APOLLO_CLIENT__);
|
||||
|
||||
const apolloClient = new ApolloClient({
|
||||
cache,
|
||||
link: new SchemaLink({ schema }),
|
||||
});
|
||||
|
||||
const libs: InfraFrontendLibs = {
|
||||
apolloClient,
|
||||
framework,
|
||||
observableApi,
|
||||
};
|
||||
return libs;
|
||||
}
|
|
@ -20,7 +20,6 @@ import {
|
|||
} from '../graphql/types';
|
||||
|
||||
export interface InfraFrontendLibs {
|
||||
framework: InfraFrameworkAdapter;
|
||||
apolloClient: InfraApolloClient;
|
||||
observableApi: InfraObservableApi;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { compose } from '../lib/compose/kibana_compose';
|
||||
import { startApp } from './start_app';
|
||||
startApp(compose());
|
||||
import { PluginInitializerContext } from 'kibana/public';
|
||||
import { Plugin } from './new_platform_plugin';
|
||||
|
||||
export function plugin(context: PluginInitializerContext) {
|
||||
return new Plugin(context);
|
||||
}
|
63
x-pack/legacy/plugins/infra/public/new_platform_plugin.ts
Normal file
63
x-pack/legacy/plugins/infra/public/new_platform_plugin.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { CoreStart, PluginInitializerContext } from 'kibana/public';
|
||||
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import ApolloClient from 'apollo-client';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
import { HttpLink } from 'apollo-link-http';
|
||||
import { withClientState } from 'apollo-link-state';
|
||||
import { startApp } from './apps/start_app';
|
||||
import { InfraFrontendLibs } from './lib/lib';
|
||||
import introspectionQueryResultData from './graphql/introspection.json';
|
||||
import { InfraKibanaObservableApiAdapter } from './lib/adapters/observable_api/kibana_observable_api';
|
||||
|
||||
type ClientPlugins = any;
|
||||
type LegacyDeps = any;
|
||||
|
||||
export class Plugin {
|
||||
constructor(context: PluginInitializerContext) {}
|
||||
start(core: CoreStart, plugins: ClientPlugins, __LEGACY: LegacyDeps) {
|
||||
startApp(this.composeLibs(core, plugins, __LEGACY), core, plugins);
|
||||
}
|
||||
|
||||
composeLibs(core: CoreStart, plugins: ClientPlugins, legacy: LegacyDeps) {
|
||||
const cache = new InMemoryCache({
|
||||
addTypename: false,
|
||||
fragmentMatcher: new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
}),
|
||||
});
|
||||
|
||||
const observableApi = new InfraKibanaObservableApiAdapter({
|
||||
basePath: core.http.basePath.get(),
|
||||
});
|
||||
|
||||
const graphQLOptions = {
|
||||
cache,
|
||||
link: ApolloLink.from([
|
||||
withClientState({
|
||||
cache,
|
||||
resolvers: {},
|
||||
}),
|
||||
new HttpLink({
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'kbn-xsrf': true,
|
||||
},
|
||||
uri: `${core.http.basePath.get()}/api/infra/graphql`,
|
||||
}),
|
||||
]),
|
||||
};
|
||||
|
||||
const apolloClient = new ApolloClient(graphQLOptions);
|
||||
|
||||
const libs: InfraFrontendLibs = {
|
||||
apolloClient,
|
||||
observableApi,
|
||||
};
|
||||
return libs;
|
||||
}
|
||||
}
|
|
@ -8,8 +8,6 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import React from 'react';
|
||||
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
|
||||
import { DocumentTitle } from '../../components/document_title';
|
||||
import { HelpCenterContent } from '../../components/help_center_content';
|
||||
|
@ -25,13 +23,11 @@ import { SnapshotPage } from './snapshot';
|
|||
import { SettingsPage } from '../shared/settings';
|
||||
import { AppNavigation } from '../../components/navigation/app_navigation';
|
||||
import { SourceLoadingPage } from '../../components/source_loading_page';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface InfrastructurePageProps extends RouteComponentProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const InfrastructurePage = injectUICapabilities(
|
||||
({ match, uiCapabilities }: InfrastructurePageProps) => (
|
||||
export const InfrastructurePage = ({ match }: RouteComponentProps) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
return (
|
||||
<Source.Provider sourceId="default">
|
||||
<ColumnarPage>
|
||||
<DocumentTitle
|
||||
|
@ -55,7 +51,7 @@ export const InfrastructurePage = injectUICapabilities(
|
|||
}),
|
||||
},
|
||||
]}
|
||||
readOnlyBadge={!uiCapabilities.infrastructure.save}
|
||||
readOnlyBadge={!uiCapabilities?.infrastructure?.save}
|
||||
/>
|
||||
|
||||
<AppNavigation
|
||||
|
@ -114,5 +110,5 @@ export const InfrastructurePage = injectUICapabilities(
|
|||
</Switch>
|
||||
</ColumnarPage>
|
||||
</Source.Provider>
|
||||
)
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,8 +8,6 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { SnapshotPageContent } from './page_content';
|
||||
import { SnapshotToolbar } from './toolbar';
|
||||
|
||||
|
@ -27,15 +25,11 @@ import { Source } from '../../../containers/source';
|
|||
import { WithWaffleFilterUrlState } from '../../../containers/waffle/with_waffle_filters';
|
||||
import { WithWaffleOptionsUrlState } from '../../../containers/waffle/with_waffle_options';
|
||||
import { WithWaffleTimeUrlState } from '../../../containers/waffle/with_waffle_time';
|
||||
import { WithKibanaChrome } from '../../../containers/with_kibana_chrome';
|
||||
import { useTrackPageview } from '../../../hooks/use_track_metric';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface SnapshotPageProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const SnapshotPage = injectUICapabilities((props: SnapshotPageProps) => {
|
||||
const { uiCapabilities } = props;
|
||||
export const SnapshotPage = () => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const {
|
||||
createDerivedIndexPattern,
|
||||
hasFailedLoadingSource,
|
||||
|
@ -44,7 +38,7 @@ export const SnapshotPage = injectUICapabilities((props: SnapshotPageProps) => {
|
|||
loadSource,
|
||||
metricIndicesExist,
|
||||
} = useContext(Source.Context);
|
||||
|
||||
const basePath = useKibana().services.http?.basePath || '';
|
||||
useTrackPageview({ app: 'infra_metrics', path: 'inventory' });
|
||||
useTrackPageview({ app: 'infra_metrics', path: 'inventory', delay: 15000 });
|
||||
|
||||
|
@ -73,49 +67,44 @@ export const SnapshotPage = injectUICapabilities((props: SnapshotPageProps) => {
|
|||
) : hasFailedLoadingSource ? (
|
||||
<SourceErrorPage errorMessage={loadSourceFailureMessage || ''} retry={loadSource} />
|
||||
) : (
|
||||
<WithKibanaChrome>
|
||||
{({ basePath }) => (
|
||||
<NoIndices
|
||||
title={i18n.translate('xpack.infra.homePage.noMetricsIndicesTitle', {
|
||||
defaultMessage: "Looks like you don't have any metrics indices.",
|
||||
})}
|
||||
message={i18n.translate('xpack.infra.homePage.noMetricsIndicesDescription', {
|
||||
defaultMessage: "Let's add some!",
|
||||
})}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/metrics`}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="infrastructureViewSetupInstructionsButton"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel',
|
||||
{ defaultMessage: 'View setup instructions' }
|
||||
)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{uiCapabilities.infrastructure.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
{i18n.translate('xpack.infra.configureSourceActionLabel', {
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
data-test-subj="noMetricsIndicesPrompt"
|
||||
/>
|
||||
)}
|
||||
</WithKibanaChrome>
|
||||
<NoIndices
|
||||
title={i18n.translate('xpack.infra.homePage.noMetricsIndicesTitle', {
|
||||
defaultMessage: "Looks like you don't have any metrics indices.",
|
||||
})}
|
||||
message={i18n.translate('xpack.infra.homePage.noMetricsIndicesDescription', {
|
||||
defaultMessage: "Let's add some!",
|
||||
})}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/metrics`}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="infrastructureViewSetupInstructionsButton"
|
||||
>
|
||||
{i18n.translate('xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel', {
|
||||
defaultMessage: 'View setup instructions',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{uiCapabilities?.infrastructure?.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
{i18n.translate('xpack.infra.configureSourceActionLabel', {
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
data-test-subj="noMetricsIndicesPrompt"
|
||||
/>
|
||||
)}
|
||||
</ColumnarPage>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
|
||||
import { DocumentTitle } from '../../components/document_title';
|
||||
import { HelpCenterContent } from '../../components/help_center_content';
|
||||
|
@ -27,14 +25,12 @@ import {
|
|||
} from '../../containers/logs/log_analysis';
|
||||
import { useSourceId } from '../../containers/source_id';
|
||||
import { RedirectWithQueryParams } from '../../utils/redirect_with_query_params';
|
||||
import { useKibana } from '../../../../../../..//src/plugins/kibana_react/public';
|
||||
import { LogEntryCategoriesPage } from './log_entry_categories';
|
||||
import { LogEntryRatePage } from './log_entry_rate';
|
||||
|
||||
interface LogsPageProps extends RouteComponentProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPageProps) => {
|
||||
export const LogsPage = ({ match }: RouteComponentProps) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const [sourceId] = useSourceId();
|
||||
const source = useSource({ sourceId });
|
||||
const logAnalysisCapabilities = useLogAnalysisCapabilities();
|
||||
|
@ -83,7 +79,7 @@ export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPag
|
|||
text: pageTitle,
|
||||
},
|
||||
]}
|
||||
readOnlyBadge={!uiCapabilities.logs.save}
|
||||
readOnlyBadge={!uiCapabilities?.logs?.save}
|
||||
/>
|
||||
{source.isLoadingSource ||
|
||||
(!source.isLoadingSource &&
|
||||
|
@ -124,7 +120,7 @@ export const LogsPage = injectUICapabilities(({ match, uiCapabilities }: LogsPag
|
|||
</LogAnalysisCapabilities.Context.Provider>
|
||||
</Source.Context.Provider>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const pageTitle = i18n.translate('xpack.infra.header.logsTitle', {
|
||||
defaultMessage: 'Logs',
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import {
|
||||
getLogEntryRateRequestPayloadRT,
|
||||
getLogEntryRateSuccessReponsePayloadRT,
|
||||
|
@ -22,9 +21,8 @@ export const callGetLogEntryRateAPI = async (
|
|||
endTime: number,
|
||||
bucketDuration: number
|
||||
) => {
|
||||
const response = await kfetch({
|
||||
const response = await npStart.core.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, {
|
||||
method: 'POST',
|
||||
pathname: LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH,
|
||||
body: JSON.stringify(
|
||||
getLogEntryRateRequestPayloadRT.encode({
|
||||
data: {
|
||||
|
|
|
@ -9,66 +9,53 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { NoIndices } from '../../../components/empty_states/no_indices';
|
||||
import { WithKibanaChrome } from '../../../containers/with_kibana_chrome';
|
||||
import {
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from '../../../components/source_configuration';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface LogsPageNoIndicesContentProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const LogsPageNoIndicesContent = injectUICapabilities(
|
||||
(props: LogsPageNoIndicesContentProps) => {
|
||||
const { uiCapabilities } = props;
|
||||
|
||||
return (
|
||||
<WithKibanaChrome>
|
||||
{({ basePath }) => (
|
||||
<NoIndices
|
||||
data-test-subj="noLogsIndicesPrompt"
|
||||
title={i18n.translate('xpack.infra.logsPage.noLoggingIndicesTitle', {
|
||||
defaultMessage: "Looks like you don't have any logging indices.",
|
||||
})}
|
||||
message={i18n.translate('xpack.infra.logsPage.noLoggingIndicesDescription', {
|
||||
defaultMessage: "Let's add some!",
|
||||
})}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/logging`}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="logsViewSetupInstructionsButton"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel',
|
||||
{ defaultMessage: 'View setup instructions' }
|
||||
)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{uiCapabilities.logs.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.logs}
|
||||
>
|
||||
{i18n.translate('xpack.infra.configureSourceActionLabel', {
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</WithKibanaChrome>
|
||||
);
|
||||
}
|
||||
);
|
||||
export const LogsPageNoIndicesContent = () => {
|
||||
const basePath = useKibana().services.http?.basePath || '';
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
return (
|
||||
<NoIndices
|
||||
data-test-subj="noLogsIndicesPrompt"
|
||||
title={i18n.translate('xpack.infra.logsPage.noLoggingIndicesTitle', {
|
||||
defaultMessage: "Looks like you don't have any logging indices.",
|
||||
})}
|
||||
message={i18n.translate('xpack.infra.logsPage.noLoggingIndicesDescription', {
|
||||
defaultMessage: "Let's add some!",
|
||||
})}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/logging`}
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="logsViewSetupInstructionsButton"
|
||||
>
|
||||
{i18n.translate('xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel', {
|
||||
defaultMessage: 'View setup instructions',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{uiCapabilities?.logs?.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.logs}
|
||||
>
|
||||
{i18n.translate('xpack.infra.configureSourceActionLabel', {
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
} from './helpers';
|
||||
import { ErrorMessage } from './error_message';
|
||||
import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting';
|
||||
import { useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { VisSectionProps } from '../types';
|
||||
|
||||
export const ChartSectionVis = ({
|
||||
|
@ -42,6 +43,7 @@ export const ChartSectionVis = ({
|
|||
seriesOverrides,
|
||||
type,
|
||||
}: VisSectionProps) => {
|
||||
const isDarkMode = useUiSetting<boolean>('theme:darkMode');
|
||||
const [dateFormat] = useKibanaUiSetting('dateFormat');
|
||||
const valueFormatter = useCallback(getFormatter(formatter, formatterTemplate), [
|
||||
formatter,
|
||||
|
@ -125,7 +127,7 @@ export const ChartSectionVis = ({
|
|||
<Settings
|
||||
tooltip={tooltipProps}
|
||||
onBrushEnd={handleTimeChange}
|
||||
theme={getChartTheme()}
|
||||
theme={getChartTheme(isDarkMode)}
|
||||
showLegend={true}
|
||||
legendPosition="right"
|
||||
/>
|
||||
|
|
|
@ -9,70 +9,67 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
import React from 'react';
|
||||
|
||||
import euiStyled from '../../../../../../common/eui_styled_components';
|
||||
import { WithKibanaChrome } from '../../../containers/with_kibana_chrome';
|
||||
import {
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from '../../../components/source_configuration';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface InvalidNodeErrorProps {
|
||||
nodeName: string;
|
||||
}
|
||||
|
||||
export const InvalidNodeError: React.FunctionComponent<InvalidNodeErrorProps> = ({ nodeName }) => {
|
||||
const basePath = useKibana().services.http?.basePath || '';
|
||||
return (
|
||||
<WithKibanaChrome>
|
||||
{({ basePath }) => (
|
||||
<CenteredEmptyPrompt
|
||||
title={
|
||||
<h2>
|
||||
<CenteredEmptyPrompt
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.invalidNodeErrorTitle"
|
||||
defaultMessage="Looks like {nodeName} isn't collecting any metrics data"
|
||||
values={{
|
||||
nodeName,
|
||||
}}
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.invalidNodeErrorDescription"
|
||||
defaultMessage="Double check your configuration"
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/metrics`}
|
||||
color="primary"
|
||||
fill
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.invalidNodeErrorTitle"
|
||||
defaultMessage="Looks like {nodeName} isn't collecting any metrics data"
|
||||
values={{
|
||||
nodeName,
|
||||
}}
|
||||
id="xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel"
|
||||
defaultMessage="View setup instructions"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<p>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metrics.invalidNodeErrorDescription"
|
||||
defaultMessage="Double check your configuration"
|
||||
id="xpack.infra.configureSourceActionLabel"
|
||||
defaultMessage="Change source configuration"
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
actions={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
href={`${basePath}/app/kibana#/home/tutorial_directory/metrics`}
|
||||
color="primary"
|
||||
fill
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel"
|
||||
defaultMessage="View setup instructions"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.configureSourceActionLabel"
|
||||
defaultMessage="Change source configuration"
|
||||
/>
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</WithKibanaChrome>
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// import { GraphQLFormattedError } from 'graphql';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KFetchError } from 'ui/kfetch/kfetch_error';
|
||||
import { IHttpFetchError } from 'src/core/public';
|
||||
import { InvalidNodeError } from './invalid_node';
|
||||
// import { InfraMetricsErrorCodes } from '../../../../common/errors';
|
||||
import { DocumentTitle } from '../../../components/document_title';
|
||||
|
@ -15,7 +15,7 @@ import { ErrorPageBody } from '../../error';
|
|||
|
||||
interface Props {
|
||||
name: string;
|
||||
error: KFetchError;
|
||||
error: IHttpFetchError;
|
||||
}
|
||||
|
||||
export const PageError = ({ error, name }: Props) => {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import euiStyled, { EuiTheme, withTheme } from '../../../../../common/eui_styled_components';
|
||||
import { DocumentTitle } from '../../components/document_title';
|
||||
import { Header } from '../../components/header';
|
||||
|
@ -20,6 +18,7 @@ import { InfraLoadingPanel } from '../../components/loading';
|
|||
import { findInventoryModel } from '../../../common/inventory_models';
|
||||
import { NavItem } from './lib/side_nav_context';
|
||||
import { NodeDetailsPage } from './components/node_details_page';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
const DetailPageContent = euiStyled(PageContent)`
|
||||
overflow: auto;
|
||||
|
@ -34,111 +33,109 @@ interface Props {
|
|||
node: string;
|
||||
};
|
||||
};
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const MetricDetail = withMetricPageProviders(
|
||||
injectUICapabilities(
|
||||
withTheme(({ uiCapabilities, match, theme }: Props) => {
|
||||
const nodeId = match.params.node;
|
||||
const nodeType = match.params.type as InfraNodeType;
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const {
|
||||
name,
|
||||
filteredRequiredMetrics,
|
||||
loading: metadataLoading,
|
||||
cloudId,
|
||||
metadata,
|
||||
} = useMetadata(nodeId, nodeType, inventoryModel.requiredMetrics, sourceId);
|
||||
withTheme(({ match, theme }: Props) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const nodeId = match.params.node;
|
||||
const nodeType = match.params.type as InfraNodeType;
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const {
|
||||
name,
|
||||
filteredRequiredMetrics,
|
||||
loading: metadataLoading,
|
||||
cloudId,
|
||||
metadata,
|
||||
} = useMetadata(nodeId, nodeType, inventoryModel.requiredMetrics, sourceId);
|
||||
|
||||
const [sideNav, setSideNav] = useState<NavItem[]>([]);
|
||||
const [sideNav, setSideNav] = useState<NavItem[]>([]);
|
||||
|
||||
const addNavItem = React.useCallback(
|
||||
(item: NavItem) => {
|
||||
if (!sideNav.some(n => n.id === item.id)) {
|
||||
setSideNav([item, ...sideNav]);
|
||||
}
|
||||
},
|
||||
[sideNav]
|
||||
);
|
||||
const addNavItem = React.useCallback(
|
||||
(item: NavItem) => {
|
||||
if (!sideNav.some(n => n.id === item.id)) {
|
||||
setSideNav([item, ...sideNav]);
|
||||
}
|
||||
},
|
||||
[sideNav]
|
||||
);
|
||||
|
||||
const breadcrumbs = [
|
||||
{
|
||||
href: '#/',
|
||||
text: i18n.translate('xpack.infra.header.infrastructureTitle', {
|
||||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
},
|
||||
{ text: name },
|
||||
];
|
||||
|
||||
if (metadataLoading && !filteredRequiredMetrics.length) {
|
||||
return (
|
||||
<InfraLoadingPanel
|
||||
height="100vh"
|
||||
width="100%"
|
||||
text={i18n.translate('xpack.infra.metrics.loadingNodeDataText', {
|
||||
defaultMessage: 'Loading data',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const breadcrumbs = [
|
||||
{
|
||||
href: '#/',
|
||||
text: i18n.translate('xpack.infra.header.infrastructureTitle', {
|
||||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
},
|
||||
{ text: name },
|
||||
];
|
||||
|
||||
if (metadataLoading && !filteredRequiredMetrics.length) {
|
||||
return (
|
||||
<WithMetricsTime>
|
||||
{({
|
||||
timeRange,
|
||||
parsedTimeRange,
|
||||
setTimeRange,
|
||||
refreshInterval,
|
||||
setRefreshInterval,
|
||||
isAutoReloading,
|
||||
setAutoReload,
|
||||
triggerRefresh,
|
||||
}) => (
|
||||
<ColumnarPage>
|
||||
<Header
|
||||
breadcrumbs={breadcrumbs}
|
||||
readOnlyBadge={!uiCapabilities.infrastructure.save}
|
||||
/>
|
||||
<WithMetricsTimeUrlState />
|
||||
<DocumentTitle
|
||||
title={i18n.translate('xpack.infra.metricDetailPage.documentTitle', {
|
||||
defaultMessage: 'Infrastructure | Metrics | {name}',
|
||||
values: {
|
||||
name,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<DetailPageContent data-test-subj="infraMetricsPage">
|
||||
{metadata ? (
|
||||
<NodeDetailsPage
|
||||
name={name}
|
||||
requiredMetrics={filteredRequiredMetrics}
|
||||
sourceId={sourceId}
|
||||
timeRange={timeRange}
|
||||
parsedTimeRange={parsedTimeRange}
|
||||
nodeType={nodeType}
|
||||
nodeId={nodeId}
|
||||
cloudId={cloudId}
|
||||
metadataLoading={metadataLoading}
|
||||
isAutoReloading={isAutoReloading}
|
||||
refreshInterval={refreshInterval}
|
||||
sideNav={sideNav}
|
||||
metadata={metadata}
|
||||
addNavItem={addNavItem}
|
||||
setRefreshInterval={setRefreshInterval}
|
||||
setAutoReload={setAutoReload}
|
||||
triggerRefresh={triggerRefresh}
|
||||
setTimeRange={setTimeRange}
|
||||
/>
|
||||
) : null}
|
||||
</DetailPageContent>
|
||||
</ColumnarPage>
|
||||
)}
|
||||
</WithMetricsTime>
|
||||
<InfraLoadingPanel
|
||||
height="100vh"
|
||||
width="100%"
|
||||
text={i18n.translate('xpack.infra.metrics.loadingNodeDataText', {
|
||||
defaultMessage: 'Loading data',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<WithMetricsTime>
|
||||
{({
|
||||
timeRange,
|
||||
parsedTimeRange,
|
||||
setTimeRange,
|
||||
refreshInterval,
|
||||
setRefreshInterval,
|
||||
isAutoReloading,
|
||||
setAutoReload,
|
||||
triggerRefresh,
|
||||
}) => (
|
||||
<ColumnarPage>
|
||||
<Header
|
||||
breadcrumbs={breadcrumbs}
|
||||
readOnlyBadge={!uiCapabilities?.infrastructure?.save}
|
||||
/>
|
||||
<WithMetricsTimeUrlState />
|
||||
<DocumentTitle
|
||||
title={i18n.translate('xpack.infra.metricDetailPage.documentTitle', {
|
||||
defaultMessage: 'Infrastructure | Metrics | {name}',
|
||||
values: {
|
||||
name,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<DetailPageContent data-test-subj="infraMetricsPage">
|
||||
{metadata ? (
|
||||
<NodeDetailsPage
|
||||
name={name}
|
||||
requiredMetrics={filteredRequiredMetrics}
|
||||
sourceId={sourceId}
|
||||
timeRange={timeRange}
|
||||
parsedTimeRange={parsedTimeRange}
|
||||
nodeType={nodeType}
|
||||
nodeId={nodeId}
|
||||
cloudId={cloudId}
|
||||
metadataLoading={metadataLoading}
|
||||
isAutoReloading={isAutoReloading}
|
||||
refreshInterval={refreshInterval}
|
||||
sideNav={sideNav}
|
||||
metadata={metadata}
|
||||
addNavItem={addNavItem}
|
||||
setRefreshInterval={setRefreshInterval}
|
||||
setAutoReload={setAutoReload}
|
||||
triggerRefresh={triggerRefresh}
|
||||
setTimeRange={setTimeRange}
|
||||
/>
|
||||
) : null}
|
||||
</DetailPageContent>
|
||||
</ColumnarPage>
|
||||
)}
|
||||
</WithMetricsTime>
|
||||
);
|
||||
})
|
||||
);
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { SourceConfigurationSettings } from '../../../components/source_configuration/source_configuration_settings';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface SettingsPageProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const SettingsPage = injectUICapabilities(({ uiCapabilities }: SettingsPageProps) => (
|
||||
<SourceConfigurationSettings shouldAllowEdit={uiCapabilities.logs.configureSource as boolean} />
|
||||
));
|
||||
export const SettingsPage = () => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
return (
|
||||
<SourceConfigurationSettings
|
||||
shouldAllowEdit={uiCapabilities?.logs?.configureSource as boolean}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,55 +8,54 @@ import { History } from 'history';
|
|||
import React from 'react';
|
||||
import { Route, Router, Switch } from 'react-router-dom';
|
||||
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { NotFoundPage } from './pages/404';
|
||||
import { InfrastructurePage } from './pages/infrastructure';
|
||||
import { LinkToPage } from './pages/link_to';
|
||||
import { LogsPage } from './pages/logs';
|
||||
import { MetricDetail } from './pages/metrics';
|
||||
import { RedirectWithQueryParams } from './utils/redirect_with_query_params';
|
||||
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface RouterProps {
|
||||
history: History;
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
const PageRouterComponent: React.FC<RouterProps> = ({ history, uiCapabilities }) => {
|
||||
export const PageRouter: React.FC<RouterProps> = ({ history }) => {
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<RedirectWithQueryParams from="/" exact={true} to="/infrastructure/inventory" />
|
||||
)}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<RedirectWithQueryParams
|
||||
from="/infrastructure"
|
||||
exact={true}
|
||||
to="/infrastructure/inventory"
|
||||
/>
|
||||
)}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<RedirectWithQueryParams
|
||||
from="/infrastructure/snapshot"
|
||||
exact={true}
|
||||
to="/infrastructure/inventory"
|
||||
/>
|
||||
)}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<RedirectWithQueryParams from="/home" exact={true} to="/infrastructure/inventory" />
|
||||
)}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<Route path="/infrastructure/metrics/:type/:node" component={MetricDetail} />
|
||||
)}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<RedirectWithQueryParams from="/metrics" to="/infrastructure/metrics" />
|
||||
)}
|
||||
{uiCapabilities.logs.show && (
|
||||
{uiCapabilities?.logs?.show && (
|
||||
<RedirectWithQueryParams from="/logs" exact={true} to="/logs/stream" />
|
||||
)}
|
||||
{uiCapabilities.logs.show && <Route path="/logs" component={LogsPage} />}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
{uiCapabilities?.logs?.show && <Route path="/logs" component={LogsPage} />}
|
||||
{uiCapabilities?.infrastructure?.show && (
|
||||
<Route path="/infrastructure" component={InfrastructurePage} />
|
||||
)}
|
||||
<Route path="/link-to" component={LinkToPage} />
|
||||
|
@ -65,5 +64,3 @@ const PageRouterComponent: React.FC<RouterProps> = ({ history, uiCapabilities })
|
|||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export const PageRouter = injectUICapabilities(PageRouterComponent);
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { IFieldType } from 'src/plugins/data/public';
|
||||
import { startsWith, uniq } from 'lodash';
|
||||
import { getAllowedListForPrefix } from '../../common/ecs_allowed_list';
|
||||
|
||||
interface DisplayableFieldType extends FieldType {
|
||||
interface DisplayableFieldType extends IFieldType {
|
||||
displayable?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
state: undefined,
|
||||
};
|
||||
const expectedSearchString =
|
||||
"logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194))&sourceId=default&_g=()";
|
||||
"logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194))&sourceId=default";
|
||||
const expectedRedirect = `/logs/stream?${expectedSearchString}`;
|
||||
|
||||
await pageObjects.common.navigateToActualUrl(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue