mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[LogsUI] [InfraUI] Turn source configuration into a tab and standardize the main navigation (#42243) (#42887)
* Setup Logs routing for multiple pages * Adds nested routing for logs * Adds an index page to handle shared concerns * Adds the Stream page at /logs/stream * Introduce shared settings page * Introduces shared/settings page * Adds shared/settings page as a tab in the Logs routes * Removes previous source configuration flyout traces from Logs pages * Begin styling adjustments to settings page * Implements use of EUI Panels * Centers page content * Add discard button * Add discard button to allow resetting the form * Fix button alignment * Align Apply and Discard buttons to the right * Align Loading button to the right * Add EuiDescribedFormGroup for all form fields * Add EuiDescribedFormGroup for name panel * Add EuiDescribedFormGroup for indices panel * Add EuiDescribedFormGroup for fields panel * Remove all SourceConfigurationFlyout traces from the Infrastructure UI * Add a ViewSourceConfigurationButton * Adds a ViewSourceConfigurationButton component that will route to the /settings page * Replace all instances of "View Configuration" buttons that were opening the flyout with the new button * Enable settings tab amongst Infrastructure routes * Change navigation to mimic SIEM * Introduces an AppNavigation component * Amends styling / handling of RoutedTabs to match SIEM implementation * Adds new AppNavigation component to Infrastructure and Logs indexe pages * Functional test amendments (WIP) * Temporarily disable certain functional tests * Remove unused imports * Disable imports so build can pass * Amend I18N errors * I18N * Automatically fix issues with i18n (node scripts/i18n_check --fix result) * Functional tests * Amend tests so they pass locally. Pending CI test. * Amend RoutedTabs (without link focus style) * Tweak RoutedTabs and AppNavigation for better performance / visuals * Ensure outline isn't cut off * Ensure only the react-router instance is hit for performance * Ensure links still have href attributes for things like "Open in a new tab" even if history.push ultimately navigates * Fix i18n usages * node scripts/i18n_check --fix * Amend functional test config (post Master merge fix) * Remove unused function and fix unused import * Add a Prompt to notify users when form changes will be lost * Add aria-label to Button
This commit is contained in:
parent
e08bcd9a5a
commit
ff80382daa
42 changed files with 1019 additions and 935 deletions
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButtonIcon } from '@elastic/eui';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -20,20 +19,13 @@ import {
|
|||
LogEntryColumnContent,
|
||||
LogEntryColumnWidth,
|
||||
LogEntryColumnWidths,
|
||||
iconColumnId,
|
||||
} from './log_entry_column';
|
||||
import { ASSUMED_SCROLLBAR_WIDTH } from './vertical_scroll_panel';
|
||||
|
||||
export const LogColumnHeaders = injectI18n<{
|
||||
columnConfigurations: LogColumnConfiguration[];
|
||||
columnWidths: LogEntryColumnWidths;
|
||||
showColumnConfiguration: () => void;
|
||||
}>(({ columnConfigurations, columnWidths, intl, showColumnConfiguration }) => {
|
||||
const showColumnConfigurationLabel = intl.formatMessage({
|
||||
id: 'xpack.infra.logColumnHeaders.configureColumnsLabel',
|
||||
defaultMessage: 'Configure source',
|
||||
});
|
||||
|
||||
}>(({ columnConfigurations, columnWidths, intl }) => {
|
||||
return (
|
||||
<LogColumnHeadersWrapper>
|
||||
{columnConfigurations.map(columnConfiguration => {
|
||||
|
@ -69,19 +61,6 @@ export const LogColumnHeaders = injectI18n<{
|
|||
);
|
||||
}
|
||||
})}
|
||||
<LogColumnHeader
|
||||
columnWidth={columnWidths[iconColumnId]}
|
||||
data-test-subj="logColumnHeader iconLogColumnHeader"
|
||||
key="iconColumnHeader"
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label={showColumnConfigurationLabel}
|
||||
color="text"
|
||||
iconType="gear"
|
||||
onClick={showColumnConfiguration}
|
||||
title={showColumnConfigurationLabel}
|
||||
/>
|
||||
</LogColumnHeader>
|
||||
</LogColumnHeadersWrapper>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -48,7 +48,6 @@ interface ScrollableLogTextStreamViewProps {
|
|||
loadNewerItems: () => void;
|
||||
setFlyoutItem: (id: string) => void;
|
||||
setFlyoutVisibility: (visible: boolean) => void;
|
||||
showColumnConfiguration: () => void;
|
||||
intl: InjectedIntl;
|
||||
highlightedItem: string | null;
|
||||
currentHighlightKey: UniqueTimeKey | null;
|
||||
|
@ -109,7 +108,6 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
|
|||
items,
|
||||
lastLoadedTime,
|
||||
scale,
|
||||
showColumnConfiguration,
|
||||
wrap,
|
||||
} = this.props;
|
||||
const { targetId } = this.state;
|
||||
|
@ -153,7 +151,6 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
|
|||
<LogColumnHeaders
|
||||
columnConfigurations={columnConfigurations}
|
||||
columnWidths={columnWidths}
|
||||
showColumnConfiguration={showColumnConfiguration}
|
||||
/>
|
||||
<AutoSizer bounds content detectAnyWindowResize="height">
|
||||
{({ measureRef, bounds: { height = 0 }, content: { width = 0 } }) => (
|
||||
|
|
|
@ -6,19 +6,20 @@
|
|||
|
||||
import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { WithKibanaChrome } from '../../containers/with_kibana_chrome';
|
||||
import {
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from '../../components/source_configuration';
|
||||
|
||||
interface InvalidNodeErrorProps {
|
||||
nodeName: string;
|
||||
}
|
||||
|
||||
export const InvalidNodeError: React.FunctionComponent<InvalidNodeErrorProps> = ({ nodeName }) => {
|
||||
const { showIndicesConfiguration } = useContext(SourceConfigurationFlyoutState.Context);
|
||||
|
||||
return (
|
||||
<WithKibanaChrome>
|
||||
{({ basePath }) => (
|
||||
|
@ -57,12 +58,15 @@ export const InvalidNodeError: React.FunctionComponent<InvalidNodeErrorProps> =
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButton color="primary" onClick={showIndicesConfiguration}>
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.configureSourceActionLabel"
|
||||
defaultMessage="Change source configuration"
|
||||
/>
|
||||
</EuiButton>
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
|
||||
interface AppNavigationProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const AppNavigation = ({ children }: AppNavigationProps) => (
|
||||
<Nav>
|
||||
<EuiFlexGroup gutterSize="m" alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem>{children}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</Nav>
|
||||
);
|
||||
|
||||
const Nav = euiStyled.nav`
|
||||
background: ${props => props.theme.eui.euiColorEmptyShade};
|
||||
border-bottom: ${props => props.theme.eui.euiBorderThin};
|
||||
padding: ${props =>
|
||||
`${props.theme.eui.euiSize} ${props.theme.eui.euiSizeL} ${props.theme.eui.euiSize} ${props.theme.eui.euiSizeL}`}
|
||||
|
||||
.euiTabs {
|
||||
padding-left: 3px;
|
||||
margin-left: -3px;
|
||||
}
|
||||
`;
|
|
@ -4,9 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiTab, EuiTabs } from '@elastic/eui';
|
||||
import { EuiTab, EuiTabs, EuiLink } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
|
||||
interface TabConfiguration {
|
||||
title: string;
|
||||
|
@ -17,27 +18,42 @@ interface RoutedTabsProps {
|
|||
tabs: TabConfiguration[];
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export class RoutedTabs extends React.Component<RoutedTabsProps> {
|
||||
public render() {
|
||||
return <EuiTabs>{this.renderTabs()}</EuiTabs>;
|
||||
return <EuiTabs display="condensed">{this.renderTabs()}</EuiTabs>;
|
||||
}
|
||||
|
||||
private renderTabs() {
|
||||
return this.props.tabs.map(tab => {
|
||||
return (
|
||||
<Route
|
||||
key={`${tab.path}${tab.title}`}
|
||||
key={`${tab.path}-${tab.title}`}
|
||||
path={tab.path}
|
||||
children={({ match, history }) => (
|
||||
<EuiTab
|
||||
onClick={() => (match ? undefined : history.push(tab.path))}
|
||||
isSelected={match !== null}
|
||||
>
|
||||
{tab.title}
|
||||
</EuiTab>
|
||||
<TabContainer className="euiTab">
|
||||
<EuiLink
|
||||
href={`#${tab.path}`}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
history.push(tab.path);
|
||||
}}
|
||||
>
|
||||
<EuiTab onClick={noop} isSelected={match !== null}>
|
||||
{tab.title}
|
||||
</EuiTab>
|
||||
</EuiLink>
|
||||
</TabContainer>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const TabContainer = euiStyled.div`
|
||||
.euiLink {
|
||||
color: inherit !important;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiCode, EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import {
|
||||
EuiDescribedFormGroup,
|
||||
EuiCode,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -39,145 +47,230 @@ export const FieldsConfigurationPanel = ({
|
|||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow
|
||||
error={timestampFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.timestampFieldDescription"
|
||||
defaultMessage="Timestamp used to sort log entries. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>@timestamp</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={timestampFieldProps.isInvalid}
|
||||
label={
|
||||
<EuiDescribedFormGroup
|
||||
idAria="timestampField"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.timestampFieldLabel"
|
||||
defaultMessage="Timestamp"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...timestampFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
error={tiebreakerFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.tiebreakerFieldDescription"
|
||||
defaultMessage="Field used to break ties between two entries with the same timestamp. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>_doc</EuiCode>,
|
||||
}}
|
||||
id="xpack.infra.sourceConfiguration.timestampFieldDescription"
|
||||
defaultMessage="Timestamp used to sort log entries"
|
||||
/>
|
||||
}
|
||||
isInvalid={tiebreakerFieldProps.isInvalid}
|
||||
label={
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={['timestampField']}
|
||||
error={timestampFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.timestampFieldRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>@timestamp</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={timestampFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.timestampFieldLabel"
|
||||
defaultMessage="Timestamp"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...timestampFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
idAria="tiebreakerField"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.tiebreakerFieldLabel"
|
||||
defaultMessage="Tiebreaker"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...tiebreakerFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
error={containerFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.containerFieldDescription"
|
||||
defaultMessage="Field used to identify Docker containers. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>container.id</EuiCode>,
|
||||
}}
|
||||
id="xpack.infra.sourceConfiguration.tiebreakerFieldDescription"
|
||||
defaultMessage="Field used to break ties between two entries with the same timestamp"
|
||||
/>
|
||||
}
|
||||
isInvalid={containerFieldProps.isInvalid}
|
||||
label={
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={['tiebreakerField']}
|
||||
error={tiebreakerFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>_doc</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={tiebreakerFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.tiebreakerFieldLabel"
|
||||
defaultMessage="Tiebreaker"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...tiebreakerFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
idAria="containerField"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.containerFieldLabel"
|
||||
defaultMessage="Container ID"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...containerFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
error={hostFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.hostFieldDescription"
|
||||
defaultMessage="Field used to identify hosts. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>host.name</EuiCode>,
|
||||
}}
|
||||
id="xpack.infra.sourceConfiguration.containerFieldDescription"
|
||||
defaultMessage="Field used to identify Docker containers"
|
||||
/>
|
||||
}
|
||||
isInvalid={hostFieldProps.isInvalid}
|
||||
label={
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={['containerField']}
|
||||
error={containerFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.containerFieldRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>container.id</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={containerFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.containerFieldLabel"
|
||||
defaultMessage="Container ID"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...containerFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
idAria="hostNameField"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.hostFieldLabel"
|
||||
id="xpack.infra.sourceConfiguration.hostNameFieldLabel"
|
||||
defaultMessage="Host name"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...hostFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
error={podFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.podFieldDescription"
|
||||
defaultMessage="Field used to identify Kubernetes pods. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>kubernetes.pod.uid</EuiCode>,
|
||||
}}
|
||||
id="xpack.infra.sourceConfiguration.hostNameFieldDescription"
|
||||
defaultMessage="Field used to identify hosts"
|
||||
/>
|
||||
}
|
||||
isInvalid={podFieldProps.isInvalid}
|
||||
label={
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={['hostNameField']}
|
||||
error={hostFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.hostFieldDescription"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>host.name</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={hostFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.hostFieldLabel"
|
||||
defaultMessage="Host name"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...hostFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
idAria="podField"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.podFieldLabel"
|
||||
defaultMessage="Pod ID"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.podFieldDescription"
|
||||
defaultMessage="Field used to identify Kubernetes pods"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
<EuiFormRow
|
||||
describedByIds={['podField']}
|
||||
error={podFieldProps.error}
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...podFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.podFieldRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>kubernetes.pod.uid</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={podFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.podFieldLabel"
|
||||
defaultMessage="Pod ID"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...podFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiForm>
|
||||
);
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { SourceConfigurationButton } from './source_configuration_button';
|
||||
export { SourceConfigurationFlyout } from './source_configuration_flyout';
|
||||
export { SourceConfigurationSettings } from './source_configuration_settings';
|
||||
export {
|
||||
SourceConfigurationFlyoutState,
|
||||
useSourceConfigurationFlyoutState,
|
||||
} from './source_configuration_flyout_state';
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from './view_source_configuration_button';
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiCode, EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import {
|
||||
EuiCode,
|
||||
EuiDescribedFormGroup,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -33,63 +41,97 @@ export const IndicesConfigurationPanel = ({
|
|||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow
|
||||
error={metricAliasFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
<EuiDescribedFormGroup
|
||||
idAria="matricIndices"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.metricIndicesTitle"
|
||||
defaultMessage="Metric Indices"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.metricIndicesDescription"
|
||||
defaultMessage="Index pattern for matching indices that contain Metricbeat data. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>metricbeat-*</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={metricAliasFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.metricIndicesLabel"
|
||||
defaultMessage="Metric indices"
|
||||
defaultMessage="Index pattern for matching indices that contain Metricbeat data"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="metricIndicesInput"
|
||||
<EuiFormRow
|
||||
describedByIds={['metricIndices']}
|
||||
error={metricAliasFieldProps.error}
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...metricAliasFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
error={logAliasFieldProps.error}
|
||||
fullWidth
|
||||
helpText={
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.metricIndicesRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>metricbeat-*</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={metricAliasFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.metricIndicesLabel"
|
||||
defaultMessage="Metric indices"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="metricIndicesInput"
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...metricAliasFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
idAria="logIndices"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logIndicesTitle"
|
||||
defaultMessage="Log Indices"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logIndicesDescription"
|
||||
defaultMessage="Index pattern for matching indices that contain log data. The recommended value is {defaultValue}."
|
||||
values={{
|
||||
defaultValue: <EuiCode>filebeat-*</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={logAliasFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logIndicesLabel"
|
||||
defaultMessage="Log indices"
|
||||
defaultMessage="Index pattern for matching indices that contain log data"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="logIndicesInput"
|
||||
<EuiFormRow
|
||||
describedByIds={['logIndices']}
|
||||
error={logAliasFieldProps.error}
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
readOnly={readOnly}
|
||||
{...logAliasFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logIndicesRecommendedValue"
|
||||
defaultMessage="The recommended value is {defaultValue}"
|
||||
values={{
|
||||
defaultValue: <EuiCode>filebeat-*</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
isInvalid={logAliasFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logIndicesLabel"
|
||||
defaultMessage="Log indices"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="logIndicesInput"
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
readOnly={readOnly}
|
||||
{...logAliasFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiForm>
|
||||
);
|
||||
|
|
|
@ -56,7 +56,7 @@ export const LogColumnsConfigurationPanel: React.FunctionComponent<
|
|||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logColumnsSectionTitle"
|
||||
defaultMessage="Columns"
|
||||
defaultMessage="Log Columns"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
|
@ -245,6 +245,7 @@ const RemoveLogColumnButton: React.FunctionComponent<{
|
|||
iconType="trash"
|
||||
onClick={onClick}
|
||||
title={removeColumnLabel}
|
||||
aria-label={removeColumnLabel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import {
|
||||
EuiDescribedFormGroup,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -31,22 +38,36 @@ export const NameConfigurationPanel = ({
|
|||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow
|
||||
error={nameFieldProps.error}
|
||||
fullWidth
|
||||
isInvalid={nameFieldProps.isInvalid}
|
||||
label={
|
||||
<EuiDescribedFormGroup
|
||||
idAria="name"
|
||||
title={
|
||||
<FormattedMessage id="xpack.infra.sourceConfiguration.nameLabel" defaultMessage="Name" />
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.nameDescription"
|
||||
defaultMessage="A descriptive name for the source configuration"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="nameInput"
|
||||
<EuiFormRow
|
||||
describedByIds={['name']}
|
||||
error={nameFieldProps.error}
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...nameFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
isInvalid={nameFieldProps.isInvalid}
|
||||
label={
|
||||
<FormattedMessage id="xpack.infra.sourceConfiguration.nameLabel" defaultMessage="Name" />
|
||||
}
|
||||
>
|
||||
<EuiFieldText
|
||||
data-test-subj="nameInput"
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
readOnly={readOnly}
|
||||
isLoading={isLoading}
|
||||
{...nameFieldProps}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</EuiForm>
|
||||
);
|
||||
|
|
|
@ -1,31 +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 { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { SourceConfigurationFlyoutState } from './source_configuration_flyout_state';
|
||||
|
||||
export const SourceConfigurationButton: React.FunctionComponent = () => {
|
||||
const { toggleIsVisible } = useContext(SourceConfigurationFlyoutState.Context);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
aria-label="Configure source"
|
||||
color="text"
|
||||
data-test-subj="configureSourceButton"
|
||||
iconType="gear"
|
||||
onClick={toggleIsVisible}
|
||||
size="xs"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.sourceConfigurationButtonLabel"
|
||||
defaultMessage="Configuration"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
};
|
|
@ -1,281 +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 {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlyoutHeader,
|
||||
EuiTabbedContent,
|
||||
EuiTabbedContentTab,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, injectI18n, InjectedIntl } from '@kbn/i18n/react';
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
|
||||
import { Source } from '../../containers/source';
|
||||
import { FieldsConfigurationPanel } from './fields_configuration_panel';
|
||||
import { IndicesConfigurationPanel } from './indices_configuration_panel';
|
||||
import { NameConfigurationPanel } from './name_configuration_panel';
|
||||
import { LogColumnsConfigurationPanel } from './log_columns_configuration_panel';
|
||||
import { isValidTabId, SourceConfigurationFlyoutState } from './source_configuration_flyout_state';
|
||||
import { useSourceConfigurationFormState } from './source_configuration_form_state';
|
||||
|
||||
const noop = () => undefined;
|
||||
|
||||
interface SourceConfigurationFlyoutProps {
|
||||
intl: InjectedIntl;
|
||||
shouldAllowEdit: boolean;
|
||||
}
|
||||
|
||||
export const SourceConfigurationFlyout = injectI18n(
|
||||
({ intl, shouldAllowEdit }: SourceConfigurationFlyoutProps) => {
|
||||
const { activeTabId, hide, isVisible, setActiveTab } = useContext(
|
||||
SourceConfigurationFlyoutState.Context
|
||||
);
|
||||
|
||||
const {
|
||||
createSourceConfiguration,
|
||||
source,
|
||||
sourceExists,
|
||||
isLoading,
|
||||
updateSourceConfiguration,
|
||||
} = useContext(Source.Context);
|
||||
const availableFields = useMemo(
|
||||
() => (source && source.status ? source.status.indexFields.map(field => field.name) : []),
|
||||
[source]
|
||||
);
|
||||
|
||||
const {
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
indicesConfigurationProps,
|
||||
logColumnConfigurationProps,
|
||||
errors,
|
||||
resetForm,
|
||||
isFormDirty,
|
||||
isFormValid,
|
||||
formState,
|
||||
formStateChanges,
|
||||
} = useSourceConfigurationFormState(source && source.configuration);
|
||||
|
||||
const persistUpdates = useCallback(async () => {
|
||||
if (sourceExists) {
|
||||
await updateSourceConfiguration(formStateChanges);
|
||||
} else {
|
||||
await createSourceConfiguration(formState);
|
||||
}
|
||||
resetForm();
|
||||
}, [
|
||||
sourceExists,
|
||||
updateSourceConfiguration,
|
||||
createSourceConfiguration,
|
||||
resetForm,
|
||||
formState,
|
||||
formStateChanges,
|
||||
]);
|
||||
|
||||
const isWriteable = useMemo(() => shouldAllowEdit && source && source.origin !== 'internal', [
|
||||
shouldAllowEdit,
|
||||
source,
|
||||
]);
|
||||
|
||||
const tabs: EuiTabbedContentTab[] = useMemo(
|
||||
() =>
|
||||
isVisible
|
||||
? [
|
||||
{
|
||||
id: 'indicesAndFieldsTab',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.infra.sourceConfiguration.sourceConfigurationIndicesTabTitle',
|
||||
defaultMessage: 'Indices and fields',
|
||||
}),
|
||||
content: (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<NameConfigurationPanel
|
||||
isLoading={isLoading}
|
||||
nameFieldProps={indicesConfigurationProps.name}
|
||||
readOnly={!isWriteable}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<IndicesConfigurationPanel
|
||||
isLoading={isLoading}
|
||||
logAliasFieldProps={indicesConfigurationProps.logAlias}
|
||||
metricAliasFieldProps={indicesConfigurationProps.metricAlias}
|
||||
readOnly={!isWriteable}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<FieldsConfigurationPanel
|
||||
containerFieldProps={indicesConfigurationProps.containerField}
|
||||
hostFieldProps={indicesConfigurationProps.hostField}
|
||||
isLoading={isLoading}
|
||||
podFieldProps={indicesConfigurationProps.podField}
|
||||
readOnly={!isWriteable}
|
||||
tiebreakerFieldProps={indicesConfigurationProps.tiebreakerField}
|
||||
timestampFieldProps={indicesConfigurationProps.timestampField}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'logsTab',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.infra.sourceConfiguration.sourceConfigurationLogColumnsTabTitle',
|
||||
defaultMessage: 'Log Columns',
|
||||
}),
|
||||
content: (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<LogColumnsConfigurationPanel
|
||||
addLogColumn={addLogColumn}
|
||||
moveLogColumn={moveLogColumn}
|
||||
availableFields={availableFields}
|
||||
isLoading={isLoading}
|
||||
logColumnConfiguration={logColumnConfigurationProps}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]
|
||||
: [],
|
||||
[
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
availableFields,
|
||||
indicesConfigurationProps,
|
||||
intl.formatMessage,
|
||||
isLoading,
|
||||
isVisible,
|
||||
logColumnConfigurationProps,
|
||||
isWriteable,
|
||||
]
|
||||
);
|
||||
const activeTab = useMemo(() => tabs.filter(tab => tab.id === activeTabId)[0] || tabs[0], [
|
||||
activeTabId,
|
||||
tabs,
|
||||
]);
|
||||
const activateTab = useCallback(
|
||||
(tab: EuiTabbedContentTab) => {
|
||||
const tabId = tab.id;
|
||||
if (isValidTabId(tabId)) {
|
||||
setActiveTab(tabId);
|
||||
}
|
||||
},
|
||||
[setActiveTab, isValidTabId]
|
||||
);
|
||||
|
||||
if (!isVisible || !source || !source.configuration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlyout
|
||||
aria-labelledby="sourceConfigurationTitle"
|
||||
data-test-subj="sourceConfigurationFlyout"
|
||||
hideCloseButton
|
||||
onClose={noop}
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle>
|
||||
<h2 id="sourceConfigurationTitle">
|
||||
{isWriteable ? (
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.sourceConfigurationTitle"
|
||||
defaultMessage="Configure source"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.sourceConfigurationReadonlyTitle"
|
||||
defaultMessage="View source configuration"
|
||||
/>
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTabbedContent onTabClick={activateTab} selectedTab={activeTab} tabs={tabs} />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
{errors.length > 0 ? (
|
||||
<>
|
||||
<EuiCallOut color="danger">
|
||||
<ul>
|
||||
{errors.map((error, errorIndex) => (
|
||||
<li key={errorIndex}>{error}</li>
|
||||
))}
|
||||
</ul>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null}
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
{!isFormDirty ? (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="closeFlyoutButton"
|
||||
iconType="cross"
|
||||
isDisabled={isLoading}
|
||||
onClick={() => hide()}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="discardAndCloseFlyoutButton"
|
||||
color="danger"
|
||||
iconType="cross"
|
||||
isDisabled={isLoading}
|
||||
onClick={() => {
|
||||
resetForm();
|
||||
hide();
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.discardAndCloseButtonLabel"
|
||||
defaultMessage="Discard and Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem />
|
||||
{isWriteable && (
|
||||
<EuiFlexItem grow={false}>
|
||||
{isLoading ? (
|
||||
<EuiButton color="primary" isLoading fill>
|
||||
Loading
|
||||
</EuiButton>
|
||||
) : (
|
||||
<EuiButton
|
||||
data-test-subj="updateSourceConfigurationButton"
|
||||
color="primary"
|
||||
isDisabled={!isFormDirty || !isFormValid}
|
||||
fill
|
||||
onClick={persistUpdates}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.updateSourceConfigurationButtonLabel"
|
||||
defaultMessage="Update Source"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,51 +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 createContainer from 'constate-latest';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { useVisibilityState } from '../../utils/use_visibility_state';
|
||||
|
||||
type TabId = 'indicesAndFieldsTab' | 'logsTab';
|
||||
const validTabIds: TabId[] = ['indicesAndFieldsTab', 'logsTab'];
|
||||
|
||||
export const useSourceConfigurationFlyoutState = ({
|
||||
initialVisibility = false,
|
||||
initialTab = 'indicesAndFieldsTab',
|
||||
}: {
|
||||
initialVisibility?: boolean;
|
||||
initialTab?: TabId;
|
||||
} = {}) => {
|
||||
const { isVisible, show, hide, toggle: toggleIsVisible } = useVisibilityState(initialVisibility);
|
||||
const [activeTabId, setActiveTab] = useState(initialTab);
|
||||
|
||||
const showWithTab = useCallback(
|
||||
(tabId?: TabId) => {
|
||||
if (tabId != null) {
|
||||
setActiveTab(tabId);
|
||||
}
|
||||
show();
|
||||
},
|
||||
[show]
|
||||
);
|
||||
const showIndicesConfiguration = useCallback(() => showWithTab('indicesAndFieldsTab'), [show]);
|
||||
const showLogsConfiguration = useCallback(() => showWithTab('logsTab'), [show]);
|
||||
|
||||
return {
|
||||
activeTabId,
|
||||
hide,
|
||||
isVisible,
|
||||
setActiveTab,
|
||||
show: showWithTab,
|
||||
showIndicesConfiguration,
|
||||
showLogsConfiguration,
|
||||
toggleIsVisible,
|
||||
};
|
||||
};
|
||||
|
||||
export const isValidTabId = (value: any): value is TabId => validTabIds.includes(value);
|
||||
|
||||
export const SourceConfigurationFlyoutState = createContainer(useSourceConfigurationFlyoutState);
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiButton,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, injectI18n, InjectedIntl } from '@kbn/i18n/react';
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import { Prompt } from 'react-router-dom';
|
||||
|
||||
import { Source } from '../../containers/source';
|
||||
import { FieldsConfigurationPanel } from './fields_configuration_panel';
|
||||
import { IndicesConfigurationPanel } from './indices_configuration_panel';
|
||||
import { NameConfigurationPanel } from './name_configuration_panel';
|
||||
import { LogColumnsConfigurationPanel } from './log_columns_configuration_panel';
|
||||
import { useSourceConfigurationFormState } from './source_configuration_form_state';
|
||||
|
||||
interface SourceConfigurationSettingsProps {
|
||||
intl: InjectedIntl;
|
||||
shouldAllowEdit: boolean;
|
||||
}
|
||||
|
||||
export const SourceConfigurationSettings = injectI18n(
|
||||
({ shouldAllowEdit }: SourceConfigurationSettingsProps) => {
|
||||
const {
|
||||
createSourceConfiguration,
|
||||
source,
|
||||
sourceExists,
|
||||
isLoading,
|
||||
updateSourceConfiguration,
|
||||
} = useContext(Source.Context);
|
||||
|
||||
const availableFields = useMemo(
|
||||
() => (source && source.status ? source.status.indexFields.map(field => field.name) : []),
|
||||
[source]
|
||||
);
|
||||
|
||||
const {
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
indicesConfigurationProps,
|
||||
logColumnConfigurationProps,
|
||||
errors,
|
||||
resetForm,
|
||||
isFormDirty,
|
||||
isFormValid,
|
||||
formState,
|
||||
formStateChanges,
|
||||
} = useSourceConfigurationFormState(source && source.configuration);
|
||||
|
||||
const persistUpdates = useCallback(async () => {
|
||||
if (sourceExists) {
|
||||
await updateSourceConfiguration(formStateChanges);
|
||||
} else {
|
||||
await createSourceConfiguration(formState);
|
||||
}
|
||||
resetForm();
|
||||
}, [
|
||||
sourceExists,
|
||||
updateSourceConfiguration,
|
||||
createSourceConfiguration,
|
||||
resetForm,
|
||||
formState,
|
||||
formStateChanges,
|
||||
]);
|
||||
|
||||
const isWriteable = useMemo(() => shouldAllowEdit && source && source.origin !== 'internal', [
|
||||
shouldAllowEdit,
|
||||
source,
|
||||
]);
|
||||
|
||||
if (!source || !source.configuration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent
|
||||
verticalPosition="center"
|
||||
horizontalPosition="center"
|
||||
data-test-subj="sourceConfigurationContent"
|
||||
>
|
||||
<EuiPageContentBody>
|
||||
<Prompt
|
||||
when={isFormDirty}
|
||||
message={i18n.translate('xpack.infra.sourceConfiguration.unsavedFormPrompt', {
|
||||
defaultMessage: 'Are you sure you want to leave? Changes will be lost',
|
||||
})}
|
||||
/>
|
||||
<EuiPanel paddingSize="l">
|
||||
<NameConfigurationPanel
|
||||
isLoading={isLoading}
|
||||
nameFieldProps={indicesConfigurationProps.name}
|
||||
readOnly={!isWriteable}
|
||||
/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
<EuiPanel paddingSize="l">
|
||||
<IndicesConfigurationPanel
|
||||
isLoading={isLoading}
|
||||
logAliasFieldProps={indicesConfigurationProps.logAlias}
|
||||
metricAliasFieldProps={indicesConfigurationProps.metricAlias}
|
||||
readOnly={!isWriteable}
|
||||
/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
<EuiPanel paddingSize="l">
|
||||
<FieldsConfigurationPanel
|
||||
containerFieldProps={indicesConfigurationProps.containerField}
|
||||
hostFieldProps={indicesConfigurationProps.hostField}
|
||||
isLoading={isLoading}
|
||||
podFieldProps={indicesConfigurationProps.podField}
|
||||
readOnly={!isWriteable}
|
||||
tiebreakerFieldProps={indicesConfigurationProps.tiebreakerField}
|
||||
timestampFieldProps={indicesConfigurationProps.timestampField}
|
||||
/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
<EuiPanel paddingSize="l">
|
||||
<LogColumnsConfigurationPanel
|
||||
addLogColumn={addLogColumn}
|
||||
moveLogColumn={moveLogColumn}
|
||||
availableFields={availableFields}
|
||||
isLoading={isLoading}
|
||||
logColumnConfiguration={logColumnConfigurationProps}
|
||||
/>
|
||||
</EuiPanel>
|
||||
{errors.length > 0 ? (
|
||||
<>
|
||||
<EuiCallOut color="danger">
|
||||
<ul>
|
||||
{errors.map((error, errorIndex) => (
|
||||
<li key={errorIndex}>{error}</li>
|
||||
))}
|
||||
</ul>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null}
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup>
|
||||
{isWriteable && (
|
||||
<EuiFlexItem>
|
||||
{isLoading ? (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton color="primary" isLoading fill>
|
||||
Loading
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="discardSettingsButton"
|
||||
color="danger"
|
||||
iconType="cross"
|
||||
isDisabled={isLoading || !isFormDirty}
|
||||
onClick={() => {
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.discardSettingsButtonLabel"
|
||||
defaultMessage="Discard"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="applySettingsButton"
|
||||
color="primary"
|
||||
isDisabled={!isFormDirty || !isFormValid}
|
||||
fill
|
||||
onClick={persistUpdates}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.applySettingsButtonLabel"
|
||||
defaultMessage="Apply"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 { EuiButton } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
||||
export enum ViewSourceConfigurationButtonHrefBase {
|
||||
infrastructure = 'infrastructure',
|
||||
logs = 'logs',
|
||||
}
|
||||
|
||||
interface ViewSourceConfigurationButtonProps {
|
||||
'data-test-subj'?: string;
|
||||
hrefBase: ViewSourceConfigurationButtonHrefBase;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ViewSourceConfigurationButton = ({
|
||||
'data-test-subj': dataTestSubj,
|
||||
hrefBase,
|
||||
children,
|
||||
}: ViewSourceConfigurationButtonProps) => {
|
||||
const href = `/${hrefBase}/settings`;
|
||||
|
||||
return (
|
||||
<Route
|
||||
key={href}
|
||||
path={href}
|
||||
children={({ match, history }) => (
|
||||
<EuiButton data-test-subj={dataTestSubj} color="primary" onClick={() => history.push(href)}>
|
||||
{children}
|
||||
</EuiButton>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -15,10 +15,11 @@ import { ColumnarPage } from '../../components/page';
|
|||
import { MetricsExplorerOptionsContainer } from '../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state';
|
||||
import { WithSource } from '../../containers/with_source';
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { Source } from '../../containers/source';
|
||||
import { MetricsExplorerPage } from './metrics_explorer';
|
||||
import { SnapshotPage } from './snapshot';
|
||||
import { SettingsPage } from '../shared/settings';
|
||||
import { AppNavigation } from '../../components/navigation/app_navigation';
|
||||
|
||||
interface InfrastructurePageProps extends RouteComponentProps {
|
||||
intl: InjectedIntl;
|
||||
|
@ -26,23 +27,23 @@ interface InfrastructurePageProps extends RouteComponentProps {
|
|||
|
||||
export const InfrastructurePage = injectI18n(({ match, intl }: InfrastructurePageProps) => (
|
||||
<Source.Provider sourceId="default">
|
||||
<SourceConfigurationFlyoutState.Provider>
|
||||
<ColumnarPage>
|
||||
<DocumentTitle
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.infra.homePage.documentTitle',
|
||||
defaultMessage: 'Infrastructure',
|
||||
})}
|
||||
/>
|
||||
<ColumnarPage>
|
||||
<DocumentTitle
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.infra.homePage.documentTitle',
|
||||
defaultMessage: 'Infrastructure',
|
||||
})}
|
||||
/>
|
||||
|
||||
<HelpCenterContent
|
||||
feedbackLink="https://discuss.elastic.co/c/infrastructure"
|
||||
feedbackLinkText={intl.formatMessage({
|
||||
id: 'xpack.infra.infrastructure.infrastructureHelpContent.feedbackLinkText',
|
||||
defaultMessage: 'Provide feedback for Infrastructure',
|
||||
})}
|
||||
/>
|
||||
<HelpCenterContent
|
||||
feedbackLink="https://discuss.elastic.co/c/infrastructure"
|
||||
feedbackLinkText={intl.formatMessage({
|
||||
id: 'xpack.infra.infrastructure.infrastructureHelpContent.feedbackLinkText',
|
||||
defaultMessage: 'Provide feedback for Infrastructure',
|
||||
})}
|
||||
/>
|
||||
|
||||
<AppNavigation>
|
||||
<RoutedTabs
|
||||
tabs={[
|
||||
{
|
||||
|
@ -59,30 +60,38 @@ export const InfrastructurePage = injectI18n(({ match, intl }: InfrastructurePag
|
|||
}),
|
||||
path: `${match.path}/metrics-explorer`,
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'xpack.infra.homePage.settingsTabTitle',
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
path: `${match.path}/settings`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</AppNavigation>
|
||||
|
||||
<Switch>
|
||||
<Route path={`${match.path}/inventory`} component={SnapshotPage} />
|
||||
<Route
|
||||
path={`${match.path}/metrics-explorer`}
|
||||
render={props => (
|
||||
<WithSource>
|
||||
{({ configuration, createDerivedIndexPattern }) => (
|
||||
<MetricsExplorerOptionsContainer.Provider>
|
||||
<WithMetricsExplorerOptionsUrlState />
|
||||
<MetricsExplorerPage
|
||||
derivedIndexPattern={createDerivedIndexPattern('metrics')}
|
||||
source={configuration}
|
||||
{...props}
|
||||
/>
|
||||
</MetricsExplorerOptionsContainer.Provider>
|
||||
)}
|
||||
</WithSource>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</ColumnarPage>
|
||||
</SourceConfigurationFlyoutState.Provider>
|
||||
<Switch>
|
||||
<Route path={`${match.path}/inventory`} component={SnapshotPage} />
|
||||
<Route
|
||||
path={`${match.path}/metrics-explorer`}
|
||||
render={props => (
|
||||
<WithSource>
|
||||
{({ configuration, createDerivedIndexPattern }) => (
|
||||
<MetricsExplorerOptionsContainer.Provider>
|
||||
<WithMetricsExplorerOptionsUrlState />
|
||||
<MetricsExplorerPage
|
||||
derivedIndexPattern={createDerivedIndexPattern('metrics')}
|
||||
source={configuration}
|
||||
{...props}
|
||||
/>
|
||||
</MetricsExplorerOptionsContainer.Provider>
|
||||
)}
|
||||
</WithSource>
|
||||
)}
|
||||
/>
|
||||
<Route path={`${match.path}/settings`} component={SettingsPage} />
|
||||
</Switch>
|
||||
</ColumnarPage>
|
||||
</Source.Provider>
|
||||
));
|
||||
|
|
|
@ -17,10 +17,12 @@ import { NoIndices } from '../../../components/empty_states/no_indices';
|
|||
import { Header } from '../../../components/header';
|
||||
import { ColumnarPage } from '../../../components/page';
|
||||
|
||||
import { SourceConfigurationFlyout } from '../../../components/source_configuration';
|
||||
import { SourceConfigurationFlyoutState } from '../../../components/source_configuration';
|
||||
import { SourceErrorPage } from '../../../components/source_error_page';
|
||||
import { SourceLoadingPage } from '../../../components/source_loading_page';
|
||||
import {
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from '../../../components/source_configuration';
|
||||
import { Source } from '../../../containers/source';
|
||||
import { WithWaffleFilterUrlState } from '../../../containers/waffle/with_waffle_filters';
|
||||
import { WithWaffleOptionsUrlState } from '../../../containers/waffle/with_waffle_options';
|
||||
|
@ -36,7 +38,6 @@ interface SnapshotPageProps {
|
|||
export const SnapshotPage = injectUICapabilities(
|
||||
injectI18n((props: SnapshotPageProps) => {
|
||||
const { intl, uiCapabilities } = props;
|
||||
const { showIndicesConfiguration } = useContext(SourceConfigurationFlyoutState.Context);
|
||||
const {
|
||||
createDerivedIndexPattern,
|
||||
hasFailedLoadingSource,
|
||||
|
@ -76,9 +77,6 @@ export const SnapshotPage = injectUICapabilities(
|
|||
]}
|
||||
readOnlyBadge={!uiCapabilities.infrastructure.save}
|
||||
/>
|
||||
<SourceConfigurationFlyout
|
||||
shouldAllowEdit={uiCapabilities.infrastructure.configureSource as boolean}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<SourceLoadingPage />
|
||||
) : metricIndicesExist ? (
|
||||
|
@ -120,16 +118,15 @@ export const SnapshotPage = injectUICapabilities(
|
|||
</EuiFlexItem>
|
||||
{uiCapabilities.infrastructure.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
color="primary"
|
||||
onClick={showIndicesConfiguration}
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'xpack.infra.configureSourceActionLabel',
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</EuiButton>
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -10,7 +10,6 @@ import React from 'react';
|
|||
|
||||
import { AutocompleteField } from '../../../components/autocomplete_field';
|
||||
import { Toolbar } from '../../../components/eui/toolbar';
|
||||
import { SourceConfigurationButton } from '../../../components/source_configuration';
|
||||
import { WaffleGroupByControls } from '../../../components/waffle/waffle_group_by_controls';
|
||||
import { WaffleMetricControls } from '../../../components/waffle/waffle_metric_controls';
|
||||
import { WaffleNodeTypeSwitcher } from '../../../components/waffle/waffle_node_type_switcher';
|
||||
|
@ -111,9 +110,6 @@ export const SnapshotToolbar = injectI18n(({ intl }) => (
|
|||
customOptions={customOptions}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SourceConfigurationButton />
|
||||
</EuiFlexItem>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</WithWaffleOptions>
|
||||
|
|
69
x-pack/legacy/plugins/infra/public/pages/logs/index.tsx
Normal file
69
x-pack/legacy/plugins/infra/public/pages/logs/index.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
|
||||
import { DocumentTitle } from '../../components/document_title';
|
||||
import { HelpCenterContent } from '../../components/help_center_content';
|
||||
import { RoutedTabs } from '../../components/navigation/routed_tabs';
|
||||
import { ColumnarPage } from '../../components/page';
|
||||
import { Source } from '../../containers/source';
|
||||
import { StreamPage } from './stream';
|
||||
import { SettingsPage } from '../shared/settings';
|
||||
import { AppNavigation } from '../../components/navigation/app_navigation';
|
||||
|
||||
interface LogsPageProps extends RouteComponentProps {
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
export const LogsPage = injectI18n(({ match, intl }: LogsPageProps) => (
|
||||
<Source.Provider sourceId="default">
|
||||
<ColumnarPage>
|
||||
<DocumentTitle
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.infra.logs.index.documentTitle',
|
||||
defaultMessage: 'Logs',
|
||||
})}
|
||||
/>
|
||||
|
||||
<HelpCenterContent
|
||||
feedbackLink="https://discuss.elastic.co/c/logs"
|
||||
feedbackLinkText={intl.formatMessage({
|
||||
id: 'xpack.infra.logsPage.logsHelpContent.feedbackLinkText',
|
||||
defaultMessage: 'Provide feedback for Logs',
|
||||
})}
|
||||
/>
|
||||
|
||||
<AppNavigation>
|
||||
<RoutedTabs
|
||||
tabs={[
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'xpack.infra.logs.index.streamTabTitle',
|
||||
defaultMessage: 'Stream',
|
||||
}),
|
||||
path: `${match.path}/stream`,
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'xpack.infra.logs.index.settingsTabTitle',
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
path: `${match.path}/settings`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</AppNavigation>
|
||||
|
||||
<Switch>
|
||||
<Route path={`${match.path}/stream`} component={StreamPage} />
|
||||
<Route path={`${match.path}/settings`} component={SettingsPage} />
|
||||
</Switch>
|
||||
</ColumnarPage>
|
||||
</Source.Provider>
|
||||
));
|
|
@ -1,57 +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 { injectI18n, InjectedIntl } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { DocumentTitle } from '../../components/document_title';
|
||||
import { Header } from '../../components/header';
|
||||
import { HelpCenterContent } from '../../components/help_center_content';
|
||||
import { SourceConfigurationFlyout } from '../../components/source_configuration';
|
||||
|
||||
interface LogsPageHeaderProps {
|
||||
intl: InjectedIntl;
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const LogsPageHeader = injectUICapabilities(
|
||||
injectI18n((props: LogsPageHeaderProps) => {
|
||||
const { intl, uiCapabilities } = props;
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
breadcrumbs={[
|
||||
{
|
||||
text: intl.formatMessage({
|
||||
id: 'xpack.infra.logsPage.logsBreadcrumbsText',
|
||||
defaultMessage: 'Logs',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
readOnlyBadge={!uiCapabilities.logs.save}
|
||||
/>
|
||||
<DocumentTitle
|
||||
title={intl.formatMessage({
|
||||
id: 'xpack.infra.logsPage.documentTitle',
|
||||
defaultMessage: 'Logs',
|
||||
})}
|
||||
/>
|
||||
<HelpCenterContent
|
||||
feedbackLink="https://discuss.elastic.co/c/logs"
|
||||
feedbackLinkText={intl.formatMessage({
|
||||
id: 'xpack.infra.logsPage.logsHelpContent.feedbackLinkText',
|
||||
defaultMessage: 'Provide feedback for Logs',
|
||||
})}
|
||||
/>
|
||||
<SourceConfigurationFlyout
|
||||
shouldAllowEdit={uiCapabilities.logs.configureSource as boolean}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
})
|
||||
);
|
|
@ -1,33 +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 { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { LogFlyout } from '../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { LogHighlightsState } from '../../containers/logs/log_highlights/log_highlights';
|
||||
import { Source, useSource } from '../../containers/source';
|
||||
import { useSourceId } from '../../containers/source_id';
|
||||
|
||||
export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
|
||||
const [sourceId] = useSourceId();
|
||||
const source = useSource({ sourceId });
|
||||
|
||||
return (
|
||||
<Source.Context.Provider value={source}>
|
||||
<SourceConfigurationFlyoutState.Provider>
|
||||
<LogViewConfiguration.Provider>
|
||||
<LogFlyout.Provider>
|
||||
<LogHighlightsState.Provider sourceId={sourceId} sourceVersion={source.version}>
|
||||
{children}
|
||||
</LogHighlightsState.Provider>
|
||||
</LogFlyout.Provider>
|
||||
</LogViewConfiguration.Provider>
|
||||
</SourceConfigurationFlyoutState.Provider>
|
||||
</Source.Context.Provider>
|
||||
);
|
||||
};
|
|
@ -4,4 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { LogsPage } from './page';
|
||||
export { StreamPage } from './page';
|
|
@ -6,20 +6,20 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { ColumnarPage } from '../../components/page';
|
||||
import { LogsPageContent } from './page_content';
|
||||
import { LogsPageHeader } from './page_header';
|
||||
import { ColumnarPage } from '../../../components/page';
|
||||
import { StreamPageContent } from './page_content';
|
||||
import { StreamPageHeader } from './page_header';
|
||||
import { LogsPageProviders } from './page_providers';
|
||||
import { useTrackPageview } from '../../hooks/use_track_metric';
|
||||
import { useTrackPageview } from '../../../hooks/use_track_metric';
|
||||
|
||||
export const LogsPage = () => {
|
||||
export const StreamPage = () => {
|
||||
useTrackPageview({ app: 'infra_logs', path: 'stream' });
|
||||
useTrackPageview({ app: 'infra_logs', path: 'stream', delay: 15000 });
|
||||
return (
|
||||
<LogsPageProviders>
|
||||
<ColumnarPage data-test-subj="infraLogsPage">
|
||||
<LogsPageHeader />
|
||||
<LogsPageContent />
|
||||
<StreamPageHeader />
|
||||
<StreamPageContent />
|
||||
</ColumnarPage>
|
||||
</LogsPageProviders>
|
||||
);
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { SourceErrorPage } from '../../components/source_error_page';
|
||||
import { SourceLoadingPage } from '../../components/source_loading_page';
|
||||
import { Source } from '../../containers/source';
|
||||
import { SourceErrorPage } from '../../../components/source_error_page';
|
||||
import { SourceLoadingPage } from '../../../components/source_loading_page';
|
||||
import { Source } from '../../../containers/source';
|
||||
import { LogsPageLogsContent } from './page_logs_content';
|
||||
import { LogsPageNoIndicesContent } from './page_no_indices_content';
|
||||
|
||||
export const LogsPageContent: React.FunctionComponent = () => {
|
||||
export const StreamPageContent: React.FunctionComponent = () => {
|
||||
const {
|
||||
hasFailedLoadingSource,
|
||||
isLoadingSource,
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { DocumentTitle } from '../../../components/document_title';
|
||||
import { Header } from '../../../components/header';
|
||||
|
||||
interface StreamPageHeaderProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const StreamPageHeader = injectUICapabilities((props: StreamPageHeaderProps) => {
|
||||
const { uiCapabilities } = props;
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
breadcrumbs={[
|
||||
{
|
||||
text: i18n.translate('xpack.infra.logs.streamPage.logsBreadcrumbsText', {
|
||||
defaultMessage: 'Logs',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
readOnlyBadge={!uiCapabilities.logs.save}
|
||||
/>
|
||||
<DocumentTitle
|
||||
title={(previousTitle: string) =>
|
||||
i18n.translate('xpack.infra.logs.streamPage.documentTitle', {
|
||||
defaultMessage: '{previousTitle} | Stream',
|
||||
values: {
|
||||
previousTitle,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -6,30 +6,29 @@
|
|||
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
import { AutoSizer } from '../../components/auto_sizer';
|
||||
import { LogEntryFlyout } from '../../components/logging/log_entry_flyout';
|
||||
import { LogMinimap } from '../../components/logging/log_minimap';
|
||||
import { ScrollableLogTextStreamView } from '../../components/logging/log_text_stream';
|
||||
import { PageContent } from '../../components/page';
|
||||
import euiStyled from '../../../../../../common/eui_styled_components';
|
||||
import { AutoSizer } from '../../../components/auto_sizer';
|
||||
import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout';
|
||||
import { LogMinimap } from '../../../components/logging/log_minimap';
|
||||
import { ScrollableLogTextStreamView } from '../../../components/logging/log_text_stream';
|
||||
import { PageContent } from '../../../components/page';
|
||||
|
||||
import { WithSummary } from '../../containers/logs/log_summary';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter, WithLogFilterUrlState } from '../../containers/logs/with_log_filter';
|
||||
import { WithSummary } from '../../../containers/logs/log_summary';
|
||||
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter, WithLogFilterUrlState } from '../../../containers/logs/with_log_filter';
|
||||
import {
|
||||
LogFlyout as LogFlyoutState,
|
||||
WithFlyoutOptionsUrlState,
|
||||
} from '../../containers/logs/log_flyout';
|
||||
import { WithLogMinimapUrlState } from '../../containers/logs/with_log_minimap';
|
||||
import { WithLogPositionUrlState } from '../../containers/logs/with_log_position';
|
||||
import { WithLogPosition } from '../../containers/logs/with_log_position';
|
||||
import { WithLogTextviewUrlState } from '../../containers/logs/with_log_textview';
|
||||
import { ReduxSourceIdBridge, WithStreamItems } from '../../containers/logs/with_stream_items';
|
||||
import { Source } from '../../containers/source';
|
||||
} from '../../../containers/logs/log_flyout';
|
||||
import { WithLogMinimapUrlState } from '../../../containers/logs/with_log_minimap';
|
||||
import { WithLogPositionUrlState } from '../../../containers/logs/with_log_position';
|
||||
import { WithLogPosition } from '../../../containers/logs/with_log_position';
|
||||
import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview';
|
||||
import { ReduxSourceIdBridge, WithStreamItems } from '../../../containers/logs/with_stream_items';
|
||||
import { Source } from '../../../containers/source';
|
||||
|
||||
import { LogsToolbar } from './page_toolbar';
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { LogHighlightsBridge } from '../../containers/logs/log_highlights';
|
||||
import { LogHighlightsBridge } from '../../../containers/logs/log_highlights';
|
||||
|
||||
export const LogsPageLogsContent: React.FunctionComponent = () => {
|
||||
const { createDerivedIndexPattern, source, sourceId, version } = useContext(Source.Context);
|
||||
|
@ -43,7 +42,6 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
flyoutItem,
|
||||
isLoading,
|
||||
} = useContext(LogFlyoutState.Context);
|
||||
const { showLogsConfiguration } = useContext(SourceConfigurationFlyoutState.Context);
|
||||
|
||||
const derivedIndexPattern = createDerivedIndexPattern('logs');
|
||||
|
||||
|
@ -105,7 +103,6 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
loadNewerItems={loadNewerEntries}
|
||||
reportVisibleInterval={reportVisiblePositions}
|
||||
scale={textScale}
|
||||
showColumnConfiguration={showLogsConfiguration}
|
||||
target={targetPosition}
|
||||
wrap={textWrap}
|
||||
setFlyoutItem={setFlyoutId}
|
|
@ -6,13 +6,16 @@
|
|||
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { injectI18n, InjectedIntl } from '@kbn/i18n/react';
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { NoIndices } from '../../components/empty_states/no_indices';
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { WithKibanaChrome } from '../../containers/with_kibana_chrome';
|
||||
import { NoIndices } from '../../../components/empty_states/no_indices';
|
||||
import { WithKibanaChrome } from '../../../containers/with_kibana_chrome';
|
||||
import {
|
||||
ViewSourceConfigurationButton,
|
||||
ViewSourceConfigurationButtonHrefBase,
|
||||
} from '../../../components/source_configuration';
|
||||
|
||||
interface LogsPageNoIndicesContentProps {
|
||||
intl: InjectedIntl;
|
||||
|
@ -22,7 +25,6 @@ interface LogsPageNoIndicesContentProps {
|
|||
export const LogsPageNoIndicesContent = injectUICapabilities(
|
||||
injectI18n((props: LogsPageNoIndicesContentProps) => {
|
||||
const { intl, uiCapabilities } = props;
|
||||
const { showIndicesConfiguration } = useContext(SourceConfigurationFlyoutState.Context);
|
||||
|
||||
return (
|
||||
<WithKibanaChrome>
|
||||
|
@ -54,16 +56,15 @@ export const LogsPageNoIndicesContent = injectUICapabilities(
|
|||
</EuiFlexItem>
|
||||
{uiCapabilities.logs.configureSource ? (
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
<ViewSourceConfigurationButton
|
||||
data-test-subj="configureSourceButton"
|
||||
color="primary"
|
||||
onClick={showIndicesConfiguration}
|
||||
hrefBase={ViewSourceConfigurationButtonHrefBase.logs}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'xpack.infra.configureSourceActionLabel',
|
||||
defaultMessage: 'Change source configuration',
|
||||
})}
|
||||
</EuiButton>
|
||||
</ViewSourceConfigurationButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { LogFlyout } from '../../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
|
||||
import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights';
|
||||
import { Source, useSource } from '../../../containers/source';
|
||||
import { useSourceId } from '../../../containers/source_id';
|
||||
|
||||
export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
|
||||
const [sourceId] = useSourceId();
|
||||
const source = useSource({ sourceId });
|
||||
|
||||
return (
|
||||
<Source.Context.Provider value={source}>
|
||||
<LogViewConfiguration.Provider>
|
||||
<LogFlyout.Provider>
|
||||
<LogHighlightsState.Provider sourceId={sourceId} sourceVersion={source.version}>
|
||||
{children}
|
||||
</LogHighlightsState.Provider>
|
||||
</LogFlyout.Provider>
|
||||
</LogViewConfiguration.Provider>
|
||||
</Source.Context.Provider>
|
||||
);
|
||||
};
|
|
@ -8,22 +8,21 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import { injectI18n } from '@kbn/i18n/react';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { AutocompleteField } from '../../components/autocomplete_field';
|
||||
import { Toolbar } from '../../components/eui';
|
||||
import { LogCustomizationMenu } from '../../components/logging/log_customization_menu';
|
||||
import { LogHighlightsMenu } from '../../components/logging/log_highlights_menu';
|
||||
import { LogHighlightsState } from '../../containers/logs/log_highlights/log_highlights';
|
||||
import { LogMinimapScaleControls } from '../../components/logging/log_minimap_scale_controls';
|
||||
import { LogTextScaleControls } from '../../components/logging/log_text_scale_controls';
|
||||
import { LogTextWrapControls } from '../../components/logging/log_text_wrap_controls';
|
||||
import { LogTimeControls } from '../../components/logging/log_time_controls';
|
||||
import { SourceConfigurationButton } from '../../components/source_configuration';
|
||||
import { LogFlyout } from '../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter } from '../../containers/logs/with_log_filter';
|
||||
import { WithLogPosition } from '../../containers/logs/with_log_position';
|
||||
import { Source } from '../../containers/source';
|
||||
import { WithKueryAutocompletion } from '../../containers/with_kuery_autocompletion';
|
||||
import { AutocompleteField } from '../../../components/autocomplete_field';
|
||||
import { Toolbar } from '../../../components/eui';
|
||||
import { LogCustomizationMenu } from '../../../components/logging/log_customization_menu';
|
||||
import { LogHighlightsMenu } from '../../../components/logging/log_highlights_menu';
|
||||
import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights';
|
||||
import { LogMinimapScaleControls } from '../../../components/logging/log_minimap_scale_controls';
|
||||
import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls';
|
||||
import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls';
|
||||
import { LogTimeControls } from '../../../components/logging/log_time_controls';
|
||||
import { LogFlyout } from '../../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter } from '../../../containers/logs/with_log_filter';
|
||||
import { WithLogPosition } from '../../../containers/logs/with_log_position';
|
||||
import { Source } from '../../../containers/source';
|
||||
import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion';
|
||||
|
||||
export const LogsToolbar = injectI18n(({ intl }) => {
|
||||
const { createDerivedIndexPattern } = useContext(Source.Context);
|
||||
|
@ -91,9 +90,6 @@ export const LogsToolbar = injectI18n(({ intl }) => {
|
|||
)}
|
||||
</WithKueryAutocompletion>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SourceConfigurationButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LogCustomizationMenu>
|
||||
<LogMinimapScaleControls
|
|
@ -28,7 +28,6 @@ import { InvalidNodeError } from '../../components/metrics/invalid_node';
|
|||
import { MetricsSideNav } from '../../components/metrics/side_nav';
|
||||
import { MetricsTimeControls } from '../../components/metrics/time_controls';
|
||||
import { ColumnarPage, PageContent } from '../../components/page';
|
||||
import { SourceConfigurationFlyout } from '../../components/source_configuration';
|
||||
import { WithMetrics } from '../../containers/metrics/with_metrics';
|
||||
import {
|
||||
WithMetricsTime,
|
||||
|
@ -130,9 +129,6 @@ export const MetricDetail = withMetricPageProviders(
|
|||
breadcrumbs={breadcrumbs}
|
||||
readOnlyBadge={!uiCapabilities.infrastructure.save}
|
||||
/>
|
||||
<SourceConfigurationFlyout
|
||||
shouldAllowEdit={uiCapabilities.infrastructure.configureSource as boolean}
|
||||
/>
|
||||
<WithMetricsTimeUrlState />
|
||||
<DocumentTitle
|
||||
title={intl.formatMessage(
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { MetricsTimeContainer } from '../../containers/metrics/with_metrics_time';
|
||||
import { Source } from '../../containers/source';
|
||||
|
||||
|
@ -14,10 +13,8 @@ export const withMetricPageProviders = <T extends object>(Component: React.Compo
|
|||
props: T
|
||||
) => (
|
||||
<Source.Provider sourceId="default">
|
||||
<SourceConfigurationFlyoutState.Provider>
|
||||
<MetricsTimeContainer.Provider>
|
||||
<Component {...props} />
|
||||
</MetricsTimeContainer.Provider>
|
||||
</SourceConfigurationFlyoutState.Provider>
|
||||
<MetricsTimeContainer.Provider>
|
||||
<Component {...props} />
|
||||
</MetricsTimeContainer.Provider>
|
||||
</Source.Provider>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { UICapabilities } from 'ui/capabilities';
|
||||
import { injectUICapabilities } from 'ui/capabilities/react';
|
||||
import { SourceConfigurationSettings } from '../../../components/source_configuration/source_configuration_settings';
|
||||
|
||||
interface SettingsPageProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
}
|
||||
|
||||
export const SettingsPage = injectUICapabilities(({ uiCapabilities }: SettingsPageProps) => (
|
||||
<SourceConfigurationSettings shouldAllowEdit={uiCapabilities.logs.configureSource as boolean} />
|
||||
));
|
|
@ -37,6 +37,7 @@ const PageRouterComponent: React.SFC<RouterProps> = ({ history, uiCapabilities }
|
|||
{uiCapabilities.infrastructure.show && (
|
||||
<Redirect from="/home" exact={true} to="/infrastructure/inventory" />
|
||||
)}
|
||||
{uiCapabilities.logs.show && <Redirect from="/logs" exact={true} to="/logs/stream" />}
|
||||
{uiCapabilities.logs.show && <Route path="/logs" component={LogsPage} />}
|
||||
{uiCapabilities.infrastructure.show && (
|
||||
<Route path="/infrastructure" component={InfrastructurePage} />
|
||||
|
|
|
@ -484,11 +484,6 @@
|
|||
"common.ui.indexPattern.unknownFieldHeader": "不明なフィールドタイプ {type}",
|
||||
"common.ui.indexPattern.warningText": "現在 {index} に一致するすべてのインデックスにクエリを実行しています。{title} はワイルドカードベースのインデックスパターンに移行されるはずです。",
|
||||
"common.ui.indexPattern.warningTitle": "時間間隔インデックスパターンのサポートは廃止されました",
|
||||
"inspector.closeButton": "インスペクターを閉じる",
|
||||
"inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です",
|
||||
"inspector.reqTimestampKey": "リクエストのタイムスタンプ",
|
||||
"inspector.title": "インスペクター",
|
||||
"inspector.view": "{viewName} を表示",
|
||||
"common.ui.legacyBrowserMessage": "この Kibana インストレーションは、現在ご使用のブラウザが満たしていない厳格なセキュリティ要件が有効になっています。",
|
||||
"common.ui.legacyBrowserTitle": "ブラウザをアップグレードしてください",
|
||||
"common.ui.management.breadcrumb": "管理",
|
||||
|
@ -615,6 +610,11 @@
|
|||
"common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "バウンドを取得できませんでした",
|
||||
"common.ui.welcomeErrorMessage": "Kibana が正常に読み込まれませんでした。詳細はサーバーアウトプットを確認してください。",
|
||||
"common.ui.welcomeMessage": "Kibana を読み込み中",
|
||||
"inspector.closeButton": "インスペクターを閉じる",
|
||||
"inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です",
|
||||
"inspector.reqTimestampKey": "リクエストのタイムスタンプ",
|
||||
"inspector.title": "インスペクター",
|
||||
"inspector.view": "{viewName} を表示",
|
||||
"core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "ホームページに移動",
|
||||
"core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel": "ヘルプメニュー",
|
||||
"core.ui.chrome.headerGlobalNav.helpMenuGoToDocumentation": "ドキュメントに移動",
|
||||
|
@ -5078,7 +5078,6 @@
|
|||
"xpack.infra.linkLogsTitle": "ログ",
|
||||
"xpack.infra.linkTo.hostWithIp.error": "IP アドレス「{hostIp}」でホストが見つかりません.",
|
||||
"xpack.infra.linkTo.hostWithIp.loading": "IP アドレス「{hostIp}」のホストを読み込み中",
|
||||
"xpack.infra.logColumnHeaders.configureColumnsLabel": "列を構成",
|
||||
"xpack.infra.logEntryActionsMenu.buttonLabel": "アクション",
|
||||
"xpack.infra.logEntryActionsMenu.uptimeActionLabel": "監視ステータスを表示",
|
||||
"xpack.infra.logEntryItemView.viewDetailsToolTip": "詳細を表示",
|
||||
|
@ -5111,8 +5110,6 @@
|
|||
"xpack.infra.logs.stopStreamingButtonLabel": "ストリーム停止",
|
||||
"xpack.infra.logs.streamingDescription": "新しいエントリーをストリーム中...",
|
||||
"xpack.infra.logs.streamingNewEntriesText": "新しいエントリーをストリーム中",
|
||||
"xpack.infra.logsPage.documentTitle": "ログ",
|
||||
"xpack.infra.logsPage.logsBreadcrumbsText": "ログ",
|
||||
"xpack.infra.logsPage.logsHelpContent.feedbackLinkText": "ログのフィードバックを提供",
|
||||
"xpack.infra.logsPage.noLoggingIndicesDescription": "追加しましょう!",
|
||||
"xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel": "セットアップの手順を表示",
|
||||
|
@ -5234,10 +5231,7 @@
|
|||
"xpack.infra.registerFeatures.logsDescription": "ログをリアルタイムでストリーするか、コンソール式の UI で履歴ビューをスクロールします。",
|
||||
"xpack.infra.registerFeatures.logsTitle": "ログ",
|
||||
"xpack.infra.sourceConfiguration.addLogColumnButtonLabel": "列を追加",
|
||||
"xpack.infra.sourceConfiguration.closeButtonLabel": "閉じる",
|
||||
"xpack.infra.sourceConfiguration.containerFieldDescription": "Docker コンテナーの識別に使用されるフィールドです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.containerFieldLabel": "コンテナー ID",
|
||||
"xpack.infra.sourceConfiguration.discardAndCloseButtonLabel": "破棄して閉じる",
|
||||
"xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "このフィールドは未入力のままにできません。",
|
||||
"xpack.infra.sourceConfiguration.fieldLogColumnTitle": "フィールド",
|
||||
"xpack.infra.sourceConfiguration.fieldsSectionTitle": "フィールド",
|
||||
|
@ -5246,29 +5240,18 @@
|
|||
"xpack.infra.sourceConfiguration.indicesSectionTitle": "インデックス",
|
||||
"xpack.infra.sourceConfiguration.logColumnListEmptyErrorMessage": "ログ列リストは未入力のままにできません。",
|
||||
"xpack.infra.sourceConfiguration.logColumnsSectionTitle": "列",
|
||||
"xpack.infra.sourceConfiguration.logIndicesDescription": "ログデータを含む一致するインデックスのインデックスパターンです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.logIndicesLabel": "ログインデックス",
|
||||
"xpack.infra.sourceConfiguration.messageLogColumnDescription": "このシステムフィールドは、ドキュメントフィールドから取得されたログエントリーメッセージを表示します。",
|
||||
"xpack.infra.sourceConfiguration.metricIndicesDescription": "Metricbeat データを含む一致するインデックスのインデックスパターンです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.metricIndicesLabel": "メトリックインデックス",
|
||||
"xpack.infra.sourceConfiguration.nameLabel": "名前",
|
||||
"xpack.infra.sourceConfiguration.nameSectionTitle": "名前",
|
||||
"xpack.infra.sourceConfiguration.noLogColumnsDescription": "上のボタンでこのリストに列を追加します。",
|
||||
"xpack.infra.sourceConfiguration.noLogColumnsTitle": "列がありません",
|
||||
"xpack.infra.sourceConfiguration.podFieldDescription": "Kubernetes ポッドの識別に使用されるフィールドです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.podFieldLabel": "ポッド ID",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationButtonLabel": "構成",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationIndicesTabTitle": "インデックスとフィールド",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationLogColumnsTabTitle": "ログ列",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationReadonlyTitle": "ソース構成を表示",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationTitle": "ソースの構成",
|
||||
"xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "システム",
|
||||
"xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "同じタイムスタンプの 2 つのエントリーを識別するのに使用されるフィールドです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "タイブレーカー",
|
||||
"xpack.infra.sourceConfiguration.timestampFieldDescription": "ログエントリーの並べ替えに使用されるタイムスタンプです。推奨値は {defaultValue} です。",
|
||||
"xpack.infra.sourceConfiguration.timestampFieldLabel": "タイムスタンプ",
|
||||
"xpack.infra.sourceConfiguration.timestampLogColumnDescription": "このシステムフィールドは、{timestampSetting} フィールド設定から判断されたログエントリーの時刻を表示します。",
|
||||
"xpack.infra.sourceConfiguration.updateSourceConfigurationButtonLabel": "ソースを更新",
|
||||
"xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "データソースの読み込みに失敗しました。",
|
||||
"xpack.infra.sourceLoadingPage.loadingDataSourcesMessage": "データソースを読み込み中",
|
||||
"xpack.infra.tableView.columnName.avg": "平均",
|
||||
|
@ -10643,4 +10626,4 @@
|
|||
"xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。",
|
||||
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -484,11 +484,6 @@
|
|||
"common.ui.indexPattern.unknownFieldHeader": "未知字段类型 {type}",
|
||||
"common.ui.indexPattern.warningText": "当前正在查询所有匹配 {index} 的索引。{title} 应迁移到基于通配符的索引模式。",
|
||||
"common.ui.indexPattern.warningTitle": "已移除对时间间隔索引模式的支持",
|
||||
"inspector.closeButton": "关闭检查器",
|
||||
"inspector.reqTimestampDescription": "记录请求启动的时间",
|
||||
"inspector.reqTimestampKey": "请求时间戳",
|
||||
"inspector.title": "检查器",
|
||||
"inspector.view": "视图:{viewName}",
|
||||
"common.ui.legacyBrowserMessage": "此 Kibana 安装启用了当前浏览器未满足的严格安全要求。",
|
||||
"common.ui.legacyBrowserTitle": "请升级您的浏览器",
|
||||
"common.ui.management.breadcrumb": "管理",
|
||||
|
@ -616,6 +611,11 @@
|
|||
"common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "无法获取边界",
|
||||
"common.ui.welcomeErrorMessage": "Kibana 未正确加载。检查服务器输出以了解详情。",
|
||||
"common.ui.welcomeMessage": "正在加载 Kibana",
|
||||
"inspector.closeButton": "关闭检查器",
|
||||
"inspector.reqTimestampDescription": "记录请求启动的时间",
|
||||
"inspector.reqTimestampKey": "请求时间戳",
|
||||
"inspector.title": "检查器",
|
||||
"inspector.view": "视图:{viewName}",
|
||||
"core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "前往主页",
|
||||
"core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel": "帮助菜单",
|
||||
"core.ui.chrome.headerGlobalNav.helpMenuGoToDocumentation": "前往文档",
|
||||
|
@ -5221,7 +5221,6 @@
|
|||
"xpack.infra.linkLogsTitle": "Logs",
|
||||
"xpack.infra.linkTo.hostWithIp.error": "未找到 IP 地址为“{hostIp}”的主机。",
|
||||
"xpack.infra.linkTo.hostWithIp.loading": "正在加载 IP 地址为“{hostIp}”的主机。",
|
||||
"xpack.infra.logColumnHeaders.configureColumnsLabel": "配置列",
|
||||
"xpack.infra.logEntryActionsMenu.buttonLabel": "操作",
|
||||
"xpack.infra.logEntryActionsMenu.uptimeActionLabel": "查看监测状态",
|
||||
"xpack.infra.logEntryItemView.viewDetailsToolTip": "查看详情",
|
||||
|
@ -5254,8 +5253,6 @@
|
|||
"xpack.infra.logs.stopStreamingButtonLabel": "停止流式传输",
|
||||
"xpack.infra.logs.streamingDescription": "正在流式传输新条目……",
|
||||
"xpack.infra.logs.streamingNewEntriesText": "正在流式传输新条目",
|
||||
"xpack.infra.logsPage.documentTitle": "Logs",
|
||||
"xpack.infra.logsPage.logsBreadcrumbsText": "Logs",
|
||||
"xpack.infra.logsPage.logsHelpContent.feedbackLinkText": "提供 Logs 的反馈",
|
||||
"xpack.infra.logsPage.noLoggingIndicesDescription": "让我们添加一些!",
|
||||
"xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel": "查看设置说明",
|
||||
|
@ -5377,10 +5374,7 @@
|
|||
"xpack.infra.registerFeatures.logsDescription": "实时流式传输日志或在类似控制台的工具中滚动浏览历史视图。",
|
||||
"xpack.infra.registerFeatures.logsTitle": "Logs",
|
||||
"xpack.infra.sourceConfiguration.addLogColumnButtonLabel": "添加列",
|
||||
"xpack.infra.sourceConfiguration.closeButtonLabel": "关闭",
|
||||
"xpack.infra.sourceConfiguration.containerFieldDescription": "用于标识 Docker 容器的字段。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.containerFieldLabel": "容器 ID",
|
||||
"xpack.infra.sourceConfiguration.discardAndCloseButtonLabel": "丢弃并关闭",
|
||||
"xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "字段不得为空。",
|
||||
"xpack.infra.sourceConfiguration.fieldLogColumnTitle": "字段",
|
||||
"xpack.infra.sourceConfiguration.fieldsSectionTitle": "字段",
|
||||
|
@ -5389,29 +5383,18 @@
|
|||
"xpack.infra.sourceConfiguration.indicesSectionTitle": "索引",
|
||||
"xpack.infra.sourceConfiguration.logColumnListEmptyErrorMessage": "日志列列表不得为空。",
|
||||
"xpack.infra.sourceConfiguration.logColumnsSectionTitle": "列",
|
||||
"xpack.infra.sourceConfiguration.logIndicesDescription": "用于匹配包含日志数据的索引的索引模式。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.logIndicesLabel": "日志索引",
|
||||
"xpack.infra.sourceConfiguration.messageLogColumnDescription": "此系统字段显示派生自文档字段的日志条目消息。",
|
||||
"xpack.infra.sourceConfiguration.metricIndicesDescription": "用于匹配包含 Metricbeat 数据的索引的索引模式。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.metricIndicesLabel": "指标索引",
|
||||
"xpack.infra.sourceConfiguration.nameLabel": "名称",
|
||||
"xpack.infra.sourceConfiguration.nameSectionTitle": "名称",
|
||||
"xpack.infra.sourceConfiguration.noLogColumnsDescription": "使用上面的按钮将列添加到此列表。",
|
||||
"xpack.infra.sourceConfiguration.noLogColumnsTitle": "无列",
|
||||
"xpack.infra.sourceConfiguration.podFieldDescription": "用于标识 Kubernetes Pod 的字段。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.podFieldLabel": "Pod ID",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationButtonLabel": "配置",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationIndicesTabTitle": "索引和字段",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationLogColumnsTabTitle": "日志列",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationReadonlyTitle": "查看源配置",
|
||||
"xpack.infra.sourceConfiguration.sourceConfigurationTitle": "配置源",
|
||||
"xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "系统",
|
||||
"xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "用于时间戳相同的两个条目间决胜的字段。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "决胜属性",
|
||||
"xpack.infra.sourceConfiguration.timestampFieldDescription": "用于排序日志条目的时间戳。推荐值为 {defaultValue}。",
|
||||
"xpack.infra.sourceConfiguration.timestampFieldLabel": "时间戳",
|
||||
"xpack.infra.sourceConfiguration.timestampLogColumnDescription": "此系统字段显示 {timestampSetting} 字段设置所确定的日志条目时间。",
|
||||
"xpack.infra.sourceConfiguration.updateSourceConfigurationButtonLabel": "更新源",
|
||||
"xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "无法加载数据源。",
|
||||
"xpack.infra.sourceLoadingPage.loadingDataSourcesMessage": "正在加载数据源",
|
||||
"xpack.infra.tableView.columnName.avg": "平均值",
|
||||
|
@ -10785,4 +10768,4 @@
|
|||
"xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "“日志文本”必填。",
|
||||
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,10 +11,10 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const infraLogStream = getService('infraLogStream');
|
||||
const infraSourceConfigurationFlyout = getService('infraSourceConfigurationFlyout');
|
||||
const pageObjects = getPageObjects(['infraLogs']);
|
||||
const infraSourceConfigurationForm = getService('infraSourceConfigurationForm');
|
||||
const pageObjects = getPageObjects(['common', 'infraLogs']);
|
||||
|
||||
describe('Logs Page', function() {
|
||||
describe('Logs Source Configuration', function() {
|
||||
this.tags('smoke');
|
||||
before(async () => {
|
||||
await esArchiver.load('empty_kibana');
|
||||
|
@ -23,7 +23,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('empty_kibana');
|
||||
});
|
||||
|
||||
describe('with logs present', () => {
|
||||
describe('Allows indices configuration', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('infra/metrics_and_logs');
|
||||
});
|
||||
|
@ -31,54 +31,44 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('infra/metrics_and_logs');
|
||||
});
|
||||
|
||||
it('renders the log stream', async () => {
|
||||
await pageObjects.infraLogs.navigateTo();
|
||||
await pageObjects.infraLogs.getLogStream();
|
||||
});
|
||||
|
||||
it('can change the log indices to a pattern that matches nothing', async () => {
|
||||
await pageObjects.infraLogs.openSourceConfigurationFlyout();
|
||||
await infraSourceConfigurationFlyout.switchToIndicesAndFieldsTab();
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/settings');
|
||||
await infraSourceConfigurationForm.getForm();
|
||||
|
||||
const nameInput = await infraSourceConfigurationFlyout.getNameInput();
|
||||
const nameInput = await infraSourceConfigurationForm.getNameInput();
|
||||
await nameInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await nameInput.type('Modified Source');
|
||||
|
||||
const logIndicesInput = await infraSourceConfigurationFlyout.getLogIndicesInput();
|
||||
const logIndicesInput = await infraSourceConfigurationForm.getLogIndicesInput();
|
||||
await logIndicesInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await logIndicesInput.type('does-not-exist-*');
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
await infraSourceConfigurationForm.saveConfiguration();
|
||||
});
|
||||
|
||||
it('renders the no indices screen when no indices match the pattern', async () => {
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/stream');
|
||||
await pageObjects.infraLogs.getNoLogsIndicesPrompt();
|
||||
});
|
||||
|
||||
it('can change the log indices back to a pattern that matches something', async () => {
|
||||
await pageObjects.infraLogs.openSourceConfigurationFlyout();
|
||||
await infraSourceConfigurationFlyout.switchToIndicesAndFieldsTab();
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/settings');
|
||||
await infraSourceConfigurationForm.getForm();
|
||||
|
||||
const logIndicesInput = await infraSourceConfigurationFlyout.getLogIndicesInput();
|
||||
const logIndicesInput = await infraSourceConfigurationForm.getLogIndicesInput();
|
||||
await logIndicesInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await logIndicesInput.type('filebeat-*');
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
});
|
||||
|
||||
it('renders the log stream again', async () => {
|
||||
await pageObjects.infraLogs.getLogStream();
|
||||
await infraSourceConfigurationForm.saveConfiguration();
|
||||
});
|
||||
|
||||
it('renders the default log columns with their headers', async () => {
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/stream');
|
||||
const columnHeaderLabels = await infraLogStream.getColumnHeaderLabels();
|
||||
|
||||
expect(columnHeaderLabels).to.eql(['Timestamp', 'event.dataset', 'Message', '']);
|
||||
expect(columnHeaderLabels).to.eql(['Timestamp', 'event.dataset', 'Message']);
|
||||
|
||||
const logStreamEntries = await infraLogStream.getStreamEntries();
|
||||
|
||||
expect(logStreamEntries.length).to.be.greaterThan(0);
|
||||
|
||||
const firstLogStreamEntry = logStreamEntries[0];
|
||||
|
@ -90,26 +80,25 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
it('can change the log columns', async () => {
|
||||
await pageObjects.infraLogs.openSourceConfigurationFlyout();
|
||||
await infraSourceConfigurationFlyout.switchToLogsTab();
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/settings');
|
||||
await infraSourceConfigurationForm.getForm();
|
||||
|
||||
await infraSourceConfigurationFlyout.removeAllLogColumns();
|
||||
await infraSourceConfigurationFlyout.addTimestampLogColumn();
|
||||
await infraSourceConfigurationFlyout.addFieldLogColumn('host.name');
|
||||
await infraSourceConfigurationForm.removeAllLogColumns();
|
||||
await infraSourceConfigurationForm.addTimestampLogColumn();
|
||||
await infraSourceConfigurationForm.addFieldLogColumn('host.name');
|
||||
|
||||
// TODO: make test more robust
|
||||
// await infraSourceConfigurationFlyout.moveLogColumn(0, 1);
|
||||
// await infraSourceConfigurationForm.moveLogColumn(0, 1);
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
await infraSourceConfigurationForm.saveConfiguration();
|
||||
});
|
||||
|
||||
it('renders the changed log columns with their headers', async () => {
|
||||
await pageObjects.common.navigateToActualUrl('infraLogs', 'logs/stream');
|
||||
const columnHeaderLabels = await infraLogStream.getColumnHeaderLabels();
|
||||
|
||||
// TODO: make test more robust
|
||||
// expect(columnHeaderLabels).to.eql(['host.name', 'Timestamp', '']);
|
||||
expect(columnHeaderLabels).to.eql(['Timestamp', 'host.name', '']);
|
||||
// expect(columnHeaderLabels).to.eql(['host.name', 'Timestamp']);
|
||||
expect(columnHeaderLabels).to.eql(['Timestamp', 'host.name']);
|
||||
|
||||
const logStreamEntries = await infraLogStream.getStreamEntries();
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ const DATE_WITH_DATA = DATES.metricsAndLogs.hosts.withData;
|
|||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const infraSourceConfigurationFlyout = getService('infraSourceConfigurationFlyout');
|
||||
const infraSourceConfigurationForm = getService('infraSourceConfigurationForm');
|
||||
const pageObjects = getPageObjects(['common', 'infraHome']);
|
||||
|
||||
describe('Infrastructure Snapshot Page', function() {
|
||||
describe('Infrastructure Source Configuration', function() {
|
||||
this.tags('smoke');
|
||||
before(async () => {
|
||||
await esArchiver.load('empty_kibana');
|
||||
|
@ -38,38 +38,37 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
it('can change the metric indices to a pattern that matches nothing', async () => {
|
||||
await pageObjects.infraHome.openSourceConfigurationFlyout();
|
||||
await infraSourceConfigurationFlyout.switchToIndicesAndFieldsTab();
|
||||
await pageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/settings');
|
||||
|
||||
const nameInput = await infraSourceConfigurationFlyout.getNameInput();
|
||||
const nameInput = await infraSourceConfigurationForm.getNameInput();
|
||||
await nameInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await nameInput.type('Modified Source');
|
||||
|
||||
const metricIndicesInput = await infraSourceConfigurationFlyout.getMetricIndicesInput();
|
||||
const metricIndicesInput = await infraSourceConfigurationForm.getMetricIndicesInput();
|
||||
await metricIndicesInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await metricIndicesInput.type('does-not-exist-*');
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
await infraSourceConfigurationForm.saveConfiguration();
|
||||
});
|
||||
|
||||
it('renders the no indices screen when no indices match the pattern', async () => {
|
||||
await pageObjects.common.navigateToApp('infraOps');
|
||||
await pageObjects.infraHome.getNoMetricsIndicesPrompt();
|
||||
});
|
||||
|
||||
it('can change the log indices back to a pattern that matches something', async () => {
|
||||
await pageObjects.infraHome.openSourceConfigurationFlyout();
|
||||
await infraSourceConfigurationFlyout.switchToIndicesAndFieldsTab();
|
||||
it('can change the metric indices back to a pattern that matches something', async () => {
|
||||
await pageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/settings');
|
||||
|
||||
const metricIndicesInput = await infraSourceConfigurationFlyout.getMetricIndicesInput();
|
||||
const metricIndicesInput = await infraSourceConfigurationForm.getMetricIndicesInput();
|
||||
await metricIndicesInput.clearValueWithKeyboard({ charByChar: true });
|
||||
await metricIndicesInput.type('metricbeat-*');
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
await infraSourceConfigurationForm.saveConfiguration();
|
||||
});
|
||||
|
||||
it('renders the log stream again', async () => {
|
||||
it('renders the waffle map again', async () => {
|
||||
await pageObjects.common.navigateToApp('infraOps');
|
||||
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
|
||||
await pageObjects.infraHome.getWaffleMap();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
|
|||
|
||||
export function InfraHomePageProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const browser = getService('browser');
|
||||
|
||||
|
@ -23,6 +24,12 @@ export function InfraHomePageProvider({ getService }: FtrProviderContext) {
|
|||
},
|
||||
|
||||
async getWaffleMap() {
|
||||
await retry.try(async () => {
|
||||
const element = await testSubjects.find('waffleMap');
|
||||
if (!element) {
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
return await testSubjects.find('waffleMap');
|
||||
},
|
||||
|
||||
|
|
|
@ -25,10 +25,5 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide
|
|||
async getNoLogsIndicesPrompt() {
|
||||
return await testSubjects.find('noLogsIndicesPrompt');
|
||||
},
|
||||
|
||||
async openSourceConfigurationFlyout() {
|
||||
await testSubjects.click('configureSourceButton');
|
||||
await testSubjects.exists('sourceConfigurationFlyout');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import { GrokDebuggerProvider } from './grok_debugger';
|
|||
// @ts-ignore not ts yet
|
||||
import { UserMenuProvider } from './user_menu';
|
||||
import { UptimeProvider } from './uptime';
|
||||
import { InfraSourceConfigurationFlyoutProvider } from './infra_source_configuration_flyout';
|
||||
import { InfraSourceConfigurationFormProvider } from './infra_source_configuration_form';
|
||||
import { InfraLogStreamProvider } from './infra_log_stream';
|
||||
import { MachineLearningProvider } from './ml';
|
||||
|
||||
|
@ -86,7 +86,7 @@ export const services = {
|
|||
spaces: SpacesServiceProvider,
|
||||
userMenu: UserMenuProvider,
|
||||
uptime: UptimeProvider,
|
||||
infraSourceConfigurationFlyout: InfraSourceConfigurationFlyoutProvider,
|
||||
infraSourceConfigurationForm: InfraSourceConfigurationFormProvider,
|
||||
infraLogStream: InfraLogStreamProvider,
|
||||
ml: MachineLearningProvider,
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ import { WebElementWrapper } from '../../../../test/functional/services/lib/web_
|
|||
|
||||
export function InfraLogStreamProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
|
||||
return {
|
||||
async getColumnHeaderLabels(): Promise<string[]> {
|
||||
|
@ -18,7 +19,14 @@ export function InfraLogStreamProvider({ getService }: FtrProviderContext) {
|
|||
return await Promise.all(columnHeaderElements.map(element => element.getVisibleText()));
|
||||
},
|
||||
|
||||
async getStreamEntries(): Promise<WebElementWrapper[]> {
|
||||
async getStreamEntries(minimumItems = 1): Promise<WebElementWrapper[]> {
|
||||
await retry.try(async () => {
|
||||
const elements = await testSubjects.findAll('streamEntry');
|
||||
if (!elements || elements.length < minimumItems) {
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
|
||||
return await testSubjects.findAll('streamEntry');
|
||||
},
|
||||
|
||||
|
|
|
@ -7,68 +7,56 @@
|
|||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
|
||||
|
||||
export function InfraSourceConfigurationFlyoutProvider({ getService }: FtrProviderContext) {
|
||||
const find = getService('find');
|
||||
export function InfraSourceConfigurationFormProvider({ getService }: FtrProviderContext) {
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Tab navigation
|
||||
*/
|
||||
async switchToIndicesAndFieldsTab() {
|
||||
await (await find.descendantDisplayedByCssSelector(
|
||||
'#indicesAndFieldsTab',
|
||||
await this.getFlyout()
|
||||
)).click();
|
||||
await testSubjects.find('sourceConfigurationNameSectionTitle');
|
||||
},
|
||||
async switchToLogsTab() {
|
||||
await (await find.descendantDisplayedByCssSelector(
|
||||
'#logsTab',
|
||||
await this.getFlyout()
|
||||
)).click();
|
||||
await testSubjects.find('sourceConfigurationLogColumnsSectionTitle');
|
||||
},
|
||||
|
||||
/**
|
||||
* Indices and fields
|
||||
*/
|
||||
async getNameInput(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.findDescendant('nameInput', await this.getFlyout());
|
||||
return await testSubjects.findDescendant('nameInput', await this.getForm());
|
||||
},
|
||||
async getLogIndicesInput(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.findDescendant('logIndicesInput', await this.getFlyout());
|
||||
return await testSubjects.findDescendant('logIndicesInput', await this.getForm());
|
||||
},
|
||||
async getMetricIndicesInput(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.findDescendant('metricIndicesInput', await this.getFlyout());
|
||||
return await testSubjects.findDescendant('metricIndicesInput', await this.getForm());
|
||||
},
|
||||
|
||||
/**
|
||||
* Logs
|
||||
*/
|
||||
async getAddLogColumnButton(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.findDescendant('addLogColumnButton', await this.getFlyout());
|
||||
return await testSubjects.findDescendant('addLogColumnButton', await this.getForm());
|
||||
},
|
||||
async getAddLogColumnPopover(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.find('addLogColumnPopover');
|
||||
},
|
||||
async addTimestampLogColumn() {
|
||||
await (await this.getAddLogColumnButton()).click();
|
||||
await (await testSubjects.findDescendant(
|
||||
'addTimestampLogColumn',
|
||||
await this.getAddLogColumnPopover()
|
||||
)).click();
|
||||
await retry.try(async () => {
|
||||
await (await testSubjects.findDescendant(
|
||||
'addTimestampLogColumn',
|
||||
await this.getAddLogColumnPopover()
|
||||
)).click();
|
||||
});
|
||||
},
|
||||
async addFieldLogColumn(fieldName: string) {
|
||||
await (await this.getAddLogColumnButton()).click();
|
||||
const popover = await this.getAddLogColumnPopover();
|
||||
await (await testSubjects.findDescendant('fieldSearchInput', popover)).type(fieldName);
|
||||
await (await testSubjects.findDescendant(`addFieldLogColumn:${fieldName}`, popover)).click();
|
||||
await retry.try(async () => {
|
||||
const popover = await this.getAddLogColumnPopover();
|
||||
await (await testSubjects.findDescendant('fieldSearchInput', popover)).type(fieldName);
|
||||
await (await testSubjects.findDescendant(
|
||||
`addFieldLogColumn:${fieldName}`,
|
||||
popover
|
||||
)).click();
|
||||
});
|
||||
},
|
||||
async getLogColumnPanels(): Promise<WebElementWrapper[]> {
|
||||
return await testSubjects.findAllDescendant('logColumnPanel', await this.getFlyout());
|
||||
return await testSubjects.findAllDescendant('logColumnPanel', await this.getForm());
|
||||
},
|
||||
async removeLogColumn(columnIndex: number) {
|
||||
const logColumnPanel = (await this.getLogColumnPanels())[columnIndex];
|
||||
|
@ -104,29 +92,24 @@ export function InfraSourceConfigurationFlyoutProvider({ getService }: FtrProvid
|
|||
},
|
||||
|
||||
/**
|
||||
* Form and flyout
|
||||
* Form
|
||||
*/
|
||||
async getFlyout(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.find('sourceConfigurationFlyout');
|
||||
async getForm(): Promise<WebElementWrapper> {
|
||||
return await testSubjects.find('sourceConfigurationContent');
|
||||
},
|
||||
async saveConfiguration() {
|
||||
await (await testSubjects.findDescendant(
|
||||
'updateSourceConfigurationButton',
|
||||
await this.getFlyout()
|
||||
'applySettingsButton',
|
||||
await this.getForm()
|
||||
)).click();
|
||||
|
||||
await retry.try(async () => {
|
||||
const element = await testSubjects.findDescendant(
|
||||
'updateSourceConfigurationButton',
|
||||
await this.getFlyout()
|
||||
'applySettingsButton',
|
||||
await this.getForm()
|
||||
);
|
||||
return !(await element.isEnabled());
|
||||
});
|
||||
},
|
||||
async closeFlyout() {
|
||||
const flyout = await this.getFlyout();
|
||||
await (await testSubjects.findDescendant('closeFlyoutButton', flyout)).click();
|
||||
await testSubjects.waitForDeleted(flyout);
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue