[Logs / Metrics UI] Separate logs / metrics source configuration awareness (#95334)

* Remove metrics awareness of logs fields
This commit is contained in:
Kerry Gallagher 2021-03-25 18:59:18 +00:00 committed by GitHub
parent 43e3d558fd
commit 6b6404954e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 539 additions and 633 deletions

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { LogSourceConfigurationProperties } from '../http_api/log_sources';
// NOTE: Type will change, see below.
type ResolvedLogsSourceConfiguration = LogSourceConfigurationProperties;
// NOTE: This will handle real resolution for https://github.com/elastic/kibana/issues/92650, via the index patterns service, but for now just
// hands back properties from the saved object (and therefore looks pointless...).
export const resolveLogSourceConfiguration = (
sourceConfiguration: LogSourceConfigurationProperties
): ResolvedLogsSourceConfiguration => {
return sourceConfiguration;
};

View file

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import { omit } from 'lodash';
import {
SourceConfigurationRT,
SourceStatusRuntimeType,
} from '../source_configuration/source_configuration';
import { DeepPartial } from '../utility_types';
/**
* Properties specific to the Metrics Source Configuration.
*/
export const metricsSourceConfigurationPropertiesRT = rt.strict({
name: SourceConfigurationRT.props.name,
description: SourceConfigurationRT.props.description,
metricAlias: SourceConfigurationRT.props.metricAlias,
inventoryDefaultView: SourceConfigurationRT.props.inventoryDefaultView,
metricsExplorerDefaultView: SourceConfigurationRT.props.metricsExplorerDefaultView,
fields: rt.strict(omit(SourceConfigurationRT.props.fields.props, 'message')),
anomalyThreshold: rt.number,
});
export type MetricsSourceConfigurationProperties = rt.TypeOf<
typeof metricsSourceConfigurationPropertiesRT
>;
export const partialMetricsSourceConfigurationPropertiesRT = rt.partial({
...metricsSourceConfigurationPropertiesRT.type.props,
fields: rt.partial({
...metricsSourceConfigurationPropertiesRT.type.props.fields.type.props,
}),
});
export type PartialMetricsSourceConfigurationProperties = rt.TypeOf<
typeof partialMetricsSourceConfigurationPropertiesRT
>;
const metricsSourceConfigurationOriginRT = rt.keyof({
fallback: null,
internal: null,
stored: null,
});
export const metricsSourceStatusRT = rt.strict({
metricIndicesExist: SourceStatusRuntimeType.props.metricIndicesExist,
indexFields: SourceStatusRuntimeType.props.indexFields,
});
export type MetricsSourceStatus = rt.TypeOf<typeof metricsSourceStatusRT>;
export const metricsSourceConfigurationRT = rt.exact(
rt.intersection([
rt.type({
id: rt.string,
origin: metricsSourceConfigurationOriginRT,
configuration: metricsSourceConfigurationPropertiesRT,
}),
rt.partial({
updatedAt: rt.number,
version: rt.string,
status: metricsSourceStatusRT,
}),
])
);
export type MetricsSourceConfiguration = rt.TypeOf<typeof metricsSourceConfigurationRT>;
export type PartialMetricsSourceConfiguration = DeepPartial<MetricsSourceConfiguration>;
export const metricsSourceConfigurationResponseRT = rt.type({
source: metricsSourceConfigurationRT,
});
export type MetricsSourceConfigurationResponse = rt.TypeOf<
typeof metricsSourceConfigurationResponseRT
>;

View file

@ -5,8 +5,19 @@
* 2.0.
*/
/**
* These are the core source configuration types that represent a Source Configuration in
* it's entirety. There are then subsets of this configuration that form the Logs Source Configuration
* and Metrics Source Configuration. The Logs Source Configuration is further expanded to it's resolved form.
* -> Source Configuration
* -> Logs source configuration
* -> Resolved Logs Source Configuration
* -> Metrics Source Configuration
*/
/* eslint-disable @typescript-eslint/no-empty-interface */
import { omit } from 'lodash';
import * as rt from 'io-ts';
import moment from 'moment';
import { pipe } from 'fp-ts/lib/pipeable';
@ -29,121 +40,113 @@ export const TimestampFromString = new rt.Type<number, string>(
);
/**
* Stored source configuration as read from and written to saved objects
* Log columns
*/
const SavedSourceConfigurationFieldsRuntimeType = rt.partial({
container: rt.string,
host: rt.string,
pod: rt.string,
tiebreaker: rt.string,
timestamp: rt.string,
});
export type InfraSavedSourceConfigurationFields = rt.TypeOf<
typeof SavedSourceConfigurationFieldColumnRuntimeType
>;
export const SavedSourceConfigurationTimestampColumnRuntimeType = rt.type({
export const SourceConfigurationTimestampColumnRuntimeType = rt.type({
timestampColumn: rt.type({
id: rt.string,
}),
});
export type InfraSourceConfigurationTimestampColumn = rt.TypeOf<
typeof SavedSourceConfigurationTimestampColumnRuntimeType
typeof SourceConfigurationTimestampColumnRuntimeType
>;
export const SavedSourceConfigurationMessageColumnRuntimeType = rt.type({
export const SourceConfigurationMessageColumnRuntimeType = rt.type({
messageColumn: rt.type({
id: rt.string,
}),
});
export type InfraSourceConfigurationMessageColumn = rt.TypeOf<
typeof SavedSourceConfigurationMessageColumnRuntimeType
typeof SourceConfigurationMessageColumnRuntimeType
>;
export const SavedSourceConfigurationFieldColumnRuntimeType = rt.type({
export const SourceConfigurationFieldColumnRuntimeType = rt.type({
fieldColumn: rt.type({
id: rt.string,
field: rt.string,
}),
});
export const SavedSourceConfigurationColumnRuntimeType = rt.union([
SavedSourceConfigurationTimestampColumnRuntimeType,
SavedSourceConfigurationMessageColumnRuntimeType,
SavedSourceConfigurationFieldColumnRuntimeType,
]);
export type InfraSavedSourceConfigurationColumn = rt.TypeOf<
typeof SavedSourceConfigurationColumnRuntimeType
export type InfraSourceConfigurationFieldColumn = rt.TypeOf<
typeof SourceConfigurationFieldColumnRuntimeType
>;
export const SavedSourceConfigurationRuntimeType = rt.partial({
export const SourceConfigurationColumnRuntimeType = rt.union([
SourceConfigurationTimestampColumnRuntimeType,
SourceConfigurationMessageColumnRuntimeType,
SourceConfigurationFieldColumnRuntimeType,
]);
export type InfraSourceConfigurationColumn = rt.TypeOf<typeof SourceConfigurationColumnRuntimeType>;
/**
* Fields
*/
const SourceConfigurationFieldsRT = rt.type({
container: rt.string,
host: rt.string,
pod: rt.string,
tiebreaker: rt.string,
timestamp: rt.string,
message: rt.array(rt.string),
});
/**
* Properties that represent a full source configuration, which is the result of merging static values with
* saved values.
*/
export const SourceConfigurationRT = rt.type({
name: rt.string,
description: rt.string,
metricAlias: rt.string,
logAlias: rt.string,
inventoryDefaultView: rt.string,
metricsExplorerDefaultView: rt.string,
fields: SavedSourceConfigurationFieldsRuntimeType,
logColumns: rt.array(SavedSourceConfigurationColumnRuntimeType),
fields: SourceConfigurationFieldsRT,
logColumns: rt.array(SourceConfigurationColumnRuntimeType),
anomalyThreshold: rt.number,
});
/**
* Stored source configuration as read from and written to saved objects
*/
const SavedSourceConfigurationFieldsRuntimeType = rt.partial(
omit(SourceConfigurationFieldsRT.props, ['message'])
);
export type InfraSavedSourceConfigurationFields = rt.TypeOf<
typeof SavedSourceConfigurationFieldsRuntimeType
>;
export const SavedSourceConfigurationRuntimeType = rt.intersection([
rt.partial(omit(SourceConfigurationRT.props, ['fields'])),
rt.partial({
fields: SavedSourceConfigurationFieldsRuntimeType,
}),
]);
export interface InfraSavedSourceConfiguration
extends rt.TypeOf<typeof SavedSourceConfigurationRuntimeType> {}
export const pickSavedSourceConfiguration = (
value: InfraSourceConfiguration
): InfraSavedSourceConfiguration => {
const {
name,
description,
metricAlias,
logAlias,
fields,
inventoryDefaultView,
metricsExplorerDefaultView,
logColumns,
anomalyThreshold,
} = value;
const { container, host, pod, tiebreaker, timestamp } = fields;
return {
name,
description,
metricAlias,
logAlias,
inventoryDefaultView,
metricsExplorerDefaultView,
fields: { container, host, pod, tiebreaker, timestamp },
logColumns,
anomalyThreshold,
};
return value;
};
/**
* Static source configuration as read from the configuration file
* Static source configuration, the result of merging values from the config file and
* hardcoded defaults.
*/
const StaticSourceConfigurationFieldsRuntimeType = rt.partial({
...SavedSourceConfigurationFieldsRuntimeType.props,
message: rt.array(rt.string),
});
const StaticSourceConfigurationFieldsRuntimeType = rt.partial(SourceConfigurationFieldsRT.props);
export const StaticSourceConfigurationRuntimeType = rt.partial({
name: rt.string,
description: rt.string,
metricAlias: rt.string,
logAlias: rt.string,
inventoryDefaultView: rt.string,
metricsExplorerDefaultView: rt.string,
...SourceConfigurationRT.props,
fields: StaticSourceConfigurationFieldsRuntimeType,
logColumns: rt.array(SavedSourceConfigurationColumnRuntimeType),
anomalyThreshold: rt.number,
});
export interface InfraStaticSourceConfiguration
@ -153,18 +156,20 @@ export interface InfraStaticSourceConfiguration
* Full source configuration type after all cleanup has been done at the edges
*/
const SourceConfigurationFieldsRuntimeType = rt.type({
...StaticSourceConfigurationFieldsRuntimeType.props,
});
export type InfraSourceConfigurationFields = rt.TypeOf<typeof SourceConfigurationFieldsRuntimeType>;
export type InfraSourceConfigurationFields = rt.TypeOf<typeof SourceConfigurationFieldsRT>;
export const SourceConfigurationRuntimeType = rt.type({
...SavedSourceConfigurationRuntimeType.props,
fields: SourceConfigurationFieldsRuntimeType,
logColumns: rt.array(SavedSourceConfigurationColumnRuntimeType),
...SourceConfigurationRT.props,
fields: SourceConfigurationFieldsRT,
logColumns: rt.array(SourceConfigurationColumnRuntimeType),
});
export interface InfraSourceConfiguration
extends rt.TypeOf<typeof SourceConfigurationRuntimeType> {}
/**
* Source status
*/
const SourceStatusFieldRuntimeType = rt.type({
name: rt.string,
type: rt.string,
@ -175,12 +180,17 @@ const SourceStatusFieldRuntimeType = rt.type({
export type InfraSourceIndexField = rt.TypeOf<typeof SourceStatusFieldRuntimeType>;
const SourceStatusRuntimeType = rt.type({
export const SourceStatusRuntimeType = rt.type({
logIndicesExist: rt.boolean,
metricIndicesExist: rt.boolean,
indexFields: rt.array(SourceStatusFieldRuntimeType),
});
export interface InfraSourceStatus extends rt.TypeOf<typeof SourceStatusRuntimeType> {}
/**
* Source configuration along with source status and metadata
*/
export const SourceRuntimeType = rt.intersection([
rt.type({
id: rt.string,
@ -198,11 +208,6 @@ export const SourceRuntimeType = rt.intersection([
}),
]);
export interface InfraSourceStatus extends rt.TypeOf<typeof SourceStatusRuntimeType> {}
export interface InfraSourceConfiguration
extends rt.TypeOf<typeof SourceConfigurationRuntimeType> {}
export interface InfraSource extends rt.TypeOf<typeof SourceRuntimeType> {}
export const SourceResponseRuntimeType = rt.type({

View file

@ -17,7 +17,7 @@ import { act } from 'react-dom/test-utils';
import { Comparator } from '../../../../server/lib/alerting/metric_threshold/types';
import { SnapshotCustomMetricInput } from '../../../../common/http_api/snapshot_api';
jest.mock('../../../containers/source/use_source_via_http', () => ({
jest.mock('../../../containers/metrics_source/use_source_via_http', () => ({
useSourceViaHttp: () => ({
source: { id: 'default' },
createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }),

View file

@ -43,7 +43,7 @@ import {
AlertTypeParamsExpressionProps,
} from '../../../../../triggers_actions_ui/public';
import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar';
import { useSourceViaHttp } from '../../../containers/source/use_source_via_http';
import { useSourceViaHttp } from '../../../containers/metrics_source/use_source_via_http';
import { sqsMetricTypes } from '../../../../common/inventory_models/aws_sqs/toolbar_items';
import { ec2MetricTypes } from '../../../../common/inventory_models/aws_ec2/toolbar_items';
import { s3MetricTypes } from '../../../../common/inventory_models/aws_s3/toolbar_items';
@ -124,14 +124,13 @@ export const Expressions: React.FC<Props> = (props) => {
} = props;
const { source, createDerivedIndexPattern } = useSourceViaHttp({
sourceId: 'default',
type: 'metrics',
fetch: http.fetch,
toastWarning: notifications.toasts.addWarning,
});
const [timeSize, setTimeSize] = useState<number | undefined>(1);
const [timeUnit, setTimeUnit] = useState<Unit>('m');
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern(), [
createDerivedIndexPattern,
]);

View file

@ -12,7 +12,7 @@ import React from 'react';
import { Expression, AlertContextMeta } from './expression';
import { act } from 'react-dom/test-utils';
jest.mock('../../../containers/source/use_source_via_http', () => ({
jest.mock('../../../containers/metrics_source/use_source_via_http', () => ({
useSourceViaHttp: () => ({
source: { id: 'default' },
createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }),

View file

@ -27,7 +27,7 @@ import {
AlertTypeParamsExpressionProps,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../triggers_actions_ui/public/types';
import { useSourceViaHttp } from '../../../containers/source/use_source_via_http';
import { useSourceViaHttp } from '../../../containers/metrics_source/use_source_via_http';
import { findInventoryModel } from '../../../../common/inventory_models';
import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types';
import { NodeTypeExpression } from './node_type';
@ -75,12 +75,11 @@ export const Expression: React.FC<Props> = (props) => {
} = props;
const { source, createDerivedIndexPattern } = useSourceViaHttp({
sourceId: 'default',
type: 'metrics',
fetch: http.fetch,
toastWarning: notifications.toasts.addWarning,
});
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern(), [
createDerivedIndexPattern,
]);

View file

@ -15,7 +15,7 @@ import { act } from 'react-dom/test-utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { Comparator } from '../../../../server/lib/alerting/metric_threshold/types';
jest.mock('../../../containers/source/use_source_via_http', () => ({
jest.mock('../../../containers/metrics_source/use_source_via_http', () => ({
useSourceViaHttp: () => ({
source: { id: 'default' },
createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }),

View file

@ -35,7 +35,7 @@ import {
import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar';
import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by';
import { useSourceViaHttp } from '../../../containers/source/use_source_via_http';
import { useSourceViaHttp } from '../../../containers/metrics_source/use_source_via_http';
import { convertKueryToElasticSearchQuery } from '../../../utils/kuery';
import { ExpressionRow } from './expression_row';
@ -73,14 +73,13 @@ export const Expressions: React.FC<Props> = (props) => {
const { http, notifications } = useKibanaContextForPlugin().services;
const { source, createDerivedIndexPattern } = useSourceViaHttp({
sourceId: 'default',
type: 'metrics',
fetch: http.fetch,
toastWarning: notifications.toasts.addWarning,
});
const [timeSize, setTimeSize] = useState<number | undefined>(1);
const [timeUnit, setTimeUnit] = useState<Unit | undefined>('m');
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern(), [
createDerivedIndexPattern,
]);

View file

@ -10,7 +10,7 @@ import { mountWithIntl, nextTick } from '@kbn/test/jest';
import { coreMock as mockCoreMock } from 'src/core/public/mocks';
import { MetricExpression } from '../types';
import { IIndexPattern } from 'src/plugins/data/public';
import { InfraSource } from '../../../../common/http_api/source_api';
import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
import React from 'react';
import { ExpressionChart } from './expression_chart';
import { act } from 'react-dom/test-utils';
@ -45,20 +45,17 @@ describe('ExpressionChart', () => {
fields: [],
};
const source: InfraSource = {
const source: MetricsSourceConfiguration = {
id: 'default',
origin: 'fallback',
configuration: {
name: 'default',
description: 'The default configuration',
logColumns: [],
metricAlias: 'metricbeat-*',
logAlias: 'filebeat-*',
inventoryDefaultView: 'host',
metricsExplorerDefaultView: 'host',
fields: {
timestamp: '@timestamp',
message: ['message'],
container: 'container.id',
host: 'host.name',
pod: 'kubernetes.pod.uid',

View file

@ -11,7 +11,7 @@ import { first, last } from 'lodash';
import { EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { IIndexPattern } from 'src/plugins/data/public';
import { InfraSource } from '../../../../common/http_api/source_api';
import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
import { Color } from '../../../../common/color_palette';
import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api';
import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart';
@ -35,7 +35,7 @@ import { ThresholdAnnotations } from '../../common/criterion_preview_chart/thres
interface Props {
expression: MetricExpression;
derivedIndexPattern: IIndexPattern;
source: InfraSource | null;
source: MetricsSourceConfiguration | null;
filterQuery?: string;
groupBy?: string | string[];
}

View file

@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { Comparator } from '../../../../server/lib/alerting/metric_threshold/types';
jest.mock('../../../containers/source/use_source_via_http', () => ({
jest.mock('../../../containers/metrics_source/use_source_via_http', () => ({
useSourceViaHttp: () => ({
source: { id: 'default' },
createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }),

View file

@ -7,7 +7,7 @@
import { IIndexPattern } from 'src/plugins/data/public';
import { useMemo } from 'react';
import { InfraSource } from '../../../../common/http_api/source_api';
import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
import { MetricExpression } from '../types';
import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data';
@ -15,7 +15,7 @@ import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/
export const useMetricsExplorerChartData = (
expression: MetricExpression,
derivedIndexPattern: IIndexPattern,
source: InfraSource | null,
source: MetricsSourceConfiguration | null,
filterQuery?: string,
groupBy?: string | string[]
) => {

View file

@ -1,10 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export * from './input_fields';
export { SourceConfigurationSettings } from './source_configuration_settings';
export { ViewSourceConfigurationButton } from './view_source_configuration_button';

View file

@ -1,156 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
LogColumnConfiguration,
isTimestampLogColumnConfiguration,
isMessageLogColumnConfiguration,
TimestampLogColumnConfiguration,
MessageLogColumnConfiguration,
FieldLogColumnConfiguration,
} from '../../utils/source_configuration';
export interface TimestampLogColumnConfigurationProps {
logColumnConfiguration: TimestampLogColumnConfiguration['timestampColumn'];
remove: () => void;
type: 'timestamp';
}
export interface MessageLogColumnConfigurationProps {
logColumnConfiguration: MessageLogColumnConfiguration['messageColumn'];
remove: () => void;
type: 'message';
}
export interface FieldLogColumnConfigurationProps {
logColumnConfiguration: FieldLogColumnConfiguration['fieldColumn'];
remove: () => void;
type: 'field';
}
export type LogColumnConfigurationProps =
| TimestampLogColumnConfigurationProps
| MessageLogColumnConfigurationProps
| FieldLogColumnConfigurationProps;
interface FormState {
logColumns: LogColumnConfiguration[];
}
type FormStateChanges = Partial<FormState>;
export const useLogColumnsConfigurationFormState = ({
initialFormState = defaultFormState,
}: {
initialFormState?: FormState;
}) => {
const [formStateChanges, setFormStateChanges] = useState<FormStateChanges>({});
const resetForm = useCallback(() => setFormStateChanges({}), []);
const formState = useMemo(
() => ({
...initialFormState,
...formStateChanges,
}),
[initialFormState, formStateChanges]
);
const logColumnConfigurationProps = useMemo<LogColumnConfigurationProps[]>(
() =>
formState.logColumns.map(
(logColumn): LogColumnConfigurationProps => {
const remove = () =>
setFormStateChanges((changes) => ({
...changes,
logColumns: formState.logColumns.filter((item) => item !== logColumn),
}));
if (isTimestampLogColumnConfiguration(logColumn)) {
return {
logColumnConfiguration: logColumn.timestampColumn,
remove,
type: 'timestamp',
};
} else if (isMessageLogColumnConfiguration(logColumn)) {
return {
logColumnConfiguration: logColumn.messageColumn,
remove,
type: 'message',
};
} else {
return {
logColumnConfiguration: logColumn.fieldColumn,
remove,
type: 'field',
};
}
}
),
[formState.logColumns]
);
const addLogColumn = useCallback(
(logColumnConfiguration: LogColumnConfiguration) =>
setFormStateChanges((changes) => ({
...changes,
logColumns: [...formState.logColumns, logColumnConfiguration],
})),
[formState.logColumns]
);
const moveLogColumn = useCallback(
(sourceIndex, destinationIndex) => {
if (destinationIndex >= 0 && sourceIndex <= formState.logColumns.length - 1) {
const newLogColumns = [...formState.logColumns];
newLogColumns.splice(destinationIndex, 0, newLogColumns.splice(sourceIndex, 1)[0]);
setFormStateChanges((changes) => ({
...changes,
logColumns: newLogColumns,
}));
}
},
[formState.logColumns]
);
const errors = useMemo(
() =>
logColumnConfigurationProps.length <= 0
? [
<FormattedMessage
id="xpack.infra.sourceConfiguration.logColumnListEmptyErrorMessage"
defaultMessage="The log column list must not be empty."
/>,
]
: [],
[logColumnConfigurationProps]
);
const isFormValid = useMemo(() => (errors.length <= 0 ? true : false), [errors]);
const isFormDirty = useMemo(() => Object.keys(formStateChanges).length > 0, [formStateChanges]);
return {
addLogColumn,
moveLogColumn,
errors,
logColumnConfigurationProps,
formState,
formStateChanges,
isFormDirty,
isFormValid,
resetForm,
};
};
const defaultFormState: FormState = {
logColumns: [],
};

View file

@ -9,27 +9,25 @@ import createContainer from 'constate';
import { useEffect, useMemo, useState } from 'react';
import {
InfraSavedSourceConfiguration,
InfraSource,
SourceResponse,
} from '../../../common/http_api/source_api';
MetricsSourceConfigurationResponse,
MetricsSourceConfiguration,
PartialMetricsSourceConfigurationProperties,
} from '../../../common/metrics_sources';
import { useTrackedPromise } from '../../utils/use_tracked_promise';
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
export const pickIndexPattern = (
source: InfraSource | undefined,
type: 'logs' | 'metrics' | 'both'
source: MetricsSourceConfiguration | undefined,
type: 'metrics'
) => {
if (!source) {
return 'unknown-index';
}
if (type === 'logs') {
return source.configuration.logAlias;
}
if (type === 'metrics') {
return source.configuration.metricAlias;
}
return `${source.configuration.logAlias},${source.configuration.metricAlias}`;
return `${source.configuration.metricAlias}`;
};
const DEPENDENCY_ERROR_MESSAGE = 'Failed to load source: No fetch client available.';
@ -39,7 +37,7 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
const fetchService = kibana.services.http?.fetch;
const API_URL = `/api/metrics/source/${sourceId}`;
const [source, setSource] = useState<InfraSource | undefined>(undefined);
const [source, setSource] = useState<MetricsSourceConfiguration | undefined>(undefined);
const [loadSourceRequest, loadSource] = useTrackedPromise(
{
@ -49,7 +47,7 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
throw new Error(DEPENDENCY_ERROR_MESSAGE);
}
return await fetchService<SourceResponse>(`${API_URL}/metrics`, {
return await fetchService<MetricsSourceConfigurationResponse>(`${API_URL}`, {
method: 'GET',
});
},
@ -62,12 +60,12 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
const [createSourceConfigurationRequest, createSourceConfiguration] = useTrackedPromise(
{
createPromise: async (sourceProperties: InfraSavedSourceConfiguration) => {
createPromise: async (sourceProperties: PartialMetricsSourceConfigurationProperties) => {
if (!fetchService) {
throw new Error(DEPENDENCY_ERROR_MESSAGE);
}
return await fetchService<SourceResponse>(API_URL, {
return await fetchService<MetricsSourceConfigurationResponse>(API_URL, {
method: 'PATCH',
body: JSON.stringify(sourceProperties),
});
@ -83,12 +81,12 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
const [updateSourceConfigurationRequest, updateSourceConfiguration] = useTrackedPromise(
{
createPromise: async (sourceProperties: InfraSavedSourceConfiguration) => {
createPromise: async (sourceProperties: PartialMetricsSourceConfigurationProperties) => {
if (!fetchService) {
throw new Error(DEPENDENCY_ERROR_MESSAGE);
}
return await fetchService<SourceResponse>(API_URL, {
return await fetchService<MetricsSourceConfigurationResponse>(API_URL, {
method: 'PATCH',
body: JSON.stringify(sourceProperties),
});
@ -102,7 +100,7 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
[fetchService, sourceId]
);
const createDerivedIndexPattern = (type: 'logs' | 'metrics' | 'both') => {
const createDerivedIndexPattern = (type: 'metrics') => {
return {
fields: source?.status ? source.status.indexFields : [],
title: pickIndexPattern(source, type),
@ -129,9 +127,6 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
const sourceExists = useMemo(() => (source ? !!source.version : undefined), [source]);
const logIndicesExist = useMemo(() => source && source.status && source.status.logIndicesExist, [
source,
]);
const metricIndicesExist = useMemo(
() => source && source.status && source.status.metricIndicesExist,
[source]
@ -144,7 +139,6 @@ export const useSource = ({ sourceId }: { sourceId: string }) => {
return {
createSourceConfiguration,
createDerivedIndexPattern,
logIndicesExist,
isLoading,
isLoadingSource: loadSourceRequest.state === 'pending',
isUninitialized,

View file

@ -13,51 +13,47 @@ import createContainer from 'constate';
import { HttpHandler } from 'src/core/public';
import { ToastInput } from 'src/core/public';
import {
SourceResponseRuntimeType,
SourceResponse,
InfraSource,
} from '../../../common/http_api/source_api';
metricsSourceConfigurationResponseRT,
MetricsSourceConfigurationResponse,
MetricsSourceConfiguration,
} from '../../../common/metrics_sources';
import { useHTTPRequest } from '../../hooks/use_http_request';
import { throwErrors, createPlainError } from '../../../common/runtime_types';
export const pickIndexPattern = (
source: InfraSource | undefined,
type: 'logs' | 'metrics' | 'both'
source: MetricsSourceConfiguration | undefined,
type: 'metrics'
) => {
if (!source) {
return 'unknown-index';
}
if (type === 'logs') {
return source.configuration.logAlias;
}
if (type === 'metrics') {
return source.configuration.metricAlias;
}
return `${source.configuration.logAlias},${source.configuration.metricAlias}`;
return `${source.configuration.metricAlias}`;
};
interface Props {
sourceId: string;
type: 'logs' | 'metrics' | 'both';
fetch?: HttpHandler;
toastWarning?: (input: ToastInput) => void;
}
export const useSourceViaHttp = ({
sourceId = 'default',
type = 'both',
fetch,
toastWarning,
}: Props) => {
export const useSourceViaHttp = ({ sourceId = 'default', fetch, toastWarning }: Props) => {
const decodeResponse = (response: any) => {
return pipe(
SourceResponseRuntimeType.decode(response),
metricsSourceConfigurationResponseRT.decode(response),
fold(throwErrors(createPlainError), identity)
);
};
const { error, loading, response, makeRequest } = useHTTPRequest<SourceResponse>(
`/api/metrics/source/${sourceId}/${type}`,
const {
error,
loading,
response,
makeRequest,
} = useHTTPRequest<MetricsSourceConfigurationResponse>(
`/api/metrics/source/${sourceId}`,
'GET',
null,
decodeResponse,
@ -71,15 +67,12 @@ export const useSourceViaHttp = ({
})();
}, [makeRequest]);
const createDerivedIndexPattern = useCallback(
(indexType: 'logs' | 'metrics' | 'both' = type) => {
return {
fields: response?.source.status ? response.source.status.indexFields : [],
title: pickIndexPattern(response?.source, indexType),
};
},
[response, type]
);
const createDerivedIndexPattern = useCallback(() => {
return {
fields: response?.source.status ? response.source.status.indexFields : [],
title: pickIndexPattern(response?.source, 'metrics'),
};
}, [response]);
const source = useMemo(() => {
return response ? response.source : null;

View file

@ -17,10 +17,10 @@ import { useUrlState } from '../../utils/use_url_state';
import { useFindSavedObject } from '../../hooks/use_find_saved_object';
import { useCreateSavedObject } from '../../hooks/use_create_saved_object';
import { useDeleteSavedObject } from '../../hooks/use_delete_saved_object';
import { Source } from '../source';
import { Source } from '../metrics_source';
import { metricsExplorerViewSavedObjectName } from '../../../common/saved_objects/metrics_explorer_view';
import { inventoryViewSavedObjectName } from '../../../common/saved_objects/inventory_view';
import { useSourceConfigurationFormState } from '../../components/source_configuration/source_configuration_form_state';
import { useSourceConfigurationFormState } from '../../pages/metrics/settings/source_configuration_form_state';
import { useGetSavedObject } from '../../hooks/use_get_saved_object';
import { useUpdateSavedObject } from '../../hooks/use_update_saved_object';

View file

@ -9,17 +9,19 @@ import React, { useContext } from 'react';
import { IIndexPattern } from 'src/plugins/data/public';
import {
InfraSavedSourceConfiguration,
InfraSourceConfiguration,
} from '../../../common/http_api/source_api';
MetricsSourceConfigurationProperties,
PartialMetricsSourceConfigurationProperties,
} from '../../../common/metrics_sources';
import { RendererFunction } from '../../utils/typed_react';
import { Source } from '../source';
import { Source } from '../metrics_source';
interface WithSourceProps {
children: RendererFunction<{
configuration?: InfraSourceConfiguration;
create: (sourceProperties: InfraSavedSourceConfiguration) => Promise<any> | undefined;
createDerivedIndexPattern: (type: 'logs' | 'metrics' | 'both') => IIndexPattern;
configuration?: MetricsSourceConfigurationProperties;
create: (
sourceProperties: PartialMetricsSourceConfigurationProperties
) => Promise<any> | undefined;
createDerivedIndexPattern: (type: 'metrics') => IIndexPattern;
exists?: boolean;
hasFailed: boolean;
isLoading: boolean;
@ -29,7 +31,9 @@ interface WithSourceProps {
metricAlias?: string;
metricIndicesExist?: boolean;
sourceId: string;
update: (sourceProperties: InfraSavedSourceConfiguration) => Promise<any> | undefined;
update: (
sourceProperties: PartialMetricsSourceConfigurationProperties
) => Promise<any> | undefined;
version?: string;
}>;
}
@ -42,7 +46,6 @@ export const WithSource: React.FunctionComponent<WithSourceProps> = ({ children
sourceExists,
sourceId,
metricIndicesExist,
logIndicesExist,
isLoading,
loadSource,
hasFailedLoadingSource,
@ -60,7 +63,6 @@ export const WithSource: React.FunctionComponent<WithSourceProps> = ({ children
isLoading,
lastFailureMessage: loadSourceFailureMessage,
load: loadSource,
logIndicesExist,
metricIndicesExist,
sourceId,
update: updateSourceConfiguration,

View file

@ -14,7 +14,7 @@ import {
SnapshotNodeMetric,
SnapshotNodePath,
} from '../../common/http_api/snapshot_api';
import { InfraSourceConfigurationFields } from '../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../common/metrics_sources';
import { WaffleSortOption } from '../pages/metrics/inventory_view/hooks/use_waffle_options';
export interface InfraWaffleMapNode {
@ -124,7 +124,7 @@ export enum InfraWaffleMapRuleOperator {
}
export interface InfraWaffleMapOptions {
fields?: InfraSourceConfigurationFields | null;
fields?: MetricsSourceConfigurationProperties['fields'] | null;
formatter: InfraFormatterType;
formatTemplate: string;
metric: SnapshotMetricInput;

View file

@ -14,9 +14,7 @@ export const createMetricsHasData = (
) => async () => {
const [coreServices] = await getStartServices();
const { http } = coreServices;
const results = await http.get<{ hasData: boolean }>(
'/api/metrics/source/default/metrics/hasData'
);
const results = await http.get<{ hasData: boolean }>('/api/metrics/source/default/hasData');
return results.hasData;
};

View file

@ -14,7 +14,7 @@ import { useHostIpToName } from './use_host_ip_to_name';
import { getFromFromLocation, getToFromLocation } from './query_params';
import { LoadingPage } from '../../components/loading_page';
import { Error } from '../error';
import { useSource } from '../../containers/source/source';
import { useSourceViaHttp } from '../../containers/metrics_source/use_source_via_http';
type RedirectToHostDetailType = RouteComponentProps<{
hostIp: string;
@ -26,7 +26,7 @@ export const RedirectToHostDetailViaIP = ({
},
location,
}: RedirectToHostDetailType) => {
const { source } = useSource({ sourceId: 'default' });
const { source } = useSourceViaHttp({ sourceId: 'default' });
const { error, name } = useHostIpToName(
hostIp,

View file

@ -19,7 +19,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { InputFieldProps } from '../../../components/source_configuration';
import { InputFieldProps } from '../../../components/source_configuration/input_fields';
interface FieldsConfigurationPanelProps {
isLoading: boolean;

View file

@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { InputFieldProps } from '../../../components/source_configuration';
import { InputFieldProps } from '../../../components/source_configuration/input_fields';
interface IndicesConfigurationPanelProps {
isLoading: boolean;

View file

@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { NoIndices } from '../../../components/empty_states/no_indices';
import { ViewSourceConfigurationButton } from '../../../components/source_configuration';
import { ViewSourceConfigurationButton } from '../../../components/source_configuration/view_source_configuration_button';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { useLinkProps } from '../../../hooks/use_link_props';

View file

@ -12,7 +12,7 @@ import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { EuiErrorBoundary, EuiFlexItem, EuiFlexGroup, EuiButtonEmpty } from '@elastic/eui';
import { IIndexPattern } from 'src/plugins/data/common';
import { InfraSourceConfiguration } from '../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources';
import { DocumentTitle } from '../../components/document_title';
import { HelpCenterContent } from '../../components/help_center_content';
import { RoutedTabs } from '../../components/navigation/routed_tabs';
@ -24,7 +24,7 @@ import {
} from './metrics_explorer/hooks/use_metrics_explorer_options';
import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state';
import { WithSource } from '../../containers/with_source';
import { Source } from '../../containers/source';
import { Source } from '../../containers/metrics_source';
import { MetricsExplorerPage } from './metrics_explorer';
import { SnapshotPage } from './inventory_view';
import { MetricsSettingsPage } from './settings';
@ -188,8 +188,8 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
};
const PageContent = (props: {
configuration: InfraSourceConfiguration;
createDerivedIndexPattern: (type: 'logs' | 'metrics' | 'both') => IIndexPattern;
configuration: MetricsSourceConfigurationProperties;
createDerivedIndexPattern: (type: 'metrics') => IIndexPattern;
}) => {
const { createDerivedIndexPattern, configuration } = props;
const { options } = useContext(MetricsExplorerOptionsContainer.Context);

View file

@ -18,7 +18,7 @@ import { useSnapshot } from '../hooks/use_snaphot';
import { useWaffleTimeContext } from '../hooks/use_waffle_time';
import { useWaffleFiltersContext } from '../hooks/use_waffle_filters';
import { DEFAULT_LEGEND, useWaffleOptionsContext } from '../hooks/use_waffle_options';
import { useSourceContext } from '../../../../containers/source';
import { useSourceContext } from '../../../../containers/metrics_source';
import { InfraFormatterType } from '../../../../lib/lib';
import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
import { Toolbar } from './toolbars/toolbar';

View file

@ -43,7 +43,7 @@ import {
import { PaginationControls } from './pagination';
import { AnomalySummary } from './annomaly_summary';
import { AnomalySeverityIndicator } from '../../../../../../../components/logging/log_analysis_results/anomaly_severity_indicator';
import { useSourceContext } from '../../../../../../../containers/source';
import { useSourceContext } from '../../../../../../../containers/metrics_source';
import { createResultsUrl } from '../flyout_home';
import { useWaffleViewState, WaffleViewState } from '../../../../hooks/use_waffle_view_state';
type JobType = 'k8s' | 'hosts';

View file

@ -13,7 +13,7 @@ import { JobSetupScreen } from './job_setup_screen';
import { useInfraMLCapabilities } from '../../../../../../containers/ml/infra_ml_capabilities';
import { MetricHostsModuleProvider } from '../../../../../../containers/ml/modules/metrics_hosts/module';
import { MetricK8sModuleProvider } from '../../../../../../containers/ml/modules/metrics_k8s/module';
import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http';
import { useSourceViaHttp } from '../../../../../../containers/metrics_source/use_source_via_http';
import { useActiveKibanaSpace } from '../../../../../../hooks/use_kibana_space';
export const AnomalyDetectionFlyout = () => {
@ -23,7 +23,6 @@ export const AnomalyDetectionFlyout = () => {
const [screenParams, setScreenParams] = useState<any | null>(null);
const { source } = useSourceViaHttp({
sourceId: 'default',
type: 'metrics',
});
const { space } = useActiveKibanaSpace();

View file

@ -17,7 +17,7 @@ import moment, { Moment } from 'moment';
import { EuiComboBox } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiLoadingSpinner } from '@elastic/eui';
import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http';
import { useSourceViaHttp } from '../../../../../../containers/metrics_source/use_source_via_http';
import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module';
import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module';
import { FixedDatePicker } from '../../../../../../components/fixed_datepicker';
@ -42,7 +42,6 @@ export const JobSetupScreen = (props: Props) => {
const [filterQuery, setFilterQuery] = useState<string>('');
const { createDerivedIndexPattern } = useSourceViaHttp({
sourceId: 'default',
type: 'metrics',
});
const indicies = h.sourceConfiguration.indices;
@ -79,7 +78,7 @@ export const JobSetupScreen = (props: Props) => {
}
}, [props.jobType, k.jobSummaries, h.jobSummaries]);
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [
const derivedIndexPattern = useMemo(() => createDerivedIndexPattern(), [
createDerivedIndexPattern,
]);

View file

@ -23,7 +23,7 @@ import { EuiLoadingChart, EuiSpacer, EuiFlexGrid, EuiFlexItem } from '@elastic/e
import { TabContent, TabProps } from '../shared';
import { useSnapshot } from '../../../../hooks/use_snaphot';
import { useWaffleOptionsContext } from '../../../../hooks/use_waffle_options';
import { useSourceContext } from '../../../../../../../containers/source';
import { useSourceContext } from '../../../../../../../containers/metrics_source';
import { findInventoryFields } from '../../../../../../../../common/inventory_models';
import { convertKueryToElasticSearchQuery } from '../../../../../../../utils/kuery';
import { SnapshotMetricType } from '../../../../../../../../common/inventory_models/types';

View file

@ -9,7 +9,7 @@ import React, { useCallback, useContext, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiLoadingChart } from '@elastic/eui';
import { TabContent, TabProps } from '../shared';
import { Source } from '../../../../../../../containers/source';
import { Source } from '../../../../../../../containers/metrics_source';
import { findInventoryModel } from '../../../../../../../../common/inventory_models';
import { InventoryItemType } from '../../../../../../../../common/inventory_models/types';
import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata';

View file

@ -7,7 +7,7 @@
import React, { useContext } from 'react';
import { i18n } from '@kbn/i18n';
import { Source } from '../../../../containers/source';
import { Source } from '../../../../containers/metrics_source';
import { AutocompleteField } from '../../../../components/autocomplete_field';
import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion';
import { useWaffleFiltersContext } from '../hooks/use_waffle_filters';

View file

@ -29,7 +29,7 @@ import { useUiSetting } from '../../../../../../../../../src/plugins/kibana_reac
import { toMetricOpt } from '../../../../../../common/snapshot_metric_i18n';
import { MetricsExplorerAggregation } from '../../../../../../common/http_api';
import { colorTransformer, Color } from '../../../../../../common/color_palette';
import { useSourceContext } from '../../../../../containers/source';
import { useSourceContext } from '../../../../../containers/metrics_source';
import { useTimeline } from '../../hooks/use_timeline';
import { useWaffleOptionsContext } from '../../hooks/use_waffle_options';
import { useWaffleTimeContext } from '../../hooks/use_waffle_time';

View file

@ -7,7 +7,7 @@
import React, { FunctionComponent } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import { useSourceContext } from '../../../../../containers/source';
import { useSourceContext } from '../../../../../containers/metrics_source';
import {
SnapshotMetricInput,
SnapshotGroupBy,
@ -24,7 +24,7 @@ import { WaffleOptionsState, WaffleSortOption } from '../../hooks/use_waffle_opt
import { useInventoryMeta } from '../../hooks/use_inventory_meta';
export interface ToolbarProps extends Omit<WaffleOptionsState, 'boundsOverride' | 'autoBounds'> {
createDerivedIndexPattern: (type: 'logs' | 'metrics' | 'both') => IIndexPattern;
createDerivedIndexPattern: (type: 'metrics') => IIndexPattern;
changeMetric: (payload: SnapshotMetricInput) => void;
changeGroupBy: (payload: SnapshotGroupBy) => void;
changeCustomOptions: (payload: InfraGroupByOptions[]) => void;

View file

@ -8,7 +8,7 @@
import React from 'react';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { fieldToName } from '../../lib/field_to_display_name';
import { useSourceContext } from '../../../../../containers/source';
import { useSourceContext } from '../../../../../containers/metrics_source';
import { useWaffleOptionsContext } from '../../hooks/use_waffle_options';
import { WaffleInventorySwitcher } from '../waffle/waffle_inventory_switcher';
import { ToolbarProps } from './toolbar';

View file

@ -17,7 +17,7 @@ import {
InfraFormatterType,
} from '../../../../../lib/lib';
jest.mock('../../../../../containers/source', () => ({
jest.mock('../../../../../containers/metrics_source', () => ({
useSourceContext: () => ({ sourceId: 'default' }),
}));

View file

@ -11,7 +11,7 @@ import { first } from 'lodash';
import { getCustomMetricLabel } from '../../../../../../common/formatters/get_custom_metric_label';
import { SnapshotCustomMetricInput } from '../../../../../../common/http_api';
import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kibana_react/common';
import { useSourceContext } from '../../../../../containers/source';
import { useSourceContext } from '../../../../../containers/metrics_source';
import { findInventoryModel } from '../../../../../../common/inventory_models';
import {
InventoryItemType,

View file

@ -13,7 +13,7 @@ import { useEffect, useState } from 'react';
import { ProcessListAPIResponse, ProcessListAPIResponseRT } from '../../../../../common/http_api';
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
import { useHTTPRequest } from '../../../../hooks/use_http_request';
import { useSourceContext } from '../../../../containers/source';
import { useSourceContext } from '../../../../containers/metrics_source';
export interface SortBy {
name: string;

View file

@ -17,7 +17,7 @@ jest.mock('react-router-dom', () => ({
}),
}));
jest.mock('../../../../containers/source', () => ({
jest.mock('../../../../containers/metrics_source', () => ({
useSourceContext: () => ({
createDerivedIndexPattern: () => 'jestbeat-*',
}),

View file

@ -13,7 +13,7 @@ import { constant, identity } from 'fp-ts/lib/function';
import createContainter from 'constate';
import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill';
import { useUrlState } from '../../../../utils/use_url_state';
import { useSourceContext } from '../../../../containers/source';
import { useSourceContext } from '../../../../containers/metrics_source';
import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery';
import { esKuery } from '../../../../../../../../src/plugins/data/public';

View file

@ -17,8 +17,8 @@ import { ColumnarPage } from '../../../components/page';
import { SourceErrorPage } from '../../../components/source_error_page';
import { SourceLoadingPage } from '../../../components/source_loading_page';
import { ViewSourceConfigurationButton } from '../../../components/source_configuration';
import { Source } from '../../../containers/source';
import { ViewSourceConfigurationButton } from '../../../components/source_configuration/view_source_configuration_button';
import { Source } from '../../../containers/metrics_source';
import { useTrackPageview } from '../../../../../observability/public';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { Layout } from './components/layout';

View file

@ -14,7 +14,6 @@ const options: InfraWaffleMapOptions = {
container: 'container.id',
pod: 'kubernetes.pod.uid',
host: 'host.name',
message: ['@message'],
timestamp: '@timestanp',
tiebreaker: '@timestamp',
},

View file

@ -9,7 +9,7 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/e
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
import { ViewSourceConfigurationButton } from '../../../../components/source_configuration';
import { ViewSourceConfigurationButton } from '../../../../components/source_configuration/view_source_configuration_button';
import { useLinkProps } from '../../../../hooks/use_link_props';
interface InvalidNodeErrorProps {

View file

@ -17,7 +17,7 @@ import { Header } from '../../../components/header';
import { ColumnarPage, PageContent } from '../../../components/page';
import { withMetricPageProviders } from './page_providers';
import { useMetadata } from './hooks/use_metadata';
import { Source } from '../../../containers/source';
import { Source } from '../../../containers/metrics_source';
import { InfraLoadingPanel } from '../../../components/loading';
import { findInventoryModel } from '../../../../common/inventory_models';
import { NavItem } from './lib/side_nav_context';

View file

@ -7,7 +7,7 @@
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { Source } from '../../../containers/source';
import { Source } from '../../../containers/metrics_source';
import { MetricsTimeProvider } from './hooks/use_metrics_time';
export const withMetricPageProviders = <T extends object>(Component: React.ComponentType<T>) => (

View file

@ -19,7 +19,7 @@ import {
} from '@elastic/charts';
import { first, last } from 'lodash';
import moment from 'moment';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer';
import {
MetricsExplorerOptions,
@ -47,7 +47,7 @@ interface Props {
options: MetricsExplorerOptions;
chartOptions: MetricsExplorerChartOptions;
series: MetricsExplorerSeries;
source: InfraSourceConfiguration | undefined;
source: MetricsSourceConfigurationProperties | undefined;
timeRange: MetricsExplorerTimeOptions;
onTimeChange: (start: string, end: string) => void;
}

View file

@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import DateMath from '@elastic/datemath';
import { Capabilities } from 'src/core/public';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
import { AlertFlyout } from '../../../../alerting/metric_threshold/components/alert_flyout';
import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer';
import {
@ -33,14 +33,14 @@ export interface Props {
options: MetricsExplorerOptions;
onFilter?: (query: string) => void;
series: MetricsExplorerSeries;
source?: InfraSourceConfiguration;
source?: MetricsSourceConfigurationProperties;
timeRange: MetricsExplorerTimeOptions;
uiCapabilities?: Capabilities;
chartOptions: MetricsExplorerChartOptions;
}
const fieldToNodeType = (
source: InfraSourceConfiguration,
source: MetricsSourceConfigurationProperties,
groupBy: string | string[]
): InventoryItemType | undefined => {
const fields = Array.isArray(groupBy) ? groupBy : [groupBy];

View file

@ -9,7 +9,7 @@ import { EuiButton, EuiFlexGrid, EuiFlexItem, EuiText, EuiHorizontalRule } from
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
import { MetricsExplorerResponse } from '../../../../../common/http_api/metrics_explorer';
import {
MetricsExplorerOptions,
@ -31,7 +31,7 @@ interface Props {
onFilter: (filter: string) => void;
onTimeChange: (start: string, end: string) => void;
data: MetricsExplorerResponse | null;
source: InfraSourceConfiguration | undefined;
source: MetricsSourceConfigurationProperties | undefined;
timeRange: MetricsExplorerTimeOptions;
}
export const MetricsExplorerCharts = ({

View file

@ -8,7 +8,7 @@
import { encode } from 'rison-node';
import uuid from 'uuid';
import { set } from '@elastic/safer-lodash-set';
import { InfraSourceConfiguration } from '../../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../../common/metrics_sources';
import { colorTransformer, Color } from '../../../../../../common/color_palette';
import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer';
import {
@ -143,7 +143,7 @@ const createTSVBIndexPattern = (alias: string) => {
};
export const createTSVBLink = (
source: InfraSourceConfiguration | undefined,
source: MetricsSourceConfigurationProperties | undefined,
options: MetricsExplorerOptions,
series: MetricsExplorerSeries,
timeRange: MetricsExplorerTimeOptions,

View file

@ -7,7 +7,7 @@
import { useState, useCallback, useContext } from 'react';
import { IIndexPattern } from 'src/plugins/data/public';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
import {
MetricsExplorerMetric,
MetricsExplorerAggregation,
@ -28,7 +28,7 @@ export interface MetricExplorerViewState {
}
export const useMetricsExplorerState = (
source: InfraSourceConfiguration,
source: MetricsSourceConfigurationProperties,
derivedIndexPattern: IIndexPattern,
shouldLoadImmediately = true
) => {

View file

@ -22,7 +22,7 @@ import {
import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/public';
import { HttpHandler } from 'kibana/public';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
const mockedFetch = jest.fn();
@ -38,7 +38,7 @@ const renderUseMetricsExplorerDataHook = () => {
return renderHook(
(props: {
options: MetricsExplorerOptions;
source: InfraSourceConfiguration | undefined;
source: MetricsSourceConfigurationProperties | undefined;
derivedIndexPattern: IIndexPattern;
timeRange: MetricsExplorerTimeOptions;
afterKey: string | null | Record<string, string | null>;

View file

@ -9,7 +9,7 @@ import DateMath from '@elastic/datemath';
import { isEqual } from 'lodash';
import { useEffect, useState, useCallback } from 'react';
import { IIndexPattern } from 'src/plugins/data/public';
import { InfraSourceConfiguration } from '../../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
import {
MetricsExplorerResponse,
metricsExplorerResponseRT,
@ -25,7 +25,7 @@ function isSameOptions(current: MetricsExplorerOptions, next: MetricsExplorerOpt
export function useMetricsExplorerData(
options: MetricsExplorerOptions,
source: InfraSourceConfiguration | undefined,
source: MetricsSourceConfigurationProperties | undefined,
derivedIndexPattern: IIndexPattern,
timerange: MetricsExplorerTimeOptions,
afterKey: string | null | Record<string, string | null>,

View file

@ -9,7 +9,7 @@ import { EuiErrorBoundary } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect } from 'react';
import { IIndexPattern } from 'src/plugins/data/public';
import { InfraSourceConfiguration } from '../../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources';
import { useTrackPageview } from '../../../../../observability/public';
import { DocumentTitle } from '../../../components/document_title';
import { NoData } from '../../../components/empty_states';
@ -19,7 +19,7 @@ import { useMetricsExplorerState } from './hooks/use_metric_explorer_state';
import { useSavedViewContext } from '../../../containers/saved_view/saved_view';
interface MetricsExplorerPageProps {
source: InfraSourceConfiguration;
source: MetricsSourceConfigurationProperties;
derivedIndexPattern: IIndexPattern;
}

View file

@ -8,7 +8,7 @@
import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { SourceConfigurationSettings } from '../../components/source_configuration/source_configuration_settings';
import { SourceConfigurationSettings } from './settings/source_configuration_settings';
export const MetricsSettingsPage = () => {
const uiCapabilities = useKibana().services.application?.capabilities;

View file

@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { InputFieldProps } from './input_fields';
import { InputFieldProps } from '../../../components/source_configuration/input_fields';
interface FieldsConfigurationPanelProps {
containerFieldProps: InputFieldProps;

View file

@ -11,16 +11,14 @@ import {
createInputFieldProps,
createInputRangeFieldProps,
validateInputFieldNotEmpty,
} from './input_fields';
} from '../../../components/source_configuration/input_fields';
interface FormState {
name: string;
description: string;
metricAlias: string;
logAlias: string;
containerField: string;
hostField: string;
messageField: string[];
podField: string;
tiebreakerField: string;
timestampField: string;
@ -56,16 +54,6 @@ export const useIndicesConfigurationFormState = ({
}),
[formState.name]
);
const logAliasFieldProps = useMemo(
() =>
createInputFieldProps({
errors: validateInputFieldNotEmpty(formState.logAlias),
name: 'logAlias',
onChange: (logAlias) => setFormStateChanges((changes) => ({ ...changes, logAlias })),
value: formState.logAlias,
}),
[formState.logAlias]
);
const metricAliasFieldProps = useMemo(
() =>
createInputFieldProps({
@ -144,7 +132,6 @@ export const useIndicesConfigurationFormState = ({
const fieldProps = useMemo(
() => ({
name: nameFieldProps,
logAlias: logAliasFieldProps,
metricAlias: metricAliasFieldProps,
containerField: containerFieldFieldProps,
hostField: hostFieldFieldProps,
@ -155,7 +142,6 @@ export const useIndicesConfigurationFormState = ({
}),
[
nameFieldProps,
logAliasFieldProps,
metricAliasFieldProps,
containerFieldFieldProps,
hostFieldFieldProps,
@ -193,11 +179,9 @@ export const useIndicesConfigurationFormState = ({
const defaultFormState: FormState = {
name: '',
description: '',
logAlias: '',
metricAlias: '',
containerField: '',
hostField: '',
messageField: [],
podField: '',
tiebreakerField: '',
timestampField: '',

View file

@ -17,8 +17,8 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { METRICS_INDEX_PATTERN } from '../../../common/constants';
import { InputFieldProps } from './input_fields';
import { METRICS_INDEX_PATTERN } from '../../../../common/constants';
import { InputFieldProps } from '../../../components/source_configuration/input_fields';
interface IndicesConfigurationPanelProps {
isLoading: boolean;

View file

@ -13,7 +13,7 @@ import { EuiDescribedFormGroup } from '@elastic/eui';
import { EuiForm } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { InputRangeFieldProps } from './input_fields';
import { InputRangeFieldProps } from '../../../components/source_configuration/input_fields';
interface MLConfigurationPanelProps {
isLoading: boolean;

View file

@ -6,12 +6,12 @@
*/
import { useCallback, useMemo } from 'react';
import { InfraSourceConfiguration } from '../../../common/http_api/source_api';
import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources';
import { useIndicesConfigurationFormState } from './indices_configuration_form_state';
import { useLogColumnsConfigurationFormState } from './log_columns_configuration_form_state';
export const useSourceConfigurationFormState = (configuration?: InfraSourceConfiguration) => {
export const useSourceConfigurationFormState = (
configuration?: MetricsSourceConfigurationProperties
) => {
const indicesConfigurationFormState = useIndicesConfigurationFormState({
initialFormState: useMemo(
() =>
@ -19,11 +19,9 @@ export const useSourceConfigurationFormState = (configuration?: InfraSourceConfi
? {
name: configuration.name,
description: configuration.description,
logAlias: configuration.logAlias,
metricAlias: configuration.metricAlias,
containerField: configuration.fields.container,
hostField: configuration.fields.host,
messageField: configuration.fields.message,
podField: configuration.fields.pod,
tiebreakerField: configuration.fields.tiebreaker,
timestampField: configuration.fields.timestamp,
@ -34,43 +32,26 @@ export const useSourceConfigurationFormState = (configuration?: InfraSourceConfi
),
});
const logColumnsConfigurationFormState = useLogColumnsConfigurationFormState({
initialFormState: useMemo(
() =>
configuration
? {
logColumns: configuration.logColumns,
}
: undefined,
[configuration]
),
});
const errors = useMemo(
() => [...indicesConfigurationFormState.errors, ...logColumnsConfigurationFormState.errors],
[indicesConfigurationFormState.errors, logColumnsConfigurationFormState.errors]
);
const errors = useMemo(() => [...indicesConfigurationFormState.errors], [
indicesConfigurationFormState.errors,
]);
const resetForm = useCallback(() => {
indicesConfigurationFormState.resetForm();
logColumnsConfigurationFormState.resetForm();
}, [indicesConfigurationFormState, logColumnsConfigurationFormState]);
}, [indicesConfigurationFormState]);
const isFormDirty = useMemo(
() => indicesConfigurationFormState.isFormDirty || logColumnsConfigurationFormState.isFormDirty,
[indicesConfigurationFormState.isFormDirty, logColumnsConfigurationFormState.isFormDirty]
);
const isFormDirty = useMemo(() => indicesConfigurationFormState.isFormDirty, [
indicesConfigurationFormState.isFormDirty,
]);
const isFormValid = useMemo(
() => indicesConfigurationFormState.isFormValid && logColumnsConfigurationFormState.isFormValid,
[indicesConfigurationFormState.isFormValid, logColumnsConfigurationFormState.isFormValid]
);
const isFormValid = useMemo(() => indicesConfigurationFormState.isFormValid, [
indicesConfigurationFormState.isFormValid,
]);
const formState = useMemo(
() => ({
name: indicesConfigurationFormState.formState.name,
description: indicesConfigurationFormState.formState.description,
logAlias: indicesConfigurationFormState.formState.logAlias,
metricAlias: indicesConfigurationFormState.formState.metricAlias,
fields: {
container: indicesConfigurationFormState.formState.containerField,
@ -79,17 +60,15 @@ export const useSourceConfigurationFormState = (configuration?: InfraSourceConfi
tiebreaker: indicesConfigurationFormState.formState.tiebreakerField,
timestamp: indicesConfigurationFormState.formState.timestampField,
},
logColumns: logColumnsConfigurationFormState.formState.logColumns,
anomalyThreshold: indicesConfigurationFormState.formState.anomalyThreshold,
}),
[indicesConfigurationFormState.formState, logColumnsConfigurationFormState.formState]
[indicesConfigurationFormState.formState]
);
const formStateChanges = useMemo(
() => ({
name: indicesConfigurationFormState.formStateChanges.name,
description: indicesConfigurationFormState.formStateChanges.description,
logAlias: indicesConfigurationFormState.formStateChanges.logAlias,
metricAlias: indicesConfigurationFormState.formStateChanges.metricAlias,
fields: {
container: indicesConfigurationFormState.formStateChanges.containerField,
@ -98,25 +77,18 @@ export const useSourceConfigurationFormState = (configuration?: InfraSourceConfi
tiebreaker: indicesConfigurationFormState.formStateChanges.tiebreakerField,
timestamp: indicesConfigurationFormState.formStateChanges.timestampField,
},
logColumns: logColumnsConfigurationFormState.formStateChanges.logColumns,
anomalyThreshold: indicesConfigurationFormState.formStateChanges.anomalyThreshold,
}),
[
indicesConfigurationFormState.formStateChanges,
logColumnsConfigurationFormState.formStateChanges,
]
[indicesConfigurationFormState.formStateChanges]
);
return {
addLogColumn: logColumnsConfigurationFormState.addLogColumn,
moveLogColumn: logColumnsConfigurationFormState.moveLogColumn,
errors,
formState,
formStateChanges,
isFormDirty,
isFormValid,
indicesConfigurationProps: indicesConfigurationFormState.fieldProps,
logColumnConfigurationProps: logColumnsConfigurationFormState.logColumnConfigurationProps,
resetForm,
};
};

View file

@ -19,15 +19,15 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback, useContext, useMemo } from 'react';
import { Source } from '../../containers/source';
import { Source } from '../../../containers/metrics_source';
import { FieldsConfigurationPanel } from './fields_configuration_panel';
import { IndicesConfigurationPanel } from './indices_configuration_panel';
import { NameConfigurationPanel } from './name_configuration_panel';
import { NameConfigurationPanel } from '../../../components/source_configuration/name_configuration_panel';
import { useSourceConfigurationFormState } from './source_configuration_form_state';
import { SourceLoadingPage } from '../source_loading_page';
import { Prompt } from '../../utils/navigation_warning_prompt';
import { SourceLoadingPage } from '../../../components/source_loading_page';
import { Prompt } from '../../../utils/navigation_warning_prompt';
import { MLConfigurationPanel } from './ml_configuration_panel';
import { useInfraMLCapabilitiesContext } from '../../containers/ml/infra_ml_capabilities';
import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities';
interface SourceConfigurationSettingsProps {
shouldAllowEdit: boolean;

View file

@ -6,14 +6,14 @@
*/
import {
InfraSavedSourceConfigurationColumn,
InfraSavedSourceConfigurationFields,
InfraSourceConfigurationColumn,
InfraSourceConfigurationFieldColumn,
InfraSourceConfigurationMessageColumn,
InfraSourceConfigurationTimestampColumn,
} from '../../common/http_api/source_api';
} from '../../common/source_configuration/source_configuration';
export type LogColumnConfiguration = InfraSavedSourceConfigurationColumn;
export type FieldLogColumnConfiguration = InfraSavedSourceConfigurationFields;
export type LogColumnConfiguration = InfraSourceConfigurationColumn;
export type FieldLogColumnConfiguration = InfraSourceConfigurationFieldColumn;
export type MessageLogColumnConfiguration = InfraSourceConfigurationMessageColumn;
export type TimestampLogColumnConfiguration = InfraSourceConfigurationTimestampColumn;

View file

@ -32,7 +32,7 @@ import {
} from './routes/log_entries';
import { initInventoryMetaRoute } from './routes/inventory_metadata';
import { initLogSourceConfigurationRoutes, initLogSourceStatusRoutes } from './routes/log_sources';
import { initSourceRoute } from './routes/source';
import { initMetricsSourceConfigurationRoutes } from './routes/metrics_sources';
import { initOverviewRoute } from './routes/overview';
import { initAlertPreviewRoute } from './routes/alerting';
import { initGetLogAlertsChartPreviewDataRoute } from './routes/log_alerts';
@ -50,7 +50,7 @@ export const initInfraServer = (libs: InfraBackendLibs) => {
initGetHostsAnomaliesRoute(libs);
initSnapshotRoute(libs);
initNodeDetailsRoute(libs);
initSourceRoute(libs);
initMetricsSourceConfigurationRoutes(libs);
initValidateLogAnalysisDatasetsRoute(libs);
initValidateLogAnalysisIndicesRoute(libs);
initGetLogEntryExamplesRoute(libs);

View file

@ -34,7 +34,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
options: InfraMetricsRequestOptions,
rawRequest: KibanaRequest
): Promise<NodeDetailsMetricData[]> {
const indexPattern = `${options.sourceConfiguration.metricAlias},${options.sourceConfiguration.logAlias}`;
const indexPattern = `${options.sourceConfiguration.metricAlias}`;
const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
const nodeField = fields.id;
@ -112,7 +112,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
);
}
const indexPattern = `${options.sourceConfiguration.metricAlias},${options.sourceConfiguration.logAlias}`;
const indexPattern = `${options.sourceConfiguration.metricAlias}`;
const timerange = {
min: options.timerange.from,
max: options.timerange.to,
@ -132,7 +132,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
const calculatedInterval = await calculateMetricInterval(
client,
{
indexPattern: `${options.sourceConfiguration.logAlias},${options.sourceConfiguration.metricAlias}`,
indexPattern: `${options.sourceConfiguration.metricAlias}`,
timestampField: options.sourceConfiguration.fields.timestamp,
timerange: options.timerange,
},

View file

@ -23,6 +23,7 @@ import { InfraTimerangeInput, SnapshotRequest } from '../../../../common/http_ap
import { InfraSource } from '../../sources';
import { UNGROUPED_FACTORY_KEY } from '../common/utils';
import { getNodes } from '../../../routes/snapshot/lib/get_nodes';
import { LogQueryFields } from '../../../services/log_queries/get_log_query_fields';
type ConditionResult = InventoryMetricConditions & {
shouldFire: boolean[];
@ -36,6 +37,7 @@ export const evaluateCondition = async (
condition: InventoryMetricConditions,
nodeType: InventoryItemType,
source: InfraSource,
logQueryFields: LogQueryFields,
esClient: ElasticsearchClient,
filterQuery?: string,
lookbackSize?: number
@ -58,6 +60,7 @@ export const evaluateCondition = async (
metric,
timerange,
source,
logQueryFields,
filterQuery,
customMetric
);
@ -101,6 +104,7 @@ const getData = async (
metric: SnapshotMetricType,
timerange: InfraTimerangeInput,
source: InfraSource,
logQueryFields: LogQueryFields,
filterQuery?: string,
customMetric?: SnapshotCustomMetricInput
) => {
@ -124,7 +128,7 @@ const getData = async (
includeTimeseries: Boolean(timerange.lookbackSize),
};
try {
const { nodes } = await getNodes(client, snapshotRequest, source);
const { nodes } = await getNodes(client, snapshotRequest, source, logQueryFields);
if (!nodes.length) return { [UNGROUPED_FACTORY_KEY]: null }; // No Data state

View file

@ -68,12 +68,18 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
sourceId || 'default'
);
const logQueryFields = await libs.getLogQueryFields(
sourceId || 'default',
services.savedObjectsClient
);
const results = await Promise.all(
criteria.map((c) =>
evaluateCondition(
c,
nodeType,
source,
logQueryFields,
services.scopedClusterClient.asCurrentUser,
filterQuery
)

View file

@ -14,10 +14,11 @@ import {
isTooManyBucketsPreviewException,
} from '../../../../common/alerting/metrics';
import { ElasticsearchClient } from '../../../../../../../src/core/server';
import { InfraSource } from '../../../../common/http_api/source_api';
import { InfraSource } from '../../../../common/source_configuration/source_configuration';
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
import { InventoryItemType } from '../../../../common/inventory_models/types';
import { evaluateCondition } from './evaluate_condition';
import { LogQueryFields } from '../../../services/log_queries/get_log_query_fields';
interface InventoryMetricThresholdParams {
criteria: InventoryMetricConditions[];
@ -30,6 +31,7 @@ interface PreviewInventoryMetricThresholdAlertParams {
esClient: ElasticsearchClient;
params: InventoryMetricThresholdParams;
source: InfraSource;
logQueryFields: LogQueryFields;
lookback: Unit;
alertInterval: string;
alertThrottle: string;
@ -43,6 +45,7 @@ export const previewInventoryMetricThresholdAlert: (
esClient,
params,
source,
logQueryFields,
lookback,
alertInterval,
alertThrottle,
@ -68,7 +71,7 @@ export const previewInventoryMetricThresholdAlert: (
try {
const results = await Promise.all(
criteria.map((c) =>
evaluateCondition(c, nodeType, source, esClient, filterQuery, lookbackSize)
evaluateCondition(c, nodeType, source, logQueryFields, esClient, filterQuery, lookbackSize)
)
);

View file

@ -11,7 +11,7 @@ import {
isTooManyBucketsPreviewException,
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION,
} from '../../../../../common/alerting/metrics';
import { InfraSource } from '../../../../../common/http_api/source_api';
import { InfraSource } from '../../../../../common/source_configuration/source_configuration';
import { InfraDatabaseSearchResponse } from '../../../adapters/framework/adapter_types';
import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler';
import { getAllCompositeData } from '../../../../utils/get_all_composite_data';

View file

@ -12,7 +12,7 @@ import {
isTooManyBucketsPreviewException,
} from '../../../../common/alerting/metrics';
import { ElasticsearchClient } from '../../../../../../../src/core/server';
import { InfraSource } from '../../../../common/http_api/source_api';
import { InfraSource } from '../../../../common/source_configuration/source_configuration';
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
import { PreviewResult } from '../common/types';
import { MetricExpressionParams } from './types';

View file

@ -18,21 +18,16 @@ export class InfraFieldsDomain {
public async getFields(
requestContext: InfraPluginRequestHandlerContext,
sourceId: string,
indexType: 'LOGS' | 'METRICS' | 'ANY'
indexType: 'LOGS' | 'METRICS'
): Promise<InfraSourceIndexField[]> {
const { configuration } = await this.libs.sources.getSourceConfiguration(
requestContext.core.savedObjects.client,
sourceId
);
const includeMetricIndices = ['ANY', 'METRICS'].includes(indexType);
const includeLogIndices = ['ANY', 'LOGS'].includes(indexType);
const fields = await this.adapter.getIndexFields(
requestContext,
[
...(includeMetricIndices ? [configuration.metricAlias] : []),
...(includeLogIndices ? [configuration.logAlias] : []),
].join(',')
indexType === 'LOGS' ? configuration.logAlias : configuration.metricAlias
);
return fields;

View file

@ -17,7 +17,7 @@ import { LogColumn, LogEntryCursor, LogEntry } from '../../../../common/log_entr
import {
InfraSourceConfiguration,
InfraSources,
SavedSourceConfigurationFieldColumnRuntimeType,
SourceConfigurationFieldColumnRuntimeType,
} from '../../sources';
import { getBuiltinRules } from '../../../services/log_entries/message/builtin_rules';
import {
@ -349,7 +349,7 @@ const getRequiredFields = (
): string[] => {
const fieldsFromCustomColumns = configuration.logColumns.reduce<string[]>(
(accumulatedFields, logColumn) => {
if (SavedSourceConfigurationFieldColumnRuntimeType.is(logColumn)) {
if (SourceConfigurationFieldColumnRuntimeType.is(logColumn)) {
return [...accumulatedFields, logColumn.fieldColumn.field];
}
return accumulatedFields;

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { InfraSourceConfiguration } from '../../common/http_api/source_api';
import { InfraSourceConfiguration } from '../../common/source_configuration/source_configuration';
import { InfraFieldsDomain } from './domains/fields_domain';
import { InfraLogEntriesDomain } from './domains/log_entries_domain';
import { InfraMetricsDomain } from './domains/metrics_domain';
@ -13,6 +13,7 @@ import { InfraSources } from './sources';
import { InfraSourceStatus } from './source_status';
import { InfraConfig } from '../plugin';
import { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
import { GetLogQueryFields } from '../services/log_queries/get_log_query_fields';
export interface InfraDomainLibs {
fields: InfraFieldsDomain;
@ -25,6 +26,7 @@ export interface InfraBackendLibs extends InfraDomainLibs {
framework: KibanaFramework;
sources: InfraSources;
sourceStatus: InfraSourceStatus;
getLogQueryFields: GetLogQueryFields;
}
export interface InfraConfiguration {

View file

@ -120,5 +120,5 @@ export const query = async (
ThrowReporter.report(HistogramResponseRT.decode(response.aggregations));
}
throw new Error('Elasticsearch responsed with an unrecoginzed format.');
throw new Error('Elasticsearch responded with an unrecognized format.');
};

View file

@ -10,7 +10,7 @@ import {
LOGS_INDEX_PATTERN,
TIMESTAMP_FIELD,
} from '../../../common/constants';
import { InfraSourceConfiguration } from '../../../common/http_api/source_api';
import { InfraSourceConfiguration } from '../../../common/source_configuration/source_configuration';
export const defaultSourceConfiguration: InfraSourceConfiguration = {
name: 'Default',

View file

@ -8,4 +8,4 @@
export * from './defaults';
export { infraSourceConfigurationSavedObjectType } from './saved_object_type';
export * from './sources';
export * from '../../../common/http_api/source_api';
export * from '../../../common/source_configuration/source_configuration';

View file

@ -6,7 +6,7 @@
*/
import { SavedObjectMigrationFn } from 'src/core/server';
import { InfraSourceConfiguration } from '../../../../common/http_api/source_api';
import { InfraSourceConfiguration } from '../../../../common/source_configuration/source_configuration';
export const addNewIndexingStrategyIndexNames: SavedObjectMigrationFn<
InfraSourceConfiguration,

View file

@ -23,7 +23,7 @@ import {
SourceConfigurationSavedObjectRuntimeType,
StaticSourceConfigurationRuntimeType,
InfraSource,
} from '../../../common/http_api/source_api';
} from '../../../common/source_configuration/source_configuration';
import { InfraConfig } from '../../../server';
interface Libs {

View file

@ -9,7 +9,7 @@ import { Server } from '@hapi/hapi';
import { schema, TypeOf } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { CoreSetup, PluginInitializerContext, Plugin } from 'src/core/server';
import { InfraStaticSourceConfiguration } from '../common/http_api/source_api';
import { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration';
import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view';
import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view';
import { LOGS_FEATURE, METRICS_FEATURE } from './features';
@ -30,6 +30,7 @@ import { InfraSourceStatus } from './lib/source_status';
import { LogEntriesService } from './services/log_entries';
import { InfraPluginRequestHandlerContext } from './types';
import { UsageCollector } from './usage/usage_collector';
import { createGetLogQueryFields } from './services/log_queries/get_log_query_fields';
export const config = {
schema: schema.object({
@ -123,6 +124,7 @@ export class InfraServerPlugin implements Plugin<InfraPluginSetup> {
sources,
sourceStatus,
...domainLibs,
getLogQueryFields: createGetLogQueryFields(sources),
};
plugins.features.registerKibanaFeature(METRICS_FEATURE);

View file

@ -25,7 +25,11 @@ import { previewMetricAnomalyAlert } from '../../lib/alerting/metric_anomaly/pre
import { InfraBackendLibs } from '../../lib/infra_types';
import { assertHasInfraMlPlugins } from '../../utils/request_context';
export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs) => {
export const initAlertPreviewRoute = ({
framework,
sources,
getLogQueryFields,
}: InfraBackendLibs) => {
framework.registerRoute(
{
method: 'post',
@ -77,6 +81,10 @@ export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs)
});
}
case METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID: {
const logQueryFields = await getLogQueryFields(
sourceId || 'default',
requestContext.core.savedObjects.client
);
const {
nodeType,
criteria,
@ -87,6 +95,7 @@ export const initAlertPreviewRoute = ({ framework, sources }: InfraBackendLibs)
params: { criteria, filterQuery, nodeType },
lookback,
source,
logQueryFields,
alertInterval,
alertThrottle,
alertNotifyWhen,

View file

@ -8,63 +8,49 @@
import { schema } from '@kbn/config-schema';
import Boom from '@hapi/boom';
import { createValidationFunction } from '../../../common/runtime_types';
import {
InfraSourceStatus,
SavedSourceConfigurationRuntimeType,
SourceResponseRuntimeType,
} from '../../../common/http_api/source_api';
import { InfraBackendLibs } from '../../lib/infra_types';
import { hasData } from '../../lib/sources/has_data';
import { createSearchClient } from '../../lib/create_search_client';
import { AnomalyThresholdRangeError } from '../../lib/sources/errors';
import {
partialMetricsSourceConfigurationPropertiesRT,
metricsSourceConfigurationResponseRT,
MetricsSourceStatus,
} from '../../../common/metrics_sources';
const typeToInfraIndexType = (value: string | undefined) => {
switch (value) {
case 'metrics':
return 'METRICS';
case 'logs':
return 'LOGS';
default:
return 'ANY';
}
};
export const initSourceRoute = (libs: InfraBackendLibs) => {
export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => {
const { framework } = libs;
framework.registerRoute(
{
method: 'get',
path: '/api/metrics/source/{sourceId}/{type?}',
path: '/api/metrics/source/{sourceId}',
validate: {
params: schema.object({
sourceId: schema.string(),
type: schema.string(),
}),
},
},
async (requestContext, request, response) => {
const { type, sourceId } = request.params;
const { sourceId } = request.params;
const [source, logIndexStatus, metricIndicesExist, indexFields] = await Promise.all([
const [source, metricIndicesExist, indexFields] = await Promise.all([
libs.sources.getSourceConfiguration(requestContext.core.savedObjects.client, sourceId),
libs.sourceStatus.getLogIndexStatus(requestContext, sourceId),
libs.sourceStatus.hasMetricIndices(requestContext, sourceId),
libs.fields.getFields(requestContext, sourceId, typeToInfraIndexType(type)),
libs.fields.getFields(requestContext, sourceId, 'METRICS'),
]);
if (!source) {
return response.notFound();
}
const status: InfraSourceStatus = {
logIndicesExist: logIndexStatus !== 'missing',
const status: MetricsSourceStatus = {
metricIndicesExist,
indexFields,
};
return response.ok({
body: SourceResponseRuntimeType.encode({ source: { ...source, status } }),
body: metricsSourceConfigurationResponseRT.encode({ source: { ...source, status } }),
});
}
);
@ -77,7 +63,7 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
params: schema.object({
sourceId: schema.string(),
}),
body: createValidationFunction(SavedSourceConfigurationRuntimeType),
body: createValidationFunction(partialMetricsSourceConfigurationPropertiesRT),
},
},
framework.router.handleLegacyErrors(async (requestContext, request, response) => {
@ -110,20 +96,18 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
patchedSourceConfigurationProperties
));
const [logIndexStatus, metricIndicesExist, indexFields] = await Promise.all([
libs.sourceStatus.getLogIndexStatus(requestContext, sourceId),
const [metricIndicesExist, indexFields] = await Promise.all([
libs.sourceStatus.hasMetricIndices(requestContext, sourceId),
libs.fields.getFields(requestContext, sourceId, typeToInfraIndexType('metrics')),
libs.fields.getFields(requestContext, sourceId, 'METRICS'),
]);
const status: InfraSourceStatus = {
logIndicesExist: logIndexStatus !== 'missing',
const status: MetricsSourceStatus = {
metricIndicesExist,
indexFields,
};
return response.ok({
body: SourceResponseRuntimeType.encode({
body: metricsSourceConfigurationResponseRT.encode({
source: { ...patchedSourceConfiguration, status },
}),
});
@ -154,25 +138,23 @@ export const initSourceRoute = (libs: InfraBackendLibs) => {
framework.registerRoute(
{
method: 'get',
path: '/api/metrics/source/{sourceId}/{type}/hasData',
path: '/api/metrics/source/{sourceId}/hasData',
validate: {
params: schema.object({
sourceId: schema.string(),
type: schema.string(),
}),
},
},
async (requestContext, request, response) => {
const { type, sourceId } = request.params;
const { sourceId } = request.params;
const client = createSearchClient(requestContext, framework);
const source = await libs.sources.getSourceConfiguration(
requestContext.core.savedObjects.client,
sourceId
);
const indexPattern =
type === 'metrics' ? source.configuration.metricAlias : source.configuration.logAlias;
const results = await hasData(indexPattern, client);
const results = await hasData(source.configuration.metricAlias, client);
return response.ok({
body: { hasData: results },

View file

@ -41,9 +41,15 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => {
snapshotRequest.sourceId
);
const logQueryFields = await libs.getLogQueryFields(
snapshotRequest.sourceId,
requestContext.core.savedObjects.client
);
UsageCollector.countNode(snapshotRequest.nodeType);
const client = createSearchClient(requestContext, framework);
const snapshotResponse = await getNodes(client, snapshotRequest, source);
const snapshotResponse = await getNodes(client, snapshotRequest, source, logQueryFields);
return response.ok({
body: SnapshotNodeResponseRT.encode(snapshotResponse),

View file

@ -1,23 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SnapshotRequest } from '../../../../common/http_api';
import { InfraSource } from '../../../lib/sources';
export const calculateIndexPatterBasedOnMetrics = (
options: SnapshotRequest,
source: InfraSource
) => {
const { metrics } = options;
if (metrics.every((m) => m.type === 'logRate')) {
return source.configuration.logAlias;
}
if (metrics.some((m) => m.type === 'logRate')) {
return `${source.configuration.logAlias},${source.configuration.metricAlias}`;
}
return source.configuration.metricAlias;
};

View file

@ -12,16 +12,24 @@ import { transformRequestToMetricsAPIRequest } from './transform_request_to_metr
import { queryAllData } from './query_all_data';
import { transformMetricsApiResponseToSnapshotResponse } from './trasform_metrics_ui_response';
import { copyMissingMetrics } from './copy_missing_metrics';
import { LogQueryFields } from '../../../services/log_queries/get_log_query_fields';
export const getNodes = async (
export interface SourceOverrides {
indexPattern: string;
timestamp: string;
}
const transformAndQueryData = async (
client: ESSearchClient,
snapshotRequest: SnapshotRequest,
source: InfraSource
source: InfraSource,
sourceOverrides?: SourceOverrides
) => {
const metricsApiRequest = await transformRequestToMetricsAPIRequest(
client,
source,
snapshotRequest
snapshotRequest,
sourceOverrides
);
const metricsApiResponse = await queryAllData(client, metricsApiRequest);
const snapshotResponse = transformMetricsApiResponseToSnapshotResponse(
@ -32,3 +40,59 @@ export const getNodes = async (
);
return copyMissingMetrics(snapshotResponse);
};
export const getNodes = async (
client: ESSearchClient,
snapshotRequest: SnapshotRequest,
source: InfraSource,
logQueryFields: LogQueryFields
) => {
let nodes;
if (snapshotRequest.metrics.find((metric) => metric.type === 'logRate')) {
// *Only* the log rate metric has been requested
if (snapshotRequest.metrics.length === 1) {
nodes = await transformAndQueryData(client, snapshotRequest, source, logQueryFields);
} else {
// A scenario whereby a single host might be shipping metrics and logs.
const metricsWithoutLogsMetrics = snapshotRequest.metrics.filter(
(metric) => metric.type !== 'logRate'
);
const nodesWithoutLogsMetrics = await transformAndQueryData(
client,
{ ...snapshotRequest, metrics: metricsWithoutLogsMetrics },
source
);
const logRateNodes = await transformAndQueryData(
client,
{ ...snapshotRequest, metrics: [{ type: 'logRate' }] },
source,
logQueryFields
);
// Merge nodes where possible - e.g. a single host is shipping metrics and logs
const mergedNodes = nodesWithoutLogsMetrics.nodes.map((node) => {
const logRateNode = logRateNodes.nodes.find(
(_logRateNode) => node.name === _logRateNode.name
);
if (logRateNode) {
// Remove this from the "leftovers"
logRateNodes.nodes.filter((_node) => _node.name !== logRateNode.name);
}
return logRateNode
? {
...node,
metrics: [...node.metrics, ...logRateNode.metrics],
}
: node;
});
nodes = {
...nodesWithoutLogsMetrics,
nodes: [...mergedNodes, ...logRateNodes.nodes],
};
}
} else {
nodes = await transformAndQueryData(client, snapshotRequest, source);
}
return nodes;
};

View file

@ -12,13 +12,14 @@ import { InfraSource } from '../../../lib/sources';
import { createTimeRangeWithInterval } from './create_timerange_with_interval';
import { parseFilterQuery } from '../../../utils/serialized_query';
import { transformSnapshotMetricsToMetricsAPIMetrics } from './transform_snapshot_metrics_to_metrics_api_metrics';
import { calculateIndexPatterBasedOnMetrics } from './calculate_index_pattern_based_on_metrics';
import { META_KEY } from './constants';
import { SourceOverrides } from './get_nodes';
export const transformRequestToMetricsAPIRequest = async (
client: ESSearchClient,
source: InfraSource,
snapshotRequest: SnapshotRequest
snapshotRequest: SnapshotRequest,
sourceOverrides?: SourceOverrides
): Promise<MetricsAPIRequest> => {
const timeRangeWithIntervalApplied = await createTimeRangeWithInterval(client, {
...snapshotRequest,
@ -27,9 +28,9 @@ export const transformRequestToMetricsAPIRequest = async (
});
const metricsApiRequest: MetricsAPIRequest = {
indexPattern: calculateIndexPatterBasedOnMetrics(snapshotRequest, source),
indexPattern: sourceOverrides?.indexPattern ?? source.configuration.metricAlias,
timerange: {
field: source.configuration.fields.timestamp,
field: sourceOverrides?.timestamp ?? source.configuration.fields.timestamp,
from: timeRangeWithIntervalApplied.from,
to: timeRangeWithIntervalApplied.to,
interval: timeRangeWithIntervalApplied.interval,
@ -74,7 +75,7 @@ export const transformRequestToMetricsAPIRequest = async (
top_hits: {
size: 1,
_source: [inventoryFields.name],
sort: [{ [source.configuration.fields.timestamp]: 'desc' }],
sort: [{ [sourceOverrides?.timestamp ?? source.configuration.fields.timestamp]: 'desc' }],
},
},
},

View file

@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SavedObjectsClientContract } from 'src/core/server';
import { InfraSources } from '../../lib/sources';
// NOTE: TEMPORARY: This will become a subset of the new resolved KIP compatible log source configuration.
export interface LogQueryFields {
indexPattern: string;
timestamp: string;
}
// NOTE: TEMPORARY: This will become a subset of the new resolved KIP compatible log source configuration.
export const createGetLogQueryFields = (sources: InfraSources) => {
return async (
sourceId: string,
savedObjectsClient: SavedObjectsClientContract
): Promise<LogQueryFields> => {
const source = await sources.getSourceConfiguration(savedObjectsClient, sourceId);
return {
indexPattern: source.configuration.logAlias,
timestamp: source.configuration.fields.timestamp,
};
};
};
export type GetLogQueryFields = ReturnType<typeof createGetLogQueryFields>;

View file

@ -15,16 +15,14 @@ export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const fetchSource = async (): Promise<SourceResponse | undefined> => {
const response = await supertest
.get('/api/metrics/source/default/metrics')
.get('/api/metrics/source/default')
.set('kbn-xsrf', 'xxx')
.expect(200);
return response.body;
};
const fetchHasData = async (
type: 'logs' | 'metrics'
): Promise<{ hasData: boolean } | undefined> => {
const fetchHasData = async (): Promise<{ hasData: boolean } | undefined> => {
const response = await supertest
.get(`/api/metrics/source/default/${type}/hasData`)
.get(`/api/metrics/source/default/hasData`)
.set('kbn-xsrf', 'xxx')
.expect(200);
return response.body;
@ -34,41 +32,27 @@ export default function ({ getService }: FtrProviderContext) {
describe('8.0.0', () => {
before(() => esArchiver.load('infra/8.0.0/logs_and_metrics'));
after(() => esArchiver.unload('infra/8.0.0/logs_and_metrics'));
describe('/api/metrics/source/default/metrics', () => {
describe('/api/metrics/source/default', () => {
it('should just work', async () => {
const resp = fetchSource();
return resp.then((data) => {
expect(data).to.have.property('source');
expect(data?.source.configuration.metricAlias).to.equal('metrics-*,metricbeat-*');
expect(data?.source.configuration.logAlias).to.equal(
'logs-*,filebeat-*,kibana_sample_data_logs*'
);
expect(data?.source.configuration.fields).to.eql({
container: 'container.id',
host: 'host.name',
message: ['message', '@message'],
pod: 'kubernetes.pod.uid',
tiebreaker: '_doc',
timestamp: '@timestamp',
});
expect(data?.source).to.have.property('status');
expect(data?.source.status?.metricIndicesExist).to.equal(true);
expect(data?.source.status?.logIndicesExist).to.equal(true);
});
});
});
describe('/api/metrics/source/default/metrics/hasData', () => {
describe('/api/metrics/source/default/hasData', () => {
it('should just work', async () => {
const resp = fetchHasData('metrics');
return resp.then((data) => {
expect(data).to.have.property('hasData');
expect(data?.hasData).to.be(true);
});
});
});
describe('/api/metrics/source/default/logs/hasData', () => {
it('should just work', async () => {
const resp = fetchHasData('logs');
const resp = fetchHasData();
return resp.then((data) => {
expect(data).to.have.property('hasData');
expect(data?.hasData).to.be(true);

View file

@ -8,10 +8,10 @@
import expect from '@kbn/expect';
import {
SourceResponse,
InfraSavedSourceConfiguration,
SourceResponseRuntimeType,
} from '../../../../plugins/infra/common/http_api/source_api';
MetricsSourceConfigurationResponse,
PartialMetricsSourceConfigurationProperties,
metricsSourceConfigurationResponseRT,
} from '../../../../plugins/infra/common/metrics_sources';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
@ -19,8 +19,8 @@ export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const SOURCE_API_URL = '/api/metrics/source/default';
const patchRequest = async (
body: InfraSavedSourceConfiguration
): Promise<SourceResponse | undefined> => {
body: PartialMetricsSourceConfigurationProperties
): Promise<MetricsSourceConfigurationResponse | undefined> => {
const response = await supertest
.patch(SOURCE_API_URL)
.set('kbn-xsrf', 'xxx')
@ -51,10 +51,9 @@ export default function ({ getService }: FtrProviderContext) {
name: 'UPDATED_NAME',
description: 'UPDATED_DESCRIPTION',
metricAlias: 'metricbeat-**',
logAlias: 'filebeat-**',
});
expect(SourceResponseRuntimeType.is(updateResponse)).to.be(true);
expect(metricsSourceConfigurationResponseRT.is(updateResponse)).to.be(true);
const version = updateResponse?.source.version;
const updatedAt = updateResponse?.source.updatedAt;
@ -67,15 +66,12 @@ export default function ({ getService }: FtrProviderContext) {
expect(configuration?.name).to.be('UPDATED_NAME');
expect(configuration?.description).to.be('UPDATED_DESCRIPTION');
expect(configuration?.metricAlias).to.be('metricbeat-**');
expect(configuration?.logAlias).to.be('filebeat-**');
expect(configuration?.fields.host).to.be('host.name');
expect(configuration?.fields.pod).to.be('kubernetes.pod.uid');
expect(configuration?.fields.tiebreaker).to.be('_doc');
expect(configuration?.fields.timestamp).to.be('@timestamp');
expect(configuration?.fields.container).to.be('container.id');
expect(configuration?.logColumns).to.have.length(3);
expect(configuration?.anomalyThreshold).to.be(50);
expect(status?.logIndicesExist).to.be(true);
expect(status?.metricIndicesExist).to.be(true);
});
@ -105,8 +101,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(version).to.not.be(initialVersion);
expect(updatedAt).to.be.greaterThan(createdAt || 0);
expect(configuration?.metricAlias).to.be('metricbeat-**');
expect(configuration?.logAlias).to.be('logs-*,filebeat-*,kibana_sample_data_logs*');
expect(status?.logIndicesExist).to.be(true);
expect(status?.metricIndicesExist).to.be(true);
});
@ -144,37 +138,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(configuration?.fields.timestamp).to.be('@timestamp');
});
it('applies a log column update to an existing source', async () => {
const creationResponse = await patchRequest({
name: 'NAME',
});
const initialVersion = creationResponse?.source.version;
const createdAt = creationResponse?.source.updatedAt;
const updateResponse = await patchRequest({
logColumns: [
{
fieldColumn: {
id: 'ADDED_COLUMN_ID',
field: 'ADDED_COLUMN_FIELD',
},
},
],
});
const version = updateResponse?.source.version;
const updatedAt = updateResponse?.source.updatedAt;
const configuration = updateResponse?.source.configuration;
expect(version).to.be.a('string');
expect(version).to.not.be(initialVersion);
expect(updatedAt).to.be.greaterThan(createdAt || 0);
expect(configuration?.logColumns).to.have.length(1);
expect(configuration?.logColumns[0]).to.have.key('fieldColumn');
const fieldColumn = (configuration?.logColumns[0] as any).fieldColumn;
expect(fieldColumn).to.have.property('id', 'ADDED_COLUMN_ID');
expect(fieldColumn).to.have.property('field', 'ADDED_COLUMN_FIELD');
});
it('validates anomalyThreshold is between range 1-100', async () => {
// create config with bad request
await supertest

View file

@ -6,17 +6,17 @@
*/
import {
InfraSavedSourceConfiguration,
SourceResponse,
} from '../../../plugins/infra/common/http_api/source_api';
PartialMetricsSourceConfiguration,
MetricsSourceConfigurationResponse,
} from '../../../plugins/infra/common/metrics_sources';
import { FtrProviderContext } from '../ftr_provider_context';
export function InfraOpsSourceConfigurationProvider({ getService }: FtrProviderContext) {
const log = getService('log');
const supertest = getService('supertest');
const patchRequest = async (
body: InfraSavedSourceConfiguration
): Promise<SourceResponse | undefined> => {
body: PartialMetricsSourceConfiguration
): Promise<MetricsSourceConfigurationResponse | undefined> => {
const response = await supertest
.patch('/api/metrics/source/default')
.set('kbn-xsrf', 'xxx')
@ -26,7 +26,10 @@ export function InfraOpsSourceConfigurationProvider({ getService }: FtrProviderC
};
return {
async createConfiguration(sourceId: string, sourceProperties: InfraSavedSourceConfiguration) {
async createConfiguration(
sourceId: string,
sourceProperties: PartialMetricsSourceConfiguration
) {
log.debug(
`Creating Infra UI source configuration "${sourceId}" with properties ${JSON.stringify(
sourceProperties