diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
index 98ce22cd14227..5d33a08557fed 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
@@ -22,10 +22,10 @@ import {
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
- EuiPanel,
- EuiText,
+ EuiTitle,
EuiSpacer,
EuiLoadingSpinner,
+ EuiHorizontalRule,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { ensureMinimumTime, extractTimeFields } from '../../lib';
@@ -43,6 +43,7 @@ interface StepTimeFieldProps {
goToPreviousStep: () => void;
createIndexPattern: (selectedTimeField: string | undefined, indexPatternId: string) => void;
indexPatternCreationType: IndexPatternCreationConfig;
+ selectedTimeField?: string;
}
interface StepTimeFieldState {
@@ -69,7 +70,7 @@ export class StepTimeField extends Component
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
);
}
@@ -236,7 +242,7 @@ export class StepTimeField extends Component
+ <>
-
+
-
+ >
);
}
}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx
index 111be41cfc53a..cd76ca09ccb74 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx
@@ -19,10 +19,16 @@
import React, { ReactElement, Component } from 'react';
-import { EuiGlobalToastList, EuiGlobalToastListToast, EuiPanel } from '@elastic/eui';
+import {
+ EuiGlobalToastList,
+ EuiGlobalToastListToast,
+ EuiPageContent,
+ EuiHorizontalRule,
+} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { withRouter, RouteComponentProps } from 'react-router-dom';
+import { DocLinksStart } from 'src/core/public';
import { StepIndexPattern } from './components/step_index_pattern';
import { StepTimeField } from './components/step_time_field';
import { Header } from './components/header';
@@ -31,21 +37,21 @@ import { EmptyState } from './components/empty_state';
import { context as contextType } from '../../../../kibana_react/public';
import { getCreateBreadcrumbs } from '../breadcrumbs';
-import { MAX_SEARCH_SIZE } from './constants';
import { ensureMinimumTime, getIndices } from './lib';
import { IndexPatternCreationConfig } from '../..';
import { IndexPatternManagmentContextValue } from '../../types';
-import { MatchedIndex } from './types';
+import { MatchedItem } from './types';
interface CreateIndexPatternWizardState {
step: number;
indexPattern: string;
- allIndices: MatchedIndex[];
+ allIndices: MatchedItem[];
remoteClustersExist: boolean;
isInitiallyLoadingIndices: boolean;
- isIncludingSystemIndices: boolean;
toasts: EuiGlobalToastListToast[];
indexPatternCreationType: IndexPatternCreationConfig;
+ selectedTimeField?: string;
+ docLinks: DocLinksStart;
}
export class CreateIndexPatternWizard extends Component<
@@ -69,9 +75,9 @@ export class CreateIndexPatternWizard extends Component<
allIndices: [],
remoteClustersExist: false,
isInitiallyLoadingIndices: true,
- isIncludingSystemIndices: false,
toasts: [],
indexPatternCreationType: context.services.indexPatternManagementStart.creation.getType(type),
+ docLinks: context.services.docLinks,
};
}
@@ -80,7 +86,7 @@ export class CreateIndexPatternWizard extends Component<
}
catchAndWarn = async (
- asyncFn: Promise,
+ asyncFn: Promise,
errorValue: [] | string[],
errorMsg: ReactElement
) => {
@@ -102,12 +108,6 @@ export class CreateIndexPatternWizard extends Component<
};
fetchData = async () => {
- this.setState({
- allIndices: [],
- isInitiallyLoadingIndices: true,
- remoteClustersExist: false,
- });
-
const indicesFailMsg = (
+ ).then((allIndices: MatchedItem[]) =>
this.setState({ allIndices, isInitiallyLoadingIndices: false })
);
this.catchAndWarn(
// if we get an error from remote cluster query, supply fallback value that allows user entry.
// ['a'] is fallback value
- getIndices(
- this.context.services.data.search.__LEGACY.esClient,
- this.state.indexPatternCreationType,
- `*:*`,
- 1
- ),
+ getIndices(this.context.services.http, this.state.indexPatternCreationType, `*:*`, false),
['a'],
clustersFailMsg
- ).then((remoteIndices: string[] | MatchedIndex[]) =>
+ ).then((remoteIndices: string[] | MatchedItem[]) =>
this.setState({ remoteClustersExist: !!remoteIndices.length })
);
};
@@ -189,7 +179,7 @@ export class CreateIndexPatternWizard extends Component<
if (isConfirmed) {
return history.push(`/patterns/${indexPatternId}`);
} else {
- return false;
+ return;
}
}
@@ -201,31 +191,21 @@ export class CreateIndexPatternWizard extends Component<
history.push(`/patterns/${createdId}`);
};
- goToTimeFieldStep = (indexPattern: string) => {
- this.setState({ step: 2, indexPattern });
+ goToTimeFieldStep = (indexPattern: string, selectedTimeField?: string) => {
+ this.setState({ step: 2, indexPattern, selectedTimeField });
};
goToIndexPatternStep = () => {
this.setState({ step: 1 });
};
- onChangeIncludingSystemIndices = () => {
- this.setState((prevState) => ({
- isIncludingSystemIndices: !prevState.isIncludingSystemIndices,
- }));
- };
-
renderHeader() {
- const { isIncludingSystemIndices } = this.state;
-
return (
);
}
@@ -234,7 +214,6 @@ export class CreateIndexPatternWizard extends Component<
const {
allIndices,
isInitiallyLoadingIndices,
- isIncludingSystemIndices,
step,
indexPattern,
remoteClustersExist,
@@ -244,8 +223,8 @@ export class CreateIndexPatternWizard extends Component<
return ;
}
- const hasDataIndices = allIndices.some(({ name }: MatchedIndex) => !name.startsWith('.'));
- if (!hasDataIndices && !isIncludingSystemIndices && !remoteClustersExist) {
+ const hasDataIndices = allIndices.some(({ name }: MatchedItem) => !name.startsWith('.'));
+ if (!hasDataIndices && !remoteClustersExist) {
return (
+
+ {header}
+
+
+
);
}
if (step === 2) {
return (
-
+
+ {header}
+
+
+
);
}
@@ -290,15 +282,11 @@ export class CreateIndexPatternWizard extends Component<
};
render() {
- const header = this.renderHeader();
const content = this.renderContent();
return (
-
-
- {header}
- {content}
-
+ <>
+ {content}
{
@@ -306,7 +294,7 @@ export class CreateIndexPatternWizard extends Component<
}}
toastLifeTimeMs={6000}
/>
-
+ >
);
}
}
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/__snapshots__/get_indices.test.ts.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/__snapshots__/get_indices.test.ts.snap
new file mode 100644
index 0000000000000..99876383b4343
--- /dev/null
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/__snapshots__/get_indices.test.ts.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`getIndices response object to item array 1`] = `
+Array [
+ Object {
+ "item": Object {
+ "attributes": Array [
+ "frozen",
+ ],
+ "name": "frozen_index",
+ },
+ "name": "frozen_index",
+ "tags": Array [
+ Object {
+ "color": "default",
+ "key": "index",
+ "name": "Index",
+ },
+ Object {
+ "color": "danger",
+ "key": "frozen",
+ "name": "Frozen",
+ },
+ ],
+ },
+ Object {
+ "item": Object {
+ "indices": Array [],
+ "name": "test_alias",
+ },
+ "name": "test_alias",
+ "tags": Array [
+ Object {
+ "color": "default",
+ "key": "alias",
+ "name": "Alias",
+ },
+ ],
+ },
+ Object {
+ "item": Object {
+ "backing_indices": Array [],
+ "name": "test_data_stream",
+ "timestamp_field": "test_timestamp_field",
+ },
+ "name": "test_data_stream",
+ "tags": Array [
+ Object {
+ "color": "primary",
+ "key": "data_stream",
+ "name": "Data stream",
+ },
+ ],
+ },
+ Object {
+ "item": Object {
+ "name": "test_index",
+ },
+ "name": "test_index",
+ "tags": Array [
+ Object {
+ "color": "default",
+ "key": "index",
+ "name": "Index",
+ },
+ ],
+ },
+]
+`;
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts
index b1faca8a04964..8e4dd37284333 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts
@@ -17,66 +17,31 @@
* under the License.
*/
-import { getIndices } from './get_indices';
+import { getIndices, responseToItemArray } from './get_indices';
import { IndexPatternCreationConfig } from '../../../../../index_pattern_management/public';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { LegacyApiCaller } from '../../../../../data/public/search/legacy';
+import { httpServiceMock } from '../../../../../../core/public/mocks';
+import { ResolveIndexResponseItemIndexAttrs } from '../types';
export const successfulResponse = {
- hits: {
- total: 1,
- max_score: 0.0,
- hits: [],
- },
- aggregations: {
- indices: {
- doc_count_error_upper_bound: 0,
- sum_other_doc_count: 0,
- buckets: [
- {
- key: '1',
- doc_count: 1,
- },
- {
- key: '2',
- doc_count: 1,
- },
- ],
+ indices: [
+ {
+ name: 'remoteCluster1:bar-01',
+ attributes: ['open'],
},
- },
-};
-
-export const exceptionResponse = {
- body: {
- error: {
- root_cause: [
- {
- type: 'index_not_found_exception',
- reason: 'no such index',
- index_uuid: '_na_',
- 'resource.type': 'index_or_alias',
- 'resource.id': 't',
- index: 't',
- },
- ],
- type: 'transport_exception',
- reason: 'unable to communicate with remote cluster [cluster_one]',
- caused_by: {
- type: 'index_not_found_exception',
- reason: 'no such index',
- index_uuid: '_na_',
- 'resource.type': 'index_or_alias',
- 'resource.id': 't',
- index: 't',
- },
+ ],
+ aliases: [
+ {
+ name: 'f-alias',
+ indices: ['freeze-index', 'my-index'],
},
- },
- status: 500,
-};
-
-export const errorResponse = {
- statusCode: 400,
- error: 'Bad Request',
+ ],
+ data_streams: [
+ {
+ name: 'foo',
+ backing_indices: ['foo-000001'],
+ timestamp_field: '@timestamp',
+ },
+ ],
};
const mockIndexPatternCreationType = new IndexPatternCreationConfig({
@@ -87,81 +52,62 @@ const mockIndexPatternCreationType = new IndexPatternCreationConfig({
isBeta: false,
});
-function esClientFactory(search: (params: any) => any): LegacyApiCaller {
- return {
- search,
- msearch: () => ({
- abort: () => {},
- ...new Promise((resolve) => resolve({})),
- }),
- };
-}
-
-const es = esClientFactory(() => successfulResponse);
+const http = httpServiceMock.createStartContract();
+http.get.mockResolvedValue(successfulResponse);
describe('getIndices', () => {
it('should work in a basic case', async () => {
- const result = await getIndices(es, mockIndexPatternCreationType, 'kibana', 1);
- expect(result.length).toBe(2);
- expect(result[0].name).toBe('1');
- expect(result[1].name).toBe('2');
+ const result = await getIndices(http, mockIndexPatternCreationType, 'kibana', false);
+ expect(result.length).toBe(3);
+ expect(result[0].name).toBe('f-alias');
+ expect(result[1].name).toBe('foo');
});
it('should ignore ccs query-all', async () => {
- expect((await getIndices(es, mockIndexPatternCreationType, '*:', 10)).length).toBe(0);
+ expect((await getIndices(http, mockIndexPatternCreationType, '*:', false)).length).toBe(0);
});
it('should ignore a single comma', async () => {
- expect((await getIndices(es, mockIndexPatternCreationType, ',', 10)).length).toBe(0);
- expect((await getIndices(es, mockIndexPatternCreationType, ',*', 10)).length).toBe(0);
- expect((await getIndices(es, mockIndexPatternCreationType, ',foobar', 10)).length).toBe(0);
- });
-
- it('should trim the input', async () => {
- let index;
- const esClient = esClientFactory(
- jest.fn().mockImplementation((params) => {
- index = params.index;
- })
- );
-
- await getIndices(esClient, mockIndexPatternCreationType, 'kibana ', 1);
- expect(index).toBe('kibana');
+ expect((await getIndices(http, mockIndexPatternCreationType, ',', false)).length).toBe(0);
+ expect((await getIndices(http, mockIndexPatternCreationType, ',*', false)).length).toBe(0);
+ expect((await getIndices(http, mockIndexPatternCreationType, ',foobar', false)).length).toBe(0);
});
- it('should use the limit', async () => {
- let limit;
- const esClient = esClientFactory(
- jest.fn().mockImplementation((params) => {
- limit = params.body.aggs.indices.terms.size;
- })
- );
- await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 10);
- expect(limit).toBe(10);
+ it('response object to item array', () => {
+ const result = {
+ indices: [
+ {
+ name: 'test_index',
+ },
+ {
+ name: 'frozen_index',
+ attributes: ['frozen' as ResolveIndexResponseItemIndexAttrs],
+ },
+ ],
+ aliases: [
+ {
+ name: 'test_alias',
+ indices: [],
+ },
+ ],
+ data_streams: [
+ {
+ name: 'test_data_stream',
+ backing_indices: [],
+ timestamp_field: 'test_timestamp_field',
+ },
+ ],
+ };
+ expect(responseToItemArray(result, mockIndexPatternCreationType)).toMatchSnapshot();
+ expect(responseToItemArray({}, mockIndexPatternCreationType)).toEqual([]);
});
describe('errors', () => {
it('should handle errors gracefully', async () => {
- const esClient = esClientFactory(() => errorResponse);
- const result = await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1);
- expect(result.length).toBe(0);
- });
-
- it('should throw exceptions', async () => {
- const esClient = esClientFactory(() => {
- throw new Error('Fail');
+ http.get.mockImplementationOnce(() => {
+ throw new Error('Test error');
});
-
- await expect(
- getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1)
- ).rejects.toThrow();
- });
-
- it('should handle index_not_found_exception errors gracefully', async () => {
- const esClient = esClientFactory(
- () => new Promise((resolve, reject) => reject(exceptionResponse))
- );
- const result = await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1);
+ const result = await getIndices(http, mockIndexPatternCreationType, 'kibana', false);
expect(result.length).toBe(0);
});
});
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts
index 9f75dc39a654c..c6a11de1bc4fc 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts
@@ -17,17 +17,31 @@
* under the License.
*/
-import { get, sortBy } from 'lodash';
+import { sortBy } from 'lodash';
+import { HttpStart } from 'kibana/public';
+import { i18n } from '@kbn/i18n';
import { IndexPatternCreationConfig } from '../../../../../index_pattern_management/public';
-import { DataPublicPluginStart } from '../../../../../data/public';
-import { MatchedIndex } from '../types';
+import { MatchedItem, ResolveIndexResponse, ResolveIndexResponseItemIndexAttrs } from '../types';
+
+const aliasLabel = i18n.translate('indexPatternManagement.aliasLabel', { defaultMessage: 'Alias' });
+const dataStreamLabel = i18n.translate('indexPatternManagement.dataStreamLabel', {
+ defaultMessage: 'Data stream',
+});
+
+const indexLabel = i18n.translate('indexPatternManagement.indexLabel', {
+ defaultMessage: 'Index',
+});
+
+const frozenLabel = i18n.translate('indexPatternManagement.frozenLabel', {
+ defaultMessage: 'Frozen',
+});
export async function getIndices(
- es: DataPublicPluginStart['search']['__LEGACY']['esClient'],
+ http: HttpStart,
indexPatternCreationType: IndexPatternCreationConfig,
rawPattern: string,
- limit: number
-): Promise {
+ showAllIndices: boolean
+): Promise {
const pattern = rawPattern.trim();
// Searching for `*:` fails for CCS environments. The search request
@@ -48,54 +62,58 @@ export async function getIndices(
return [];
}
- // We need to always provide a limit and not rely on the default
- if (!limit) {
- throw new Error('`getIndices()` was called without the required `limit` parameter.');
- }
-
- const params = {
- ignoreUnavailable: true,
- index: pattern,
- ignore: [404],
- body: {
- size: 0, // no hits
- aggs: {
- indices: {
- terms: {
- field: '_index',
- size: limit,
- },
- },
- },
- },
- };
+ const query = showAllIndices ? { expand_wildcards: 'all' } : undefined;
try {
- const response = await es.search(params);
- if (!response || response.error || !response.aggregations) {
- return [];
- }
-
- return sortBy(
- response.aggregations.indices.buckets
- .map((bucket: { key: string; doc_count: number }) => {
- return bucket.key;
- })
- .map((indexName: string) => {
- return {
- name: indexName,
- tags: indexPatternCreationType.getIndexTags(indexName),
- };
- }),
- 'name'
+ const response = await http.get(
+ `/internal/index-pattern-management/resolve_index/${pattern}`,
+ { query }
);
- } catch (err) {
- const type = get(err, 'body.error.caused_by.type');
- if (type === 'index_not_found_exception') {
- // This happens in a CSS environment when the controlling node returns a 500 even though the data
- // nodes returned a 404. Remove this when/if this is handled: https://github.com/elastic/elasticsearch/issues/27461
+ if (!response) {
return [];
}
- throw err;
+
+ return responseToItemArray(response, indexPatternCreationType);
+ } catch {
+ return [];
}
}
+
+export const responseToItemArray = (
+ response: ResolveIndexResponse,
+ indexPatternCreationType: IndexPatternCreationConfig
+): MatchedItem[] => {
+ const source: MatchedItem[] = [];
+
+ (response.indices || []).forEach((index) => {
+ const tags: MatchedItem['tags'] = [{ key: 'index', name: indexLabel, color: 'default' }];
+ const isFrozen = (index.attributes || []).includes(ResolveIndexResponseItemIndexAttrs.FROZEN);
+
+ tags.push(...indexPatternCreationType.getIndexTags(index.name));
+ if (isFrozen) {
+ tags.push({ name: frozenLabel, key: 'frozen', color: 'danger' });
+ }
+
+ source.push({
+ name: index.name,
+ tags,
+ item: index,
+ });
+ });
+ (response.aliases || []).forEach((alias) => {
+ source.push({
+ name: alias.name,
+ tags: [{ key: 'alias', name: aliasLabel, color: 'default' }],
+ item: alias,
+ });
+ });
+ (response.data_streams || []).forEach((dataStream) => {
+ source.push({
+ name: dataStream.name,
+ tags: [{ key: 'data_stream', name: dataStreamLabel, color: 'primary' }],
+ item: dataStream,
+ });
+ });
+
+ return sortBy(source, 'name');
+};
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts
index 65840aa64046d..c27eaa5ebc99e 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts
@@ -18,7 +18,7 @@
*/
import { getMatchedIndices } from './get_matched_indices';
-import { Tag } from '../types';
+import { Tag, MatchedItem } from '../types';
jest.mock('./../constants', () => ({
MAX_NUMBER_OF_MATCHING_INDICES: 6,
@@ -32,18 +32,18 @@ const indices = [
{ name: 'packetbeat', tags },
{ name: 'metricbeat', tags },
{ name: '.kibana', tags },
-];
+] as MatchedItem[];
const partialIndices = [
{ name: 'kibana', tags },
{ name: 'es', tags },
{ name: '.kibana', tags },
-];
+] as MatchedItem[];
const exactIndices = [
{ name: 'kibana', tags },
{ name: '.kibana', tags },
-];
+] as MatchedItem[];
describe('getMatchedIndices', () => {
it('should return all indices', () => {
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts
index 7e2eeb17ab387..dbb166597152e 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts
@@ -33,7 +33,7 @@ function isSystemIndex(index: string): boolean {
return false;
}
-function filterSystemIndices(indices: MatchedIndex[], isIncludingSystemIndices: boolean) {
+function filterSystemIndices(indices: MatchedItem[], isIncludingSystemIndices: boolean) {
if (!indices) {
return indices;
}
@@ -65,12 +65,12 @@ function filterSystemIndices(indices: MatchedIndex[], isIncludingSystemIndices:
We call this `exact` matches because ES is telling us exactly what it matches
*/
-import { MatchedIndex } from '../types';
+import { MatchedItem } from '../types';
export function getMatchedIndices(
- unfilteredAllIndices: MatchedIndex[],
- unfilteredPartialMatchedIndices: MatchedIndex[],
- unfilteredExactMatchedIndices: MatchedIndex[],
+ unfilteredAllIndices: MatchedItem[],
+ unfilteredPartialMatchedIndices: MatchedItem[],
+ unfilteredExactMatchedIndices: MatchedItem[],
isIncludingSystemIndices: boolean = false
) {
const allIndices = filterSystemIndices(unfilteredAllIndices, isIncludingSystemIndices);
diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
index 634bbd856ea86..b23924837ffb7 100644
--- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
+++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts
@@ -17,12 +17,54 @@
* under the License.
*/
-export interface MatchedIndex {
+export interface MatchedItem {
name: string;
tags: Tag[];
+ item: {
+ name: string;
+ backing_indices?: string[];
+ timestamp_field?: string;
+ indices?: string[];
+ aliases?: string[];
+ attributes?: ResolveIndexResponseItemIndexAttrs[];
+ data_stream?: string;
+ };
+}
+
+export interface ResolveIndexResponse {
+ indices?: ResolveIndexResponseItemIndex[];
+ aliases?: ResolveIndexResponseItemAlias[];
+ data_streams?: ResolveIndexResponseItemDataStream[];
+}
+
+export interface ResolveIndexResponseItem {
+ name: string;
+}
+
+export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem {
+ backing_indices: string[];
+ timestamp_field: string;
+}
+
+export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem {
+ indices: string[];
+}
+
+export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem {
+ aliases?: string[];
+ attributes?: ResolveIndexResponseItemIndexAttrs[];
+ data_stream?: string;
+}
+
+export enum ResolveIndexResponseItemIndexAttrs {
+ OPEN = 'open',
+ CLOSED = 'closed',
+ HIDDEN = 'hidden',
+ FROZEN = 'frozen',
}
export interface Tag {
name: string;
key: string;
+ color: string;
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap
index 6bc99c356592e..7a7545580d82a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap
+++ b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap
@@ -836,7 +836,6 @@ exports[`FieldEditor should show deprecated lang warning 1`] = `
testlang
,
"painlessLink":
,
"scriptsInAggregation":
Please familiarize yourself with
-
-
+
and with
-
-
+
before using scripted fields.
diff --git a/src/plugins/index_pattern_management/public/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts
index 93574cde7dc85..ec8100db42085 100644
--- a/src/plugins/index_pattern_management/public/mocks.ts
+++ b/src/plugins/index_pattern_management/public/mocks.ts
@@ -76,6 +76,13 @@ const createInstance = async () => {
};
};
+const docLinks = {
+ links: {
+ indexPatterns: {},
+ scriptedFields: {},
+ },
+};
+
const createIndexPatternManagmentContext = () => {
const {
chrome,
@@ -84,7 +91,6 @@ const createIndexPatternManagmentContext = () => {
uiSettings,
notifications,
overlays,
- docLinks,
} = coreMock.createStart();
const { http } = coreMock.createSetup();
const data = dataPluginMock.createStartContract();
diff --git a/src/plugins/index_pattern_management/public/service/creation/config.ts b/src/plugins/index_pattern_management/public/service/creation/config.ts
index 95a91fd7594ca..04510b1d64e1e 100644
--- a/src/plugins/index_pattern_management/public/service/creation/config.ts
+++ b/src/plugins/index_pattern_management/public/service/creation/config.ts
@@ -18,7 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { MatchedIndex } from '../../components/create_index_pattern_wizard/types';
+import { MatchedItem } from '../../components/create_index_pattern_wizard/types';
const indexPatternTypeName = i18n.translate(
'indexPatternManagement.editIndexPattern.createIndex.defaultTypeName',
@@ -105,7 +105,7 @@ export class IndexPatternCreationConfig {
return [];
}
- public checkIndicesForErrors(indices: MatchedIndex[]) {
+ public checkIndicesForErrors(indices: MatchedItem[]) {
return undefined;
}
diff --git a/src/plugins/index_pattern_management/server/index.ts b/src/plugins/index_pattern_management/server/index.ts
new file mode 100644
index 0000000000000..02a4631589832
--- /dev/null
+++ b/src/plugins/index_pattern_management/server/index.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'src/core/server';
+import { IndexPatternManagementPlugin } from './plugin';
+
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new IndexPatternManagementPlugin(initializerContext);
+}
diff --git a/src/plugins/index_pattern_management/server/plugin.ts b/src/plugins/index_pattern_management/server/plugin.ts
new file mode 100644
index 0000000000000..ecca45cbcc453
--- /dev/null
+++ b/src/plugins/index_pattern_management/server/plugin.ts
@@ -0,0 +1,70 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext, CoreSetup, Plugin } from 'src/core/server';
+import { schema } from '@kbn/config-schema';
+
+export class IndexPatternManagementPlugin implements Plugin {
+ constructor(initializerContext: PluginInitializerContext) {}
+
+ public setup(core: CoreSetup) {
+ const router = core.http.createRouter();
+
+ router.get(
+ {
+ path: '/internal/index-pattern-management/resolve_index/{query}',
+ validate: {
+ params: schema.object({
+ query: schema.string(),
+ }),
+ query: schema.object({
+ expand_wildcards: schema.maybe(
+ schema.oneOf([
+ schema.literal('all'),
+ schema.literal('open'),
+ schema.literal('closed'),
+ schema.literal('hidden'),
+ schema.literal('none'),
+ ])
+ ),
+ }),
+ },
+ },
+ async (context, req, res) => {
+ const queryString = req.query.expand_wildcards
+ ? { expand_wildcards: req.query.expand_wildcards }
+ : null;
+ const result = await context.core.elasticsearch.legacy.client.callAsCurrentUser(
+ 'transport.request',
+ {
+ method: 'GET',
+ path: `/_resolve/index/${encodeURIComponent(req.params.query)}${
+ queryString ? '?' + new URLSearchParams(queryString).toString() : ''
+ }`,
+ }
+ );
+ return res.ok({ body: result });
+ }
+ );
+ }
+
+ public start() {}
+
+ public stop() {}
+}
diff --git a/src/plugins/usage_collection/server/collector/collector.test.ts b/src/plugins/usage_collection/server/collector/collector.test.ts
new file mode 100644
index 0000000000000..a3e2425c1f122
--- /dev/null
+++ b/src/plugins/usage_collection/server/collector/collector.test.ts
@@ -0,0 +1,213 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { loggingSystemMock } from '../../../../core/server/mocks';
+import { Collector } from './collector';
+import { UsageCollector } from './usage_collector';
+
+const logger = loggingSystemMock.createLogger();
+
+describe('collector', () => {
+ describe('options validations', () => {
+ it('should not accept an empty object', () => {
+ // @ts-expect-error
+ expect(() => new Collector(logger, {})).toThrowError(
+ 'Collector must be instantiated with a options.type string property'
+ );
+ });
+
+ it('should fail if init is not a function', () => {
+ expect(
+ () =>
+ new Collector(logger, {
+ type: 'my_test_collector',
+ // @ts-expect-error
+ init: 1,
+ })
+ ).toThrowError(
+ 'If init property is passed, Collector must be instantiated with a options.init as a function property'
+ );
+ });
+
+ it('should fail if fetch is not defined', () => {
+ expect(
+ () =>
+ // @ts-expect-error
+ new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ })
+ ).toThrowError('Collector must be instantiated with a options.fetch function property');
+ });
+
+ it('should fail if fetch is not a function', () => {
+ expect(
+ () =>
+ new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ // @ts-expect-error
+ fetch: 1,
+ })
+ ).toThrowError('Collector must be instantiated with a options.fetch function property');
+ });
+
+ it('should be OK with all mandatory properties', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => ({ testPass: 100 }),
+ });
+ expect(collector).toBeDefined();
+ });
+
+ it('should fallback when isReady is not provided', () => {
+ const fetchOutput = { testPass: 100 };
+ // @ts-expect-error not providing isReady to test the logic fallback
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ fetch: () => fetchOutput,
+ });
+ expect(collector.isReady()).toBe(true);
+ });
+ });
+
+ describe('formatForBulkUpload', () => {
+ it('should use the default formatter', () => {
+ const fetchOutput = { testPass: 100 };
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => fetchOutput,
+ });
+ expect(collector.formatForBulkUpload(fetchOutput)).toStrictEqual({
+ type: 'my_test_collector',
+ payload: fetchOutput,
+ });
+ });
+
+ it('should use a custom formatter', () => {
+ const fetchOutput = { testPass: 100 };
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => fetchOutput,
+ formatForBulkUpload: (a) => ({ type: 'other_value', payload: { nested: a } }),
+ });
+ expect(collector.formatForBulkUpload(fetchOutput)).toStrictEqual({
+ type: 'other_value',
+ payload: { nested: fetchOutput },
+ });
+ });
+
+ it("should use UsageCollector's default formatter", () => {
+ const fetchOutput = { testPass: 100 };
+ const collector = new UsageCollector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => fetchOutput,
+ });
+ expect(collector.formatForBulkUpload(fetchOutput)).toStrictEqual({
+ type: 'kibana_stats',
+ payload: { usage: { my_test_collector: fetchOutput } },
+ });
+ });
+ });
+
+ describe('schema TS validations', () => {
+ // These tests below are used to ensure types inference is working as expected.
+ // We don't intend to test any logic as such, just the relation between the types in `fetch` and `schema`.
+ // Using ts-expect-error when an error is expected will fail the compilation if there is not such error.
+
+ test('when fetch returns a simple object', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => ({ testPass: 100 }),
+ schema: {
+ testPass: { type: 'long' },
+ },
+ });
+ expect(collector).toBeDefined();
+ });
+
+ test('when fetch returns array-properties and schema', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => ({ testPass: [{ name: 'a', value: 100 }] }),
+ schema: {
+ testPass: { name: { type: 'keyword' }, value: { type: 'long' } },
+ },
+ });
+ expect(collector).toBeDefined();
+ });
+
+ test('TS should complain when schema is missing some properties', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ fetch: () => ({ testPass: [{ name: 'a', value: 100 }], otherProp: 1 }),
+ // @ts-expect-error
+ schema: {
+ testPass: { name: { type: 'keyword' }, value: { type: 'long' } },
+ },
+ });
+ expect(collector).toBeDefined();
+ });
+
+ test('TS complains if schema misses any of the optional properties', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ // Need to be explicit with the returned type because TS struggles to identify it
+ fetch: (): { testPass?: Array<{ name: string; value: number }>; otherProp?: number } => {
+ if (Math.random() > 0.5) {
+ return { testPass: [{ name: 'a', value: 100 }] };
+ }
+ return { otherProp: 1 };
+ },
+ // @ts-expect-error
+ schema: {
+ testPass: { name: { type: 'keyword' }, value: { type: 'long' } },
+ },
+ });
+ expect(collector).toBeDefined();
+ });
+
+ test('schema defines all the optional properties', () => {
+ const collector = new Collector(logger, {
+ type: 'my_test_collector',
+ isReady: () => false,
+ // Need to be explicit with the returned type because TS struggles to identify it
+ fetch: (): { testPass?: Array<{ name: string; value: number }>; otherProp?: number } => {
+ if (Math.random() > 0.5) {
+ return { testPass: [{ name: 'a', value: 100 }] };
+ }
+ return { otherProp: 1 };
+ },
+ schema: {
+ testPass: { name: { type: 'keyword' }, value: { type: 'long' } },
+ otherProp: { type: 'long' },
+ },
+ });
+ expect(collector).toBeDefined();
+ });
+ });
+});
diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts
index 9ae63b9f50e42..d57700024c088 100644
--- a/src/plugins/usage_collection/server/collector/collector.ts
+++ b/src/plugins/usage_collection/server/collector/collector.ts
@@ -34,20 +34,20 @@ export interface SchemaField {
type: string;
}
-type Purify = { [P in T]: T }[T];
+export type RecursiveMakeSchemaFrom = U extends object
+ ? MakeSchemaFrom
+ : { type: AllowedSchemaTypes };
export type MakeSchemaFrom = {
- [Key in Purify>]: Base[Key] extends Array
- ? { type: AllowedSchemaTypes }
- : Base[Key] extends object
- ? MakeSchemaFrom
- : { type: AllowedSchemaTypes };
+ [Key in keyof Base]: Base[Key] extends Array
+ ? RecursiveMakeSchemaFrom
+ : RecursiveMakeSchemaFrom ;
};
export interface CollectorOptions {
type: string;
init?: Function;
- schema?: MakeSchemaFrom;
+ schema?: MakeSchemaFrom>; // Using Required to enforce all optional keys in the object
fetch: (callCluster: LegacyAPICaller) => Promise | T;
/*
* A hook for allowing the fetched data payload to be organized into a typed
diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js
index 906f0b83e99e7..949a01ff7873a 100644
--- a/test/functional/apps/discover/_discover.js
+++ b/test/functional/apps/discover/_discover.js
@@ -218,6 +218,8 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.awaitKibanaChrome();
await queryBar.setQuery('');
+ // To remove focus of the of the search bar so date/time picker can show
+ await PageObjects.discover.selectIndexPattern(defaultSettings.defaultIndex);
await PageObjects.timePicker.setDefaultAbsoluteRange();
log.debug(
diff --git a/test/functional/apps/management/_create_index_pattern_wizard.js b/test/functional/apps/management/_create_index_pattern_wizard.js
index 8209f3e1ac9d6..cb8b5a6ddc65f 100644
--- a/test/functional/apps/management/_create_index_pattern_wizard.js
+++ b/test/functional/apps/management/_create_index_pattern_wizard.js
@@ -22,6 +22,7 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
+ const es = getService('legacyEs');
const PageObjects = getPageObjects(['settings', 'common']);
describe('"Create Index Pattern" wizard', function () {
@@ -48,5 +49,59 @@ export default function ({ getService, getPageObjects }) {
expect(isEnabled).to.be.ok();
});
});
+
+ describe('data streams', () => {
+ it('can be an index pattern', async () => {
+ await es.transport.request({
+ path: '/_index_template/generic-logs',
+ method: 'PUT',
+ body: {
+ index_patterns: ['logs-*', 'test_data_stream'],
+ template: {
+ mappings: {
+ properties: {
+ '@timestamp': {
+ type: 'date',
+ },
+ },
+ },
+ },
+ data_stream: {
+ timestamp_field: '@timestamp',
+ },
+ },
+ });
+
+ await es.transport.request({
+ path: '/_data_stream/test_data_stream',
+ method: 'PUT',
+ });
+
+ await PageObjects.settings.createIndexPattern('test_data_stream', false);
+
+ await es.transport.request({
+ path: '/_data_stream/test_data_stream',
+ method: 'DELETE',
+ });
+ });
+ });
+
+ describe('index alias', () => {
+ it('can be an index pattern', async () => {
+ await es.transport.request({
+ path: '/blogs/_doc',
+ method: 'POST',
+ body: { user: 'matt', message: 20 },
+ });
+
+ await es.transport.request({
+ path: '/_aliases',
+ method: 'POST',
+ body: { actions: [{ add: { index: 'blogs', alias: 'alias1' } }] },
+ });
+
+ await PageObjects.settings.createIndexPattern('alias1', false);
+ });
+ });
});
}
diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts
index 27814060e70c1..78f659a064a0c 100644
--- a/test/functional/services/remote/webdriver.ts
+++ b/test/functional/services/remote/webdriver.ts
@@ -88,6 +88,7 @@ async function attemptToCreateCommand(
) {
const attemptId = ++attemptCounter;
log.debug('[webdriver] Creating session');
+ const remoteSessionUrl = process.env.REMOTE_SESSION_URL;
const buildDriverInstance = async () => {
switch (browserType) {
@@ -133,11 +134,20 @@ async function attemptToCreateCommand(
chromeCapabilities.set('goog:loggingPrefs', { browser: 'ALL' });
chromeCapabilities.setAcceptInsecureCerts(config.acceptInsecureCerts);
- const session = await new Builder()
- .forBrowser(browserType)
- .withCapabilities(chromeCapabilities)
- .setChromeService(new chrome.ServiceBuilder(chromeDriver.path).enableVerboseLogging())
- .build();
+ let session;
+ if (remoteSessionUrl) {
+ session = await new Builder()
+ .forBrowser(browserType)
+ .withCapabilities(chromeCapabilities)
+ .usingServer(remoteSessionUrl)
+ .build();
+ } else {
+ session = await new Builder()
+ .forBrowser(browserType)
+ .withCapabilities(chromeCapabilities)
+ .setChromeService(new chrome.ServiceBuilder(chromeDriver.path).enableVerboseLogging())
+ .build();
+ }
return {
session,
@@ -284,11 +294,19 @@ async function attemptToCreateCommand(
logLevel: 'TRACE',
});
- const session = await new Builder()
- .forBrowser(browserType)
- .withCapabilities(ieCapabilities)
- .build();
-
+ let session;
+ if (remoteSessionUrl) {
+ session = await new Builder()
+ .forBrowser(browserType)
+ .withCapabilities(ieCapabilities)
+ .usingServer(remoteSessionUrl)
+ .build();
+ } else {
+ session = await new Builder()
+ .forBrowser(browserType)
+ .withCapabilities(ieCapabilities)
+ .build();
+ }
return {
session,
consoleLog$: Rx.EMPTY,
diff --git a/vars/tasks.groovy b/vars/tasks.groovy
index 9de4c78322d3e..3ff9a7b4850ae 100644
--- a/vars/tasks.groovy
+++ b/vars/tasks.groovy
@@ -42,7 +42,13 @@ def test() {
}
def functionalOss(Map params = [:]) {
- def config = params ?: [ciGroups: true, firefox: true, accessibility: true, pluginFunctional: true, visualRegression: false]
+ def config = params ?: [
+ ciGroups: true,
+ firefox: !githubPr.isPr(),
+ accessibility: true,
+ pluginFunctional: true,
+ visualRegression: false
+ ]
task {
kibanaPipeline.buildOss(6)
@@ -73,7 +79,7 @@ def functionalOss(Map params = [:]) {
def functionalXpack(Map params = [:]) {
def config = params ?: [
ciGroups: true,
- firefox: true,
+ firefox: !githubPr.isPr(),
accessibility: true,
pluginFunctional: true,
savedObjectsFieldMetrics: true,
diff --git a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json
index c374cbb3bb146..4b10dab5d1ae5 100644
--- a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json
+++ b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json
@@ -4146,9 +4146,6 @@
"config_revision": {
"type": ["number", "null"]
},
- "config_newest_revision": {
- "type": "number"
- },
"last_checkin": {
"type": "string"
},
diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts
index 27f0c61685fd4..1f4718acc2c1f 100644
--- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts
+++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts
@@ -81,7 +81,6 @@ interface AgentBase {
default_api_key_id?: string;
config_id?: string;
config_revision?: number | null;
- config_newest_revision?: number;
last_checkin?: string;
user_provided_metadata: AgentMetadata;
local_metadata: AgentMetadata;
diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts
index 23e31227cbf3c..a34038d4fba04 100644
--- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts
+++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts
@@ -42,6 +42,8 @@ export enum AgentAssetType {
input = 'input',
}
+export type RegistryRelease = 'ga' | 'beta' | 'experimental';
+
// from /package/{name}
// type Package struct at https://github.com/elastic/package-registry/blob/master/util/package.go
// https://github.com/elastic/package-registry/blob/master/docs/api/package.json
@@ -49,6 +51,7 @@ export interface RegistryPackage {
name: string;
title?: string;
version: string;
+ release?: RegistryRelease;
readme?: string;
description: string;
type: string;
@@ -114,6 +117,7 @@ export type RegistrySearchResult = Pick<
| 'name'
| 'title'
| 'version'
+ | 'release'
| 'description'
| 'type'
| 'icons'
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts
index c5035d2d44432..1901b8c0c7039 100644
--- a/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts
@@ -12,13 +12,21 @@ import {
PackageInfo,
} from '../models/epm';
+export interface GetCategoriesRequest {
+ query: {
+ experimental?: boolean;
+ };
+}
+
export interface GetCategoriesResponse {
response: CategorySummaryList;
success: boolean;
}
+
export interface GetPackagesRequest {
query: {
category?: string;
+ experimental?: boolean;
};
}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts
index 011e0c69f2683..e5a7191372e9c 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts
@@ -6,7 +6,7 @@
import { useEffect, useState } from 'react';
import { ICON_TYPES } from '@elastic/eui';
-import { PackageInfo, PackageListItem } from '../../../../common/types/models';
+import { PackageInfo, PackageListItem } from '../types';
import { useLinks } from '../sections/epm/hooks';
import { sendGetPackageInfoByKey } from './index';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts
index 64bee1763b08b..40a22f6b44d50 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts
@@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { HttpFetchQuery } from 'src/core/public';
import { useRequest, sendRequest } from './use_request';
import { epmRouteService } from '../../services';
import {
+ GetCategoriesRequest,
GetCategoriesResponse,
+ GetPackagesRequest,
GetPackagesResponse,
GetLimitedPackagesResponse,
GetInfoResponse,
@@ -16,18 +17,19 @@ import {
DeletePackageResponse,
} from '../../types';
-export const useGetCategories = () => {
+export const useGetCategories = (query: GetCategoriesRequest['query'] = {}) => {
return useRequest({
path: epmRouteService.getCategoriesPath(),
method: 'get',
+ query: { experimental: true, ...query },
});
};
-export const useGetPackages = (query: HttpFetchQuery = {}) => {
+export const useGetPackages = (query: GetPackagesRequest['query'] = {}) => {
return useRequest({
path: epmRouteService.getListPath(),
method: 'get',
- query,
+ query: { experimental: true, ...query },
});
};
@@ -52,6 +54,13 @@ export const sendGetPackageInfoByKey = (pkgkey: string) => {
});
};
+export const useGetFileByPath = (filePath: string) => {
+ return useRequest({
+ path: epmRouteService.getFilePath(filePath),
+ method: 'get',
+ });
+};
+
export const sendGetFileByPath = (filePath: string) => {
return sendRequest({
path: epmRouteService.getFilePath(filePath),
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/assets_facet_group.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/assets_facet_group.tsx
index ac74b09ab4391..24b4baeaa092b 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/assets_facet_group.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/assets_facet_group.tsx
@@ -30,19 +30,24 @@ import {
ServiceTitleMap,
} from '../constants';
-export function AssetsFacetGroup({ assets }: { assets: AssetsGroupedByServiceByType }) {
- const FirstHeaderRow = styled(EuiFlexGroup)`
- padding: 0 0 ${(props) => props.theme.eui.paddingSizes.m} 0;
- `;
+const FirstHeaderRow = styled(EuiFlexGroup)`
+ padding: 0 0 ${(props) => props.theme.eui.paddingSizes.m} 0;
+`;
+
+const HeaderRow = styled(EuiFlexGroup)`
+ padding: ${(props) => props.theme.eui.paddingSizes.m} 0;
+`;
- const HeaderRow = styled(EuiFlexGroup)`
- padding: ${(props) => props.theme.eui.paddingSizes.m} 0;
- `;
+const FacetGroup = styled(EuiFacetGroup)`
+ flex-grow: 0;
+`;
- const FacetGroup = styled(EuiFacetGroup)`
- flex-grow: 0;
- `;
+const FacetButton = styled(EuiFacetButton)`
+ padding: '${(props) => props.theme.eui.paddingSizes.xs} 0';
+ height: 'unset';
+`;
+export function AssetsFacetGroup({ assets }: { assets: AssetsGroupedByServiceByType }) {
return (
{entries(assets).map(([service, typeToParts], index) => {
@@ -77,10 +82,6 @@ export function AssetsFacetGroup({ assets }: { assets: AssetsGroupedByServiceByT
// only kibana assets have icons
const iconType = type in AssetIcons && AssetIcons[type];
const iconNode = iconType ? : '';
- const FacetButton = styled(EuiFacetButton)`
- padding: '${(props) => props.theme.eui.paddingSizes.xs} 0';
- height: 'unset';
- `;
return (
+ parseFloat(props.theme.eui.euiSize) * 6 + parseFloat(props.theme.eui.spacerSizes.xl) * 2}px;
+ height: 1px;
+`;
+
+const Panel = styled(EuiPanel)`
+ padding: ${(props) => props.theme.eui.spacerSizes.xl};
+ margin-bottom: -100%;
+ svg,
+ img {
+ height: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px;
+ width: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px;
+ }
+ .euiFlexItem {
+ height: ${(props) => parseFloat(props.theme.eui.euiSize) * 6}px;
+ justify-content: center;
+ }
+`;
+
+export function IconPanel({
+ packageName,
+ version,
+ icons,
+}: Pick) {
+ const iconType = usePackageIconType({ packageName, version, icons });
-export function IconPanel({ iconType }: { iconType: IconType }) {
- const Panel = styled(EuiPanel)`
- /* 🤢🤷 https://www.styled-components.com/docs/faqs#how-can-i-override-styles-with-higher-specificity */
- &&& {
- position: absolute;
- text-align: center;
- vertical-align: middle;
- padding: ${(props) => props.theme.eui.spacerSizes.xl};
- svg,
- img {
- height: ${(props) => props.theme.eui.euiKeyPadMenuSize};
- width: ${(props) => props.theme.eui.euiKeyPadMenuSize};
- }
- }
- `;
+ return (
+
+
+
+
+
+ );
+}
+export function LoadingIconPanel() {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx
index acdcd5b9a3406..3f0803af6daae 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx
@@ -3,13 +3,20 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiIcon } from '@elastic/eui';
import React from 'react';
-import styled from 'styled-components';
+import { EuiIconTip, EuiIconProps } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
-export const StyledAlert = styled(EuiIcon)`
- color: ${(props) => props.theme.eui.euiColorWarning};
- padding: 0 5px;
-`;
-
-export const UpdateIcon = () => ;
+export const UpdateIcon = ({ size = 'm' }: { size?: EuiIconProps['size'] }) => (
+
+);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/nav_button_back.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/nav_button_back.tsx
deleted file mode 100644
index 3fcf9758368de..0000000000000
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/nav_button_back.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { EuiButtonEmpty } from '@elastic/eui';
-import React from 'react';
-import styled from 'styled-components';
-
-export function NavButtonBack({ href, text }: { href: string; text: string }) {
- const ButtonEmpty = styled(EuiButtonEmpty)`
- margin-right: ${(props) => props.theme.eui.spacerSizes.xl};
- `;
- return (
-
- {text}
-
- );
-}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx
index e3d8cdc8f4985..cf98f9dc90230 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx
@@ -9,12 +9,9 @@ import { EuiCard } from '@elastic/eui';
import { PackageInfo, PackageListItem } from '../../../types';
import { useLink } from '../../../hooks';
import { PackageIcon } from '../../../components/package_icon';
+import { RELEASE_BADGE_LABEL, RELEASE_BADGE_DESCRIPTION } from './release_badge';
-export interface BadgeProps {
- showInstalledBadge?: boolean;
-}
-
-type PackageCardProps = (PackageListItem | PackageInfo) & BadgeProps;
+type PackageCardProps = PackageListItem | PackageInfo;
// adding the `href` causes EuiCard to use a `a` instead of a `button`
// `a` tags use `euiLinkColor` which results in blueish Badge text
@@ -27,7 +24,7 @@ export function PackageCard({
name,
title,
version,
- showInstalledBadge,
+ release,
status,
icons,
...restProps
@@ -41,12 +38,14 @@ export function PackageCard({
return (
}
href={getHref('integration_details', { pkgkey: `${name}-${urlVersion}` })}
+ betaBadgeLabel={release && release !== 'ga' ? RELEASE_BADGE_LABEL[release] : undefined}
+ betaBadgeTooltipContent={
+ release && release !== 'ga' ? RELEASE_BADGE_DESCRIPTION[release] : undefined
+ }
/>
);
}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx
index dbf454acd2b74..0c1199f7c8867 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx
@@ -20,22 +20,16 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { Loading } from '../../../components';
import { PackageList } from '../../../types';
import { useLocalSearch, searchIdField } from '../hooks';
-import { BadgeProps, PackageCard } from './package_card';
+import { PackageCard } from './package_card';
-type ListProps = {
+interface ListProps {
isLoading?: boolean;
controls?: ReactNode;
title: string;
list: PackageList;
-} & BadgeProps;
+}
-export function PackageListGrid({
- isLoading,
- controls,
- title,
- list,
- showInstalledBadge,
-}: ListProps) {
+export function PackageListGrid({ isLoading, controls, title, list }: ListProps) {
const initialQuery = EuiSearchBar.Query.MATCH_ALL;
const [query, setQuery] = useState(initialQuery);
@@ -71,7 +65,7 @@ export function PackageListGrid({
.includes(item[searchIdField])
)
: list;
- gridContent = ;
+ gridContent = ;
}
return (
@@ -108,16 +102,16 @@ function ControlsColumn({ controls, title }: ControlsColumnProps) {
- {controls}
+ {controls}
);
}
-type GridColumnProps = {
+interface GridColumnProps {
list: PackageList;
-} & BadgeProps;
+}
function GridColumn({ list }: GridColumnProps) {
return (
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/release_badge.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/release_badge.ts
new file mode 100644
index 0000000000000..f3520b4e7a9b3
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/release_badge.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { i18n } from '@kbn/i18n';
+import { RegistryRelease } from '../../../types';
+
+export const RELEASE_BADGE_LABEL: { [key in Exclude]: string } = {
+ beta: i18n.translate('xpack.ingestManager.epm.releaseBadge.betaLabel', {
+ defaultMessage: 'Beta',
+ }),
+ experimental: i18n.translate('xpack.ingestManager.epm.releaseBadge.experimentalLabel', {
+ defaultMessage: 'Experimental',
+ }),
+};
+
+export const RELEASE_BADGE_DESCRIPTION: { [key in Exclude]: string } = {
+ beta: i18n.translate('xpack.ingestManager.epm.releaseBadge.betaDescription', {
+ defaultMessage: 'This integration is not recommended for use in production environments.',
+ }),
+ experimental: i18n.translate('xpack.ingestManager.epm.releaseBadge.experimentalDescription', {
+ defaultMessage: 'This integration may have breaking changes or be removed in a future release.',
+ }),
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/content.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/content.tsx
index c9a8cabdf414b..f53b4e9150ca1 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/content.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/content.tsx
@@ -16,22 +16,22 @@ import { SideNavLinks } from './side_nav_links';
import { PackageConfigsPanel } from './package_configs_panel';
import { SettingsPanel } from './settings_panel';
-type ContentProps = PackageInfo & Pick & { hasIconPanel: boolean };
-export function Content(props: ContentProps) {
- const { hasIconPanel, name, panel, version } = props;
- const SideNavColumn = hasIconPanel
- ? styled(LeftColumn)`
- /* 🤢🤷 https://www.styled-components.com/docs/faqs#how-can-i-override-styles-with-higher-specificity */
- &&& {
- margin-top: 77px;
- }
- `
- : LeftColumn;
+type ContentProps = PackageInfo & Pick;
+
+const SideNavColumn = styled(LeftColumn)`
+ /* 🤢🤷 https://www.styled-components.com/docs/faqs#how-can-i-override-styles-with-higher-specificity */
+ &&& {
+ margin-top: 77px;
+ }
+`;
+
+// fixes IE11 problem with nested flex items
+const ContentFlexGroup = styled(EuiFlexGroup)`
+ flex: 0 0 auto !important;
+`;
- // fixes IE11 problem with nested flex items
- const ContentFlexGroup = styled(EuiFlexGroup)`
- flex: 0 0 auto !important;
- `;
+export function Content(props: ContentProps) {
+ const { name, panel, version } = props;
return (
@@ -75,13 +75,13 @@ function RightColumnContent(props: RightColumnContentProps) {
const { assets, panel } = props;
switch (panel) {
case 'overview':
- return (
+ return assets ? (
- );
+ ) : null;
default:
return ;
}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx
deleted file mode 100644
index 875a8f5c5c127..0000000000000
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React, { Fragment } from 'react';
-import styled from 'styled-components';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiTitle, IconType, EuiButton } from '@elastic/eui';
-import { PackageInfo } from '../../../../types';
-import { useCapabilities, useLink } from '../../../../hooks';
-import { IconPanel } from '../../components/icon_panel';
-import { NavButtonBack } from '../../components/nav_button_back';
-import { CenterColumn, LeftColumn, RightColumn } from './layout';
-import { UpdateIcon } from '../../components/icons';
-
-const FullWidthNavRow = styled(EuiPage)`
- /* no left padding so link is against column left edge */
- padding-left: 0;
-`;
-
-const Text = styled.span`
- margin-right: ${(props) => props.theme.eui.euiSizeM};
-`;
-
-type HeaderProps = PackageInfo & { iconType?: IconType };
-
-export function Header(props: HeaderProps) {
- const { iconType, name, title, version, latestVersion } = props;
-
- let installedVersion;
- if ('savedObject' in props) {
- installedVersion = props.savedObject.attributes.version;
- }
- const hasWriteCapabilites = useCapabilities().write;
- const { getHref } = useLink();
- const updateAvailable = installedVersion && installedVersion < latestVersion ? true : false;
- return (
-
-
-
-
-
- {iconType ? (
-
-
-
- ) : null}
-
-
-
- {title}
-
-
- {version} {updateAvailable && }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx
index 505687068cf42..3267fbbe3733c 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx
@@ -3,15 +3,37 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiPage, EuiPageBody, EuiPageProps } from '@elastic/eui';
-import React, { Fragment, useEffect, useState } from 'react';
+import React, { useEffect, useState, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import styled from 'styled-components';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiText,
+ EuiSpacer,
+ EuiBetaBadge,
+ EuiButton,
+ EuiDescriptionList,
+ EuiDescriptionListTitle,
+ EuiDescriptionListDescription,
+} from '@elastic/eui';
import { DetailViewPanelName, InstallStatus, PackageInfo } from '../../../../types';
-import { sendGetPackageInfoByKey, usePackageIconType, useBreadcrumbs } from '../../../../hooks';
+import { Loading, Error } from '../../../../components';
+import {
+ useGetPackageInfoByKey,
+ useBreadcrumbs,
+ useLink,
+ useCapabilities,
+} from '../../../../hooks';
+import { WithHeaderLayout } from '../../../../layouts';
import { useSetPackageInstallStatus } from '../../hooks';
+import { IconPanel, LoadingIconPanel } from '../../components/icon_panel';
+import { RELEASE_BADGE_LABEL, RELEASE_BADGE_DESCRIPTION } from '../../components/release_badge';
+import { UpdateIcon } from '../../components/icons';
import { Content } from './content';
-import { Header } from './header';
export const DEFAULT_PANEL: DetailViewPanelName = 'overview';
@@ -20,66 +42,202 @@ export interface DetailParams {
panel?: DetailViewPanelName;
}
+const Divider = styled.div`
+ width: 0;
+ height: 100%;
+ border-left: ${(props) => props.theme.eui.euiBorderThin};
+`;
+
+// Allows child text to be truncated
+const FlexItemWithMinWidth = styled(EuiFlexItem)`
+ min-width: 0px;
+`;
+
+function Breadcrumbs({ packageTitle }: { packageTitle: string }) {
+ useBreadcrumbs('integration_details', { pkgTitle: packageTitle });
+ return null;
+}
+
export function Detail() {
// TODO: fix forced cast if possible
const { pkgkey, panel = DEFAULT_PANEL } = useParams() as DetailParams;
+ const { getHref } = useLink();
+ const hasWriteCapabilites = useCapabilities().write;
- const [info, setInfo] = useState(null);
+ // Package info state
+ const [packageInfo, setPackageInfo] = useState(null);
const setPackageInstallStatus = useSetPackageInstallStatus();
+ const updateAvailable =
+ packageInfo &&
+ 'savedObject' in packageInfo &&
+ packageInfo.savedObject &&
+ packageInfo.savedObject.attributes.version < packageInfo.latestVersion;
+
+ // Fetch package info
+ const { data: packageInfoData, error: packageInfoError, isLoading } = useGetPackageInfoByKey(
+ pkgkey
+ );
+
+ // Track install status state
useEffect(() => {
- sendGetPackageInfoByKey(pkgkey).then((response) => {
- const packageInfo = response.data?.response;
- const title = packageInfo?.title;
- const name = packageInfo?.name;
+ if (packageInfoData?.response) {
+ const packageInfoResponse = packageInfoData.response;
+ setPackageInfo(packageInfoResponse);
+
let installedVersion;
- if (packageInfo && 'savedObject' in packageInfo) {
- installedVersion = packageInfo.savedObject.attributes.version;
+ const { name } = packageInfoData.response;
+ if ('savedObject' in packageInfoResponse) {
+ installedVersion = packageInfoResponse.savedObject.attributes.version;
}
- const status: InstallStatus = packageInfo?.status as any;
-
- // track install status state
+ const status: InstallStatus = packageInfoResponse?.status as any;
if (name) {
setPackageInstallStatus({ name, status, version: installedVersion || null });
}
- if (packageInfo) {
- setInfo({ ...packageInfo, title: title || '' });
- }
- });
- }, [pkgkey, setPackageInstallStatus]);
-
- if (!info) return null;
-
- return ;
-}
+ }
+ }, [packageInfoData, setPackageInstallStatus, setPackageInfo]);
-const FullWidthHeader = styled(EuiPage)`
- border-bottom: ${(props) => props.theme.eui.euiBorderThin};
- padding-bottom: ${(props) => props.theme.eui.paddingSizes.xl};
-`;
+ const headerLeftContent = useMemo(
+ () => (
+
+
+ {/* Allows button to break out of full width */}
+
+
+
+
+
+
+
+
+
+ {isLoading || !packageInfo ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {/* Render space in place of package name while package info loads to prevent layout from jumping around */}
+ {packageInfo?.title || '\u00A0'}
+
+
+ {packageInfo?.release && packageInfo.release !== 'ga' ? (
+
+
+
+ ) : null}
+
+
+
+
+
+ ),
+ [getHref, isLoading, packageInfo]
+ );
-const FullWidthContent = styled(EuiPage)`
- background-color: ${(props) => props.theme.eui.euiColorEmptyShade};
- padding-top: ${(props) => parseInt(props.theme.eui.paddingSizes.xl, 10) * 1.25}px;
- flex-grow: 1;
-`;
+ const headerRightContent = useMemo(
+ () =>
+ packageInfo ? (
+ <>
+
+
+ {[
+ {
+ label: i18n.translate('xpack.ingestManager.epm.versionLabel', {
+ defaultMessage: 'Version',
+ }),
+ content: (
+
+ {packageInfo.version}
+ {updateAvailable ? (
+
+
+
+ ) : null}
+
+ ),
+ },
+ { isDivider: true },
+ {
+ content: (
+
+
+
+ ),
+ },
+ ].map((item, index) => (
+
+ {item.isDivider ?? false ? (
+
+ ) : item.label ? (
+
+ {item.label}
+ {item.content}
+
+ ) : (
+ item.content
+ )}
+
+ ))}
+
+ >
+ ) : undefined,
+ [getHref, hasWriteCapabilites, packageInfo, pkgkey, updateAvailable]
+ );
-type LayoutProps = PackageInfo & Pick & Pick;
-export function DetailLayout(props: LayoutProps) {
- const { name: packageName, version, icons, restrictWidth, title: packageTitle } = props;
- const iconType = usePackageIconType({ packageName, version, icons });
- useBreadcrumbs('integration_details', { pkgTitle: packageTitle });
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {packageInfo ? : null}
+ {packageInfoError ? (
+
+ }
+ error={packageInfoError}
+ />
+ ) : isLoading || !packageInfo ? (
+
+ ) : (
+
+ )}
+
);
}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/layout.tsx
index a802e35add7db..c329596384730 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/layout.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/layout.tsx
@@ -22,7 +22,7 @@ export const LeftColumn: FunctionComponent = ({ children, ...rest }
export const CenterColumn: FunctionComponent = ({ children, ...rest }) => {
return (
-
+
{children}
);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/screenshots.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/screenshots.tsx
index 696af14604c5b..d8388a71556d6 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/screenshots.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/screenshots.tsx
@@ -3,9 +3,10 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import React, { Fragment } from 'react';
import styled from 'styled-components';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { ScreenshotItem } from '../../../../types';
import { useLinks } from '../../hooks';
@@ -13,6 +14,29 @@ interface ScreenshotProps {
images: ScreenshotItem[];
}
+const getHorizontalPadding = (styledProps: any): number =>
+ parseInt(styledProps.theme.eui.paddingSizes.xl, 10) * 2;
+const getVerticalPadding = (styledProps: any): number =>
+ parseInt(styledProps.theme.eui.paddingSizes.xl, 10) * 1.75;
+const getPadding = (styledProps: any) =>
+ styledProps.hascaption
+ ? `${styledProps.theme.eui.paddingSizes.xl} ${getHorizontalPadding(
+ styledProps
+ )}px ${getVerticalPadding(styledProps)}px`
+ : `${getHorizontalPadding(styledProps)}px ${getVerticalPadding(styledProps)}px`;
+const ScreenshotsContainer = styled(EuiFlexGroup)`
+ background: linear-gradient(360deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0) 100%),
+ ${(styledProps) => styledProps.theme.eui.euiColorPrimary};
+ padding: ${(styledProps) => getPadding(styledProps)};
+ flex: 0 0 auto;
+ border-radius: ${(styledProps) => styledProps.theme.eui.euiBorderRadius};
+`;
+
+// fixes ie11 problems with nested flex items
+const NestedEuiFlexItem = styled(EuiFlexItem)`
+ flex: 0 0 auto !important;
+`;
+
export function Screenshots(props: ScreenshotProps) {
const { toImage } = useLinks();
const { images } = props;
@@ -21,36 +45,23 @@ export function Screenshots(props: ScreenshotProps) {
const image = images[0];
const hasCaption = image.title ? true : false;
- const getHorizontalPadding = (styledProps: any): number =>
- parseInt(styledProps.theme.eui.paddingSizes.xl, 10) * 2;
- const getVerticalPadding = (styledProps: any): number =>
- parseInt(styledProps.theme.eui.paddingSizes.xl, 10) * 1.75;
- const getPadding = (styledProps: any) =>
- hasCaption
- ? `${styledProps.theme.eui.paddingSizes.xl} ${getHorizontalPadding(
- styledProps
- )}px ${getVerticalPadding(styledProps)}px`
- : `${getHorizontalPadding(styledProps)}px ${getVerticalPadding(styledProps)}px`;
-
- const ScreenshotsContainer = styled(EuiFlexGroup)`
- background: linear-gradient(360deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0) 100%),
- ${(styledProps) => styledProps.theme.eui.euiColorPrimary};
- padding: ${(styledProps) => getPadding(styledProps)};
- flex: 0 0 auto;
- border-radius: ${(styledProps) => styledProps.theme.eui.euiBorderRadius};
- `;
-
- // fixes ie11 problems with nested flex items
- const NestedEuiFlexItem = styled(EuiFlexItem)`
- flex: 0 0 auto !important;
- `;
return (
- Screenshots
+
+
+
-
+
{hasCaption && (
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx
index 125289ce3ee8d..4832a89479026 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx
@@ -33,7 +33,7 @@ const NoteLabel = () => (
);
const UpdatesAvailableMsg = () => (
-
+
{entries(PanelDisplayNames).map(([panel, display]) => {
- const Link = styled(EuiButtonEmpty).attrs({
- href: getHref('integration_details', { pkgkey: `${name}-${version}`, panel }),
- })`
- font-weight: ${(p) =>
- active === panel
- ? p.theme.eui.euiFontWeightSemiBold
- : p.theme.eui.euiFontWeightRegular};
- `;
// Don't display usages tab as we haven't implemented this yet
// FIXME: Restore when we implement usages page
if (panel === 'usages' && (true || packageInstallStatus.status !== InstallStatus.installed))
@@ -50,7 +41,11 @@ export function SideNavLinks({ name, version, active }: NavLinkProps) {
return (
- {display}
+
+ {active === panel ? {display} : display}
+
);
})}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx
index c378e5a47a9b9..363b1ede89e9e 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx
@@ -39,22 +39,26 @@ export const HeroCopy = memo(() => {
);
});
+const Illustration = styled(EuiImage)`
+ margin-bottom: -68px;
+ width: 80%;
+`;
+
export const HeroImage = memo(() => {
const { toAssets } = useLinks();
const { uiSettings } = useCore();
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
- const Illustration = styled(EuiImage).attrs((props) => ({
- alt: i18n.translate('xpack.ingestManager.epm.illustrationAltText', {
- defaultMessage: 'Illustration of an integration',
- }),
- url: IS_DARK_THEME
- ? toAssets('illustration_integrations_darkmode.svg')
- : toAssets('illustration_integrations_lightmode.svg'),
- }))`
- margin-bottom: -68px;
- width: 80%;
- `;
-
- return ;
+ return (
+
+ );
});
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx
index c68833c1b2d95..a8e4d0105066b 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx
@@ -61,7 +61,9 @@ export function EPMHomePage() {
function InstalledPackages() {
useBreadcrumbs('integrations_installed');
- const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages();
+ const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages({
+ experimental: true,
+ });
const [selectedCategory, setSelectedCategory] = useState('');
const title = i18n.translate('xpack.ingestManager.epmList.installedTitle', {
@@ -118,7 +120,8 @@ function AvailablePackages() {
const queryParams = new URLSearchParams(useLocation().search);
const initialCategory = queryParams.get('category') || '';
const [selectedCategory, setSelectedCategory] = useState(initialCategory);
- const { data: categoryPackagesRes, isLoading: isLoadingPackages } = useGetPackages({
+ const { data: allPackagesRes, isLoading: isLoadingAllPackages } = useGetPackages();
+ const { data: categoryPackagesRes, isLoading: isLoadingCategoryPackages } = useGetPackages({
category: selectedCategory,
});
const { data: categoriesRes, isLoading: isLoadingCategories } = useGetCategories();
@@ -126,7 +129,7 @@ function AvailablePackages() {
categoryPackagesRes && categoryPackagesRes.response ? categoryPackagesRes.response : [];
const title = i18n.translate('xpack.ingestManager.epmList.allTitle', {
- defaultMessage: 'All integrations',
+ defaultMessage: 'Browse by category',
});
const categories = [
@@ -135,13 +138,13 @@ function AvailablePackages() {
title: i18n.translate('xpack.ingestManager.epmList.allPackagesFilterLinkText', {
defaultMessage: 'All',
}),
- count: packages.length,
+ count: allPackagesRes?.response?.length || 0,
},
...(categoriesRes ? categoriesRes.response : []),
];
const controls = categories ? (
{
@@ -156,7 +159,7 @@ function AvailablePackages() {
return (
;
- allPackages: PackageList;
-}
-
-export function SearchPackages({ searchTerm, localSearchRef, allPackages }: SearchPackagesProps) {
- // this means the search index hasn't been built yet.
- // i.e. the intial fetch of all packages hasn't finished
- if (!localSearchRef.current) return Still fetching matches. Try again in a moment.
;
-
- const matches = localSearchRef.current.search(searchTerm) as PackageList;
- const matchingIds = matches.map((match) => match[searchIdField]);
- const filtered = allPackages.filter((item) => matchingIds.includes(item[searchIdField]));
-
- return ;
-}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/search_results.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/search_results.tsx
deleted file mode 100644
index fbdcaac01931b..0000000000000
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/search_results.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { EuiText, EuiTitle } from '@elastic/eui';
-import React from 'react';
-import { PackageList } from '../../../../types';
-import { PackageListGrid } from '../../components/package_list_grid';
-
-interface SearchResultsProps {
- term: string;
- results: PackageList;
-}
-
-export function SearchResults({ term, results }: SearchResultsProps) {
- const title = 'Search results';
- return (
-
-
- {results.length} results for "{term}"
-
-
- }
- />
- );
-}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx
index ec58789becb72..30204603e764c 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useState } from 'react';
+import React, { useState, useMemo } from 'react';
import {
EuiBasicTable,
EuiButton,
@@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
import { CSSProperties } from 'styled-components';
import { AgentEnrollmentFlyout } from '../components';
-import { Agent } from '../../../types';
+import { Agent, AgentConfig } from '../../../types';
import {
usePagination,
useCapabilities,
@@ -220,6 +220,13 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
});
const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : [];
+ const agentConfigsIndexedById = useMemo(() => {
+ return agentConfigs.reduce((acc, config) => {
+ acc[config.id] = config;
+
+ return acc;
+ }, {} as { [k: string]: AgentConfig });
+ }, [agentConfigs]);
const { isLoading: isAgentConfigsLoading } = agentConfigsRequest;
const columns = [
@@ -271,9 +278,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
)}
- {agent.config_revision &&
- agent.config_newest_revision &&
- agent.config_newest_revision > agent.config_revision && (
+ {agent.config_id &&
+ agent.config_revision &&
+ agentConfigsIndexedById[agent.config_id] &&
+ agentConfigsIndexedById[agent.config_id].revision > agent.config_revision && (
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts
index 9cd8a75642296..170a9cedc08d9 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts
@@ -91,7 +91,9 @@ export {
RequirementVersion,
ScreenshotItem,
ServiceName,
+ GetCategoriesRequest,
GetCategoriesResponse,
+ GetPackagesRequest,
GetPackagesResponse,
GetLimitedPackagesResponse,
GetInfoResponse,
@@ -101,6 +103,7 @@ export {
InstallStatus,
InstallationStatus,
Installable,
+ RegistryRelease,
} from '../../../../common';
export * from './intra_app_route_state';
diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts
index a50b3b13faeab..fe813f29b72e6 100644
--- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts
+++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts
@@ -14,6 +14,7 @@ import {
GetLimitedPackagesResponse,
} from '../../../common';
import {
+ GetCategoriesRequestSchema,
GetPackagesRequestSchema,
GetFileRequestSchema,
GetInfoRequestSchema,
@@ -30,9 +31,12 @@ import {
getLimitedPackages,
} from '../../services/epm/packages';
-export const getCategoriesHandler: RequestHandler = async (context, request, response) => {
+export const getCategoriesHandler: RequestHandler<
+ undefined,
+ TypeOf
+> = async (context, request, response) => {
try {
- const res = await getCategories();
+ const res = await getCategories(request.query);
const body: GetCategoriesResponse = {
response: res,
success: true,
@@ -54,7 +58,7 @@ export const getListHandler: RequestHandler<
const savedObjectsClient = context.core.savedObjects.client;
const res = await getPackages({
savedObjectsClient,
- category: request.query.category,
+ ...request.query,
});
const body: GetPackagesResponse = {
response: res,
diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts
index ffaf0ce46c89a..b524a7b33923e 100644
--- a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts
+++ b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts
@@ -15,6 +15,7 @@ import {
deletePackageHandler,
} from './handlers';
import {
+ GetCategoriesRequestSchema,
GetPackagesRequestSchema,
GetFileRequestSchema,
GetInfoRequestSchema,
@@ -26,7 +27,7 @@ export const registerRoutes = (router: IRouter) => {
router.get(
{
path: EPM_API_ROUTES.CATEGORIES_PATTERN,
- validate: false,
+ validate: GetCategoriesRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-read`] },
},
getCategoriesHandler
diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts
index b47cf4f7e7c3b..a5b5cc4337908 100644
--- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts
+++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts
@@ -64,7 +64,6 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = {
last_updated: { type: 'date' },
last_checkin: { type: 'date' },
config_revision: { type: 'integer' },
- config_newest_revision: { type: 'integer' },
default_api_key_id: { type: 'keyword' },
default_api_key: { type: 'binary', index: false },
updated_at: { type: 'date' },
diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts
index 1cca165906732..3d40d128afda8 100644
--- a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts
+++ b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts
@@ -6,7 +6,7 @@
import { SavedObjectsClientContract } from 'src/core/server';
import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForConfigId } from './api_keys';
-import { updateAgentsForConfigId, unenrollForConfigId } from './agents';
+import { unenrollForConfigId } from './agents';
import { outputService } from './output';
export async function agentConfigUpdateEventHandler(
@@ -26,10 +26,6 @@ export async function agentConfigUpdateEventHandler(
});
}
- if (action === 'updated') {
- await updateAgentsForConfigId(soClient, configId);
- }
-
if (action === 'deleted') {
await unenrollForConfigId(soClient, configId);
await deleteEnrollmentApiKeyForConfigId(soClient, configId);
diff --git a/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts
index f8142af376eb3..ecc2c987d04b6 100644
--- a/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts
+++ b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts
@@ -23,6 +23,5 @@ export async function reassignAgent(
await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, {
config_id: newConfigId,
config_revision: null,
- config_newest_revision: config.revision,
});
}
diff --git a/x-pack/plugins/ingest_manager/server/services/agents/update.ts b/x-pack/plugins/ingest_manager/server/services/agents/update.ts
index ec7a42ff11b7a..11ad76fe81784 100644
--- a/x-pack/plugins/ingest_manager/server/services/agents/update.ts
+++ b/x-pack/plugins/ingest_manager/server/services/agents/update.ts
@@ -8,38 +8,6 @@ import { SavedObjectsClientContract } from 'src/core/server';
import { listAgents } from './crud';
import { AGENT_SAVED_OBJECT_TYPE } from '../../constants';
import { unenrollAgent } from './unenroll';
-import { agentConfigService } from '../agent_config';
-
-export async function updateAgentsForConfigId(
- soClient: SavedObjectsClientContract,
- configId: string
-) {
- const config = await agentConfigService.get(soClient, configId);
- if (!config) {
- throw new Error('Config not found');
- }
- let hasMore = true;
- let page = 1;
- while (hasMore) {
- const { agents } = await listAgents(soClient, {
- kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id:"${configId}"`,
- page: page++,
- perPage: 1000,
- showInactive: true,
- });
- if (agents.length === 0) {
- hasMore = false;
- break;
- }
- const agentUpdate = agents.map((agent) => ({
- id: agent.id,
- type: AGENT_SAVED_OBJECT_TYPE,
- attributes: { config_newest_revision: config.revision },
- }));
-
- await soClient.bulkUpdate(agentUpdate);
- }
-}
export async function unenrollForConfigId(soClient: SavedObjectsClientContract, configId: string) {
let hasMore = true;
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts
index ad9635cc02e06..78aa513d1a1dc 100644
--- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts
+++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts
@@ -17,8 +17,8 @@ function nameAsTitle(name: string) {
return name.charAt(0).toUpperCase() + name.substr(1).toLowerCase();
}
-export async function getCategories() {
- return Registry.fetchCategories();
+export async function getCategories(options: Registry.CategoriesParams) {
+ return Registry.fetchCategories(options);
}
export async function getPackages(
@@ -26,8 +26,8 @@ export async function getPackages(
savedObjectsClient: SavedObjectsClientContract;
} & Registry.SearchParams
) {
- const { savedObjectsClient } = options;
- const registryItems = await Registry.fetchList({ category: options.category }).then((items) => {
+ const { savedObjectsClient, experimental, category } = options;
+ const registryItems = await Registry.fetchList({ category, experimental }).then((items) => {
return items.map((item) =>
Object.assign({}, item, { title: item.title || nameAsTitle(item.name) })
);
@@ -56,7 +56,7 @@ export async function getLimitedPackages(options: {
savedObjectsClient: SavedObjectsClientContract;
}): Promise {
const { savedObjectsClient } = options;
- const allPackages = await getPackages({ savedObjectsClient });
+ const allPackages = await getPackages({ savedObjectsClient, experimental: true });
const installedPackages = allPackages.filter(
(pkg) => (pkg.status = InstallationStatus.installed)
);
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
index 0393cabca8ba2..ea906517f6dec 100644
--- a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
+++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts
@@ -26,6 +26,11 @@ export { ArchiveEntry } from './extract';
export interface SearchParams {
category?: CategoryId;
+ experimental?: boolean;
+}
+
+export interface CategoriesParams {
+ experimental?: boolean;
}
export const pkgToPkgKey = ({ name, version }: { name: string; version: string }) =>
@@ -34,19 +39,23 @@ export const pkgToPkgKey = ({ name, version }: { name: string; version: string }
export async function fetchList(params?: SearchParams): Promise {
const registryUrl = getRegistryUrl();
const url = new URL(`${registryUrl}/search`);
- if (params && params.category) {
- url.searchParams.set('category', params.category);
+ if (params) {
+ if (params.category) {
+ url.searchParams.set('category', params.category);
+ }
+ if (params.experimental) {
+ url.searchParams.set('experimental', params.experimental.toString());
+ }
}
return fetchUrl(url.toString()).then(JSON.parse);
}
-export async function fetchFindLatestPackage(
- packageName: string,
- internal: boolean = true
-): Promise {
+export async function fetchFindLatestPackage(packageName: string): Promise {
const registryUrl = getRegistryUrl();
- const url = new URL(`${registryUrl}/search?package=${packageName}&internal=${internal}`);
+ const url = new URL(
+ `${registryUrl}/search?package=${packageName}&internal=true&experimental=true`
+ );
const res = await fetchUrl(url.toString());
const searchResults = JSON.parse(res);
if (searchResults.length) {
@@ -66,9 +75,16 @@ export async function fetchFile(filePath: string): Promise {
return getResponse(`${registryUrl}${filePath}`);
}
-export async function fetchCategories(): Promise {
+export async function fetchCategories(params?: CategoriesParams): Promise {
const registryUrl = getRegistryUrl();
- return fetchUrl(`${registryUrl}/categories`).then(JSON.parse);
+ const url = new URL(`${registryUrl}/categories`);
+ if (params) {
+ if (params.experimental) {
+ url.searchParams.set('experimental', params.experimental.toString());
+ }
+ }
+
+ return fetchUrl(url.toString()).then(JSON.parse);
}
export async function getArchiveInfo(
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts
index 3ed6ee553a507..08f47a8f1caaa 100644
--- a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts
@@ -5,9 +5,16 @@
*/
import { schema } from '@kbn/config-schema';
+export const GetCategoriesRequestSchema = {
+ query: schema.object({
+ experimental: schema.maybe(schema.boolean()),
+ }),
+};
+
export const GetPackagesRequestSchema = {
query: schema.object({
category: schema.maybe(schema.string()),
+ experimental: schema.maybe(schema.boolean()),
}),
};
diff --git a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts
index 16eaab20fe8cb..196e17d0984f9 100644
--- a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts
+++ b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts
@@ -70,6 +70,7 @@ export const anomalyDetectionUpdateJobSchema = schema.object({
),
groups: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))),
model_snapshot_retention_days: schema.maybe(schema.number()),
+ daily_model_snapshot_retention_after_days: schema.maybe(schema.number()),
});
export const analysisConfigSchema = schema.object({
diff --git a/x-pack/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js b/x-pack/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js
index 4ff189d8f1be0..643cc3efb0136 100644
--- a/x-pack/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js
+++ b/x-pack/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js
@@ -100,6 +100,7 @@ export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig
{
key: this.type,
name: rollupIndexPatternIndexLabel,
+ color: 'primary',
},
]
: [];
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
index efd9ece8aec56..9438c28f05fef 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
@@ -99,6 +99,6 @@ describe('Cases', () => {
cy.get(TIMELINE_TITLE).should('have.attr', 'value', case1.timeline.title);
cy.get(TIMELINE_DESCRIPTION).should('have.attr', 'value', case1.timeline.description);
- cy.get(TIMELINE_QUERY).should('have.attr', 'value', case1.timeline.query);
+ cy.get(TIMELINE_QUERY).invoke('text').should('eq', case1.timeline.query);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts
index 0c3424576e4cf..6b3fc9e751ea4 100644
--- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts
@@ -27,74 +27,67 @@ import {
describe('ml conditional links', () => {
it('sets the KQL from a single IP with a value for the query', () => {
loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(process.name: "conhost.exe" or process.name: "sc.exe")'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should('eq', '(process.name: "conhost.exe" or process.name: "sc.exe")');
});
it('sets the KQL from a multiple IPs with a null for the query', () => {
loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2"))'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should(
+ 'eq',
+ '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2"))'
+ );
});
it('sets the KQL from a multiple IPs with a value for the query', () => {
loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2")) and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should(
+ 'eq',
+ '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2")) and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
+ );
});
it('sets the KQL from a $ip$ with a value for the query', () => {
loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(process.name: "conhost.exe" or process.name: "sc.exe")'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should('eq', '(process.name: "conhost.exe" or process.name: "sc.exe")');
});
it('sets the KQL from a single host name with a value for query', () => {
loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(process.name: "conhost.exe" or process.name: "sc.exe")'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should('eq', '(process.name: "conhost.exe" or process.name: "sc.exe")');
});
it('sets the KQL from a multiple host names with null for query', () => {
loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(host.name: "siem-windows" or host.name: "siem-suricata")'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should('eq', '(host.name: "siem-windows" or host.name: "siem-suricata")');
});
it('sets the KQL from a multiple host names with a value for query', () => {
loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(host.name: "siem-windows" or host.name: "siem-suricata") and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should(
+ 'eq',
+ '(host.name: "siem-windows" or host.name: "siem-suricata") and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
+ );
});
it('sets the KQL from a undefined/null host name but with a value for query', () => {
loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery);
- cy.get(KQL_INPUT).should(
- 'have.attr',
- 'value',
- '(process.name: "conhost.exe" or process.name: "sc.exe")'
- );
+ cy.get(KQL_INPUT)
+ .invoke('text')
+ .should('eq', '(process.name: "conhost.exe" or process.name: "sc.exe")');
});
it('redirects from a single IP with a null for the query', () => {
diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts
index a3a927cbea7d4..81af9ece9ed45 100644
--- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts
@@ -154,12 +154,12 @@ describe('url state', () => {
it('sets kql on network page', () => {
loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.urlKqlNetworkNetwork);
- cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+ cy.get(KQL_INPUT).invoke('text').should('eq', 'source.ip: "10.142.0.9"');
});
it('sets kql on hosts page', () => {
loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
- cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+ cy.get(KQL_INPUT).invoke('text').should('eq', 'source.ip: "10.142.0.9"');
});
it('sets the url state when kql is set', () => {
@@ -230,8 +230,7 @@ describe('url state', () => {
it('Do not clears kql when navigating to a new page', () => {
loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
navigateFromHeaderTo(NETWORK);
-
- cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+ cy.get(KQL_INPUT).invoke('text').should('eq', 'source.ip: "10.142.0.9"');
});
it.skip('sets and reads the url state for timeline by id', () => {
diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
index eca5885e7b3d9..88ae582b58891 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
@@ -82,7 +82,7 @@ export const fillAboutRuleAndContinue = (rule: CustomRule | MachineLearningRule)
export const fillDefineCustomRuleAndContinue = (rule: CustomRule) => {
cy.get(CUSTOM_QUERY_INPUT).type(rule.customQuery);
- cy.get(CUSTOM_QUERY_INPUT).should('have.attr', 'value', rule.customQuery);
+ cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery);
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
@@ -91,7 +91,7 @@ export const fillDefineCustomRuleAndContinue = (rule: CustomRule) => {
export const fillDefineCustomRuleWithImportedQueryAndContinue = (rule: CustomRule) => {
cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click();
cy.get(TIMELINE(rule.timelineId)).click();
- cy.get(CUSTOM_QUERY_INPUT).should('have.attr', 'value', rule.customQuery);
+ cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery);
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
index 9e17433090c2b..761fd2c1e6a0b 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
@@ -58,7 +58,7 @@ export const createNewTimeline = () => {
};
export const executeTimelineKQL = (query: string) => {
- cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${query} {enter}`);
+ cy.get(`${SEARCH_OR_FILTER_CONTAINER} textarea`).type(`${query} {enter}`);
};
export const expandFirstTimelineEventDetails = () => {
diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
index a3cab1cfabd71..aac83ce650d86 100644
--- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
@@ -214,15 +214,18 @@ describe('QueryBar ', () => {
/>
);
- const queryInput = wrapper.find(QueryBar).find('input[data-test-subj="queryInput"]');
+ let queryInput = wrapper.find(QueryBar).find('textarea[data-test-subj="queryInput"]');
queryInput.simulate('change', { target: { value: 'host.name:*' } });
- expect(queryInput.html()).toContain('value="host.name:*"');
+ wrapper.update();
+ queryInput = wrapper.find(QueryBar).find('textarea[data-test-subj="queryInput"]');
+ expect(queryInput.props().children).toBe('host.name:*');
wrapper.setProps({ filterQueryDraft: null });
wrapper.update();
+ queryInput = wrapper.find(QueryBar).find('textarea[data-test-subj="queryInput"]');
- expect(queryInput.html()).toContain('value=""');
+ expect(queryInput.props().children).toBe('');
});
});
@@ -258,7 +261,7 @@ describe('QueryBar ', () => {
const onSubmitQueryRef = searchBarProps.onQuerySubmit;
const onSavedQueryRef = searchBarProps.onSavedQueryUpdated;
- const queryInput = wrapper.find(QueryBar).find('input[data-test-subj="queryInput"]');
+ const queryInput = wrapper.find(QueryBar).find('textarea[data-test-subj="queryInput"]');
queryInput.simulate('change', { target: { value: 'hello: world' } });
wrapper.update();
diff --git a/x-pack/test/api_integration/apis/fleet/agents/acks.ts b/x-pack/test/api_integration/apis/fleet/agents/acks.ts
index 45833012cb475..e8381aa9d59ea 100644
--- a/x-pack/test/api_integration/apis/fleet/agents/acks.ts
+++ b/x-pack/test/api_integration/apis/fleet/agents/acks.ts
@@ -22,7 +22,7 @@ export default function (providerContext: FtrProviderContext) {
before(async () => {
await esArchiver.loadIfNeeded('fleet/agents');
- const { body: apiKeyBody } = await esClient.security.createApiKey({
+ const { body: apiKeyBody } = await esClient.security.createApiKey({
body: {
name: `test access api key: ${uuid.v4()}`,
},
diff --git a/x-pack/test/api_integration/apis/fleet/agents/checkin.ts b/x-pack/test/api_integration/apis/fleet/agents/checkin.ts
index d24f7f495a06c..8942deafdd83c 100644
--- a/x-pack/test/api_integration/apis/fleet/agents/checkin.ts
+++ b/x-pack/test/api_integration/apis/fleet/agents/checkin.ts
@@ -22,7 +22,7 @@ export default function (providerContext: FtrProviderContext) {
before(async () => {
await esArchiver.loadIfNeeded('fleet/agents');
- const { body: apiKeyBody } = await esClient.security.createApiKey({
+ const { body: apiKeyBody } = await esClient.security.createApiKey({
body: {
name: `test access api key: ${uuid.v4()}`,
},
diff --git a/x-pack/test/api_integration/apis/fleet/agents/enroll.ts b/x-pack/test/api_integration/apis/fleet/agents/enroll.ts
index b4d23a2392320..e9f7471f6437e 100644
--- a/x-pack/test/api_integration/apis/fleet/agents/enroll.ts
+++ b/x-pack/test/api_integration/apis/fleet/agents/enroll.ts
@@ -25,7 +25,7 @@ export default function (providerContext: FtrProviderContext) {
before(async () => {
await esArchiver.loadIfNeeded('fleet/agents');
- const { body: apiKeyBody } = await esClient.security.createApiKey({
+ const { body: apiKeyBody } = await esClient.security.createApiKey({
body: {
name: `test access api key: ${uuid.v4()}`,
},
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
index 4a79610cadbde..2c6edeba2129f 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
@@ -10,8 +10,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
- // flaky test https://github.com/elastic/kibana/issues/70455
- describe.skip('classification creation', function () {
+
+ describe('classification creation', function () {
before(async () => {
await esArchiver.loadIfNeeded('ml/bm_classification');
await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp');
@@ -96,9 +96,9 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
});
- it('inputs the model memory limit', async () => {
+ it('accepts the suggested model memory limit', async () => {
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
- await ml.dataFrameAnalyticsCreation.setModelMemory(testData.modelMemory);
+ await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
});
it('continues to the details step', async () => {
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
index 65e6dc9b4ea74..6cdb9caa1e2db 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
@@ -11,8 +11,7 @@ export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
- // Flaky: https://github.com/elastic/kibana/issues/70906
- describe.skip('outlier detection creation', function () {
+ describe('outlier detection creation', function () {
before(async () => {
await esArchiver.loadIfNeeded('ml/ihp_outlier');
await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp');
@@ -93,9 +92,9 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
});
- it('inputs the model memory limit', async () => {
+ it('accepts the suggested model memory limit', async () => {
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
- await ml.dataFrameAnalyticsCreation.setModelMemory(testData.modelMemory);
+ await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
});
it('continues to the details step', async () => {
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
index 33f0ee9cd99ac..03117d4cc419d 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
@@ -10,8 +10,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
- // flaky test https://github.com/elastic/kibana/issues/70455
- describe.skip('regression creation', function () {
+
+ describe('regression creation', function () {
before(async () => {
await esArchiver.loadIfNeeded('ml/egs_regression');
await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression', '@timestamp');
@@ -96,9 +96,9 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
});
- it('inputs the model memory limit', async () => {
+ it('accepts the suggested model memory limit', async () => {
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
- await ml.dataFrameAnalyticsCreation.setModelMemory(testData.modelMemory);
+ await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
});
it('continues to the details step', async () => {
diff --git a/x-pack/test/functional/page_objects/monitoring_page.js b/x-pack/test/functional/page_objects/monitoring_page.js
index ece0c0a6c7854..c3b9d20b3ac4a 100644
--- a/x-pack/test/functional/page_objects/monitoring_page.js
+++ b/x-pack/test/functional/page_objects/monitoring_page.js
@@ -8,6 +8,7 @@ export function MonitoringPageProvider({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['common', 'header', 'security', 'login', 'spaceSelector']);
const testSubjects = getService('testSubjects');
const security = getService('security');
+ const find = getService('find');
return new (class MonitoringPage {
async navigateTo(useSuperUser = false) {
@@ -25,6 +26,11 @@ export function MonitoringPageProvider({ getPageObjects, getService }) {
await PageObjects.common.navigateToApp('monitoring');
}
+ async getWelcome() {
+ const el = await find.byCssSelector('.euiCallOut--primary', 10000 * 10);
+ return await el.getVisibleText();
+ }
+
async getAccessDeniedMessage() {
return testSubjects.getVisibleText('accessDeniedTitle');
}
diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
index 918c982de02ed..1b756bbaca5d8 100644
--- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
+++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
@@ -306,6 +306,15 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
);
},
+ async assertModelMemoryInputPopulated() {
+ const actualModelMemory = await testSubjects.getAttribute(
+ 'mlAnalyticsCreateJobWizardModelMemoryInput',
+ 'value'
+ );
+
+ expect(actualModelMemory).not.to.be('');
+ },
+
async assertPredictionFieldNameValue(expectedValue: string) {
const actualPredictedFieldName = await testSubjects.getAttribute(
'mlAnalyticsCreateJobWizardPredictionFieldNameInput',
diff --git a/x-pack/test/stack_functional_integration/configs/build_state.js b/x-pack/test/stack_functional_integration/configs/build_state.js
new file mode 100644
index 0000000000000..abf1bff56331a
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/configs/build_state.js
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import dotEnv from 'dotenv';
+import testsList from './tests_list';
+
+// envObj :: path -> {}
+const envObj = (path) => dotEnv.config({ path });
+
+// default fn :: path -> {}
+export default (path) => {
+ const obj = envObj(path).parsed;
+ return { tests: testsList(obj), ...obj };
+};
diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js
new file mode 100644
index 0000000000000..a34d158496ba0
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { resolve } from 'path';
+import buildState from './build_state';
+import { ToolingLog } from '@kbn/dev-utils';
+import chalk from 'chalk';
+import { esTestConfig, kbnTestConfig } from '@kbn/test';
+
+const reportName = 'Stack Functional Integration Tests';
+const testsFolder = '../test/functional/apps';
+const stateFilePath = '../../../../../integration-test/qa/envvars.sh';
+const prepend = (testFile) => require.resolve(`${testsFolder}/${testFile}`);
+const log = new ToolingLog({
+ level: 'info',
+ writeTo: process.stdout,
+});
+
+export default async ({ readConfigFile }) => {
+ const defaultConfigs = await readConfigFile(require.resolve('../../functional/config'));
+ const { tests, ...provisionedConfigs } = buildState(resolve(__dirname, stateFilePath));
+
+ const servers = {
+ kibana: kbnTestConfig.getUrlParts(),
+ elasticsearch: esTestConfig.getUrlParts(),
+ };
+ log.info(`servers data: ${JSON.stringify(servers)}`);
+ const settings = {
+ ...defaultConfigs.getAll(),
+ junit: {
+ reportName: `${reportName} - ${provisionedConfigs.VM}`,
+ },
+ servers,
+ testFiles: tests.map(prepend).map(logTest),
+ // testFiles: ['monitoring'].map(prepend).map(logTest),
+ // If we need to do things like disable animations, we can do it in configure_start_kibana.sh, in the provisioner...which lives in the integration-test private repo
+ uiSettings: {},
+ security: { disableTestUser: true },
+ };
+ return settings;
+};
+
+// Returns index 1 from the resulting array-like.
+const splitRight = (re) => (testPath) => re.exec(testPath)[1];
+
+function truncate(testPath) {
+ const dropKibanaPath = splitRight(/^.+kibana[\\/](.*$)/gm);
+ return dropKibanaPath(testPath);
+}
+function highLight(testPath) {
+ const dropTestsPath = splitRight(/^.+test[\\/]functional[\\/]apps[\\/](.*)[\\/]/gm);
+ const cleaned = dropTestsPath(testPath);
+ const colored = chalk.greenBright.bold(cleaned);
+ return testPath.replace(cleaned, colored);
+}
+function logTest(testPath) {
+ log.info(`Testing: '${highLight(truncate(testPath))}'`);
+ return testPath;
+}
diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base_ie.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base_ie.js
new file mode 100644
index 0000000000000..933a59e4e25b9
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base_ie.js
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default async ({ readConfigFile }) => {
+ const baseConfigs = await readConfigFile(
+ require.resolve('./config.stack_functional_integration_base.js')
+ );
+ return {
+ ...baseConfigs.getAll(),
+ browser: {
+ type: 'ie',
+ },
+ security: { disableTestUser: true },
+ };
+};
diff --git a/x-pack/test/stack_functional_integration/configs/tests_list.js b/x-pack/test/stack_functional_integration/configs/tests_list.js
new file mode 100644
index 0000000000000..ff68cb6285965
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/configs/tests_list.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+// testsList :: {} -> list
+export default (envObj) => {
+ const xs = [];
+ // one of these 2 needs to create the default index pattern
+ if (envObj.PRODUCTS.includes('logstash')) {
+ xs.push('management');
+ } else {
+ xs.push('sample_data');
+ }
+
+ // get the opt in/out banner out of the way early
+ if (envObj.XPACK === 'YES') {
+ xs.push('telemetry');
+ }
+
+ if (envObj.BEATS.includes('metricbeat')) {
+ xs.push('metricbeat');
+ }
+ if (envObj.BEATS.includes('filebeat')) {
+ xs.push('filebeat');
+ }
+ if (envObj.BEATS.includes('packetbeat')) {
+ xs.push('packetbeat');
+ }
+ if (envObj.BEATS.includes('winlogbeat')) {
+ xs.push('winlogbeat');
+ }
+ if (envObj.BEATS.includes('heartbeat')) {
+ xs.push('heartbeat');
+ }
+ if (envObj.VM === 'ubuntu16_tar_ccs') {
+ xs.push('ccs');
+ }
+
+ // with latest elasticsearch Js client, we can only run these watcher tests
+ // which use the watcher API on a config with x-pack but without TLS (no security)
+ if (envObj.VM === 'ubuntu16_tar') {
+ xs.push('reporting');
+ }
+
+ if (envObj.XPACK === 'YES' && ['TRIAL', 'GOLD', 'PLATINUM'].includes(envObj.LICENSE)) {
+ // we can't test enabling monitoring on this config because we already enable it through cluster settings for both clusters.
+ if (envObj.VM !== 'ubuntu16_tar_ccs') {
+ // monitoring is last because we switch to the elastic superuser here
+ xs.push('monitoring');
+ }
+ }
+
+ return xs;
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/ccs/ccs.js b/x-pack/test/stack_functional_integration/test/functional/apps/ccs/ccs.js
new file mode 100644
index 0000000000000..a952824d8db61
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/ccs/ccs.js
@@ -0,0 +1,175 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default ({ getService, getPageObjects }) => {
+ describe('Cross cluster search test', async () => {
+ const PageObjects = getPageObjects([
+ 'common',
+ 'settings',
+ 'discover',
+ 'security',
+ 'header',
+ 'timePicker',
+ ]);
+ const retry = getService('retry');
+ const log = getService('log');
+ const browser = getService('browser');
+ const appsMenu = getService('appsMenu');
+ const kibanaServer = getService('kibanaServer');
+
+ before(async () => {
+ await browser.setWindowSize(1200, 800);
+ // pincking relative time in timepicker isn't working. This is also faster.
+ // It's the default set, plus new "makelogs" +/- 3 days from now
+ await kibanaServer.uiSettings.replace({
+ 'timepicker:quickRanges': `[
+ {
+ "from": "now-3d",
+ "to": "now+3d",
+ "display": "makelogs"
+ },
+ {
+ "from": "now/d",
+ "to": "now/d",
+ "display": "Today"
+ },
+ {
+ "from": "now/w",
+ "to": "now/w",
+ "display": "This week"
+ },
+ {
+ "from": "now-15m",
+ "to": "now",
+ "display": "Last 15 minutes"
+ },
+ {
+ "from": "now-30m",
+ "to": "now",
+ "display": "Last 30 minutes"
+ },
+ {
+ "from": "now-1h",
+ "to": "now",
+ "display": "Last 1 hour"
+ },
+ {
+ "from": "now-24h",
+ "to": "now",
+ "display": "Last 24 hours"
+ },
+ {
+ "from": "now-7d",
+ "to": "now",
+ "display": "Last 7 days"
+ },
+ {
+ "from": "now-30d",
+ "to": "now",
+ "display": "Last 30 days"
+ },
+ {
+ "from": "now-90d",
+ "to": "now",
+ "display": "Last 90 days"
+ },
+ {
+ "from": "now-1y",
+ "to": "now",
+ "display": "Last 1 year"
+ }
+ ]`,
+ });
+ });
+
+ before(async () => {
+ if (process.env.SECURITY === 'YES') {
+ log.debug(
+ '### provisionedEnv.SECURITY === YES so log in as elastic superuser to create cross cluster indices'
+ );
+ await PageObjects.security.logout();
+ }
+ const url = await browser.getCurrentUrl();
+ log.debug(url);
+ if (!url.includes('kibana')) {
+ await PageObjects.common.navigateToApp('management', { insertTimestamp: false });
+ } else if (!url.includes('management')) {
+ await appsMenu.clickLink('Management');
+ }
+ });
+
+ it('create local admin makelogs index pattern', async () => {
+ log.debug('create local admin makelogs工程 index pattern');
+ await PageObjects.settings.createIndexPattern('local:makelogs工程*');
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('local:makelogs工程*');
+ });
+
+ it('create remote data makelogs index pattern', async () => {
+ log.debug('create remote data makelogs工程 index pattern');
+ await PageObjects.settings.createIndexPattern('data:makelogs工程*');
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('data:makelogs工程*');
+ });
+
+ it('create comma separated index patterns for data and local makelogs index pattern', async () => {
+ log.debug(
+ 'create comma separated index patterns for data and local makelogs工程 index pattern'
+ );
+ await PageObjects.settings.createIndexPattern('data:makelogs工程-*,local:makelogs工程-*');
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('data:makelogs工程-*,local:makelogs工程-*');
+ });
+
+ it('create index pattern for data from both clusters', async () => {
+ await PageObjects.settings.createIndexPattern('*:makelogs工程-*', '@timestamp', true, false);
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('*:makelogs工程-*');
+ });
+
+ it('local:makelogs(star) should discover data from the local cluster', async () => {
+ await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
+
+ await PageObjects.discover.selectIndexPattern('local:makelogs工程*');
+ await PageObjects.timePicker.setCommonlyUsedTime('makelogs');
+ await retry.tryForTime(40000, async () => {
+ const hitCount = await PageObjects.discover.getHitCount();
+ log.debug('### hit count = ' + hitCount);
+ expect(hitCount).to.be('14,005');
+ });
+ });
+
+ it('data:makelogs(star) should discover data from remote', async function () {
+ await PageObjects.discover.selectIndexPattern('data:makelogs工程*');
+ await retry.tryForTime(40000, async () => {
+ const hitCount = await PageObjects.discover.getHitCount();
+ log.debug('### hit count = ' + hitCount);
+ expect(hitCount).to.be('14,005');
+ });
+ });
+
+ it('star:makelogs-star should discover data from both clusters', async function () {
+ await PageObjects.discover.selectIndexPattern('*:makelogs工程-*');
+ await PageObjects.timePicker.setCommonlyUsedTime('makelogs');
+ await retry.tryForTime(40000, async () => {
+ const hitCount = await PageObjects.discover.getHitCount();
+ log.debug('### hit count = ' + hitCount);
+ expect(hitCount).to.be('28,010');
+ });
+ });
+
+ it('data:makelogs-star,local:makelogs-star should discover data from both clusters', async function () {
+ await PageObjects.discover.selectIndexPattern('data:makelogs工程-*,local:makelogs工程-*');
+ await retry.tryForTime(40000, async () => {
+ const hitCount = await PageObjects.discover.getHitCount();
+ log.debug('### hit count = ' + hitCount);
+ expect(hitCount).to.be('28,010');
+ });
+ });
+ });
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/index.ts b/x-pack/test/stack_functional_integration/test/functional/apps/ccs/index.js
similarity index 64%
rename from x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/index.ts
rename to x-pack/test/stack_functional_integration/test/functional/apps/ccs/index.js
index 41bc2aa258807..e31a903cf0be2 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/index.ts
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/ccs/index.js
@@ -3,3 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+
+export default function ({ loadTestFile }) {
+ describe('ccs test', function () {
+ loadTestFile(require.resolve('./ccs'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/filebeat.js b/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/filebeat.js
new file mode 100644
index 0000000000000..14d06ac296ba3
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/filebeat.js
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default function ({ getService, getPageObjects }) {
+ describe('check filebeat', function () {
+ const retry = getService('retry');
+ const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
+
+ it('filebeat- should have hit count GT 0', async function () {
+ await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
+ await PageObjects.discover.selectIndexPattern('filebeat-*');
+ await PageObjects.timePicker.setCommonlyUsedTime('Last_30 days');
+ await retry.try(async () => {
+ const hitCount = parseInt(await PageObjects.discover.getHitCount());
+ expect(hitCount).to.be.greaterThan(0);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/index.js
new file mode 100644
index 0000000000000..c3a81ca43a68f
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('filebeat app', function () {
+ loadTestFile(require.resolve('./filebeat'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/_heartbeat.js b/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/_heartbeat.js
new file mode 100644
index 0000000000000..4e1c02b627de0
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/_heartbeat.js
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default function ({ getService, getPageObjects }) {
+ const retry = getService('retry');
+ const PageObjects = getPageObjects(['common', 'uptime']);
+
+ describe('check heartbeat', function () {
+ it('Uptime app should show snapshot count greater than zero', async function () {
+ await PageObjects.common.navigateToApp('uptime', { insertTimestamp: false });
+
+ await retry.try(async function () {
+ const upCount = parseInt((await PageObjects.uptime.getSnapshotCount()).up);
+ expect(upCount).to.be.greaterThan(0);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/index.js
new file mode 100644
index 0000000000000..28ae1bbaa488d
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/index.js
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('heartbeat app', function () {
+ require('./_heartbeat');
+ loadTestFile(require.resolve('./_heartbeat'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/management/_index_pattern_create.js b/x-pack/test/stack_functional_integration/test/functional/apps/management/_index_pattern_create.js
new file mode 100644
index 0000000000000..a43a2fce61ea1
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/management/_index_pattern_create.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default ({ getService, getPageObjects }) => {
+ describe('creating default index', function describeIndexTests() {
+ const PageObjects = getPageObjects(['common', 'settings']);
+ const retry = getService('retry');
+ const log = getService('log');
+ const browser = getService('browser');
+
+ before(async () => {
+ await PageObjects.common.navigateToApp('management', { insertTimestamp: false });
+ await browser.setWindowSize(1200, 800);
+ });
+
+ it('create makelogs工程 index pattern', async function pageHeader() {
+ log.debug('create makelogs工程 index pattern');
+ await PageObjects.settings.createIndexPattern('makelogs工程-*');
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('makelogs工程-*');
+ });
+
+ describe('create logstash index pattern', function indexPatternCreation() {
+ before(async () => {
+ await retry.tryForTime(120000, async () => {
+ log.debug('create Index Pattern');
+ await PageObjects.settings.createIndexPattern();
+ });
+ });
+
+ it('should have index pattern in page header', async function pageHeader() {
+ const patternName = await PageObjects.settings.getIndexPageHeading();
+ expect(patternName).to.be('logstash-*');
+ });
+
+ it('should have expected table headers', async function checkingHeader() {
+ const headers = await PageObjects.settings.getTableHeader();
+ log.debug('header.length = ' + headers.length);
+ const expectedHeaders = [
+ 'Name',
+ 'Type',
+ 'Format',
+ 'Searchable',
+ 'Aggregatable',
+ 'Excluded',
+ ];
+
+ expect(headers.length).to.be(expectedHeaders.length);
+
+ await Promise.all(
+ headers.map(async function compareHead(header, i) {
+ const text = await header.getVisibleText();
+ expect(text).to.be(expectedHeaders[i]);
+ })
+ );
+ });
+ });
+ });
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/management/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/management/index.js
new file mode 100644
index 0000000000000..6e032c198bc6a
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/management/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('settings / management app', function () {
+ loadTestFile(require.resolve('./_index_pattern_create'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/_metricbeat.js b/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/_metricbeat.js
new file mode 100644
index 0000000000000..8f6ddff180695
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/_metricbeat.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default function ({ getService, getPageObjects }) {
+ const log = getService('log');
+ const retry = getService('retry');
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
+ const appsMenu = getService('appsMenu');
+
+ describe('check metricbeat', function () {
+ it('metricbeat- should have hit count GT 0', async function () {
+ const url = await browser.getCurrentUrl();
+ log.debug(url);
+ if (!url.includes('kibana')) {
+ await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
+ } else if (!url.includes('discover')) {
+ await appsMenu.clickLink('Discover');
+ }
+
+ await PageObjects.discover.selectIndexPattern('metricbeat-*');
+ await PageObjects.timePicker.setCommonlyUsedTime('Today');
+ await retry.try(async function () {
+ const hitCount = parseInt(await PageObjects.discover.getHitCount());
+ expect(hitCount).to.be.greaterThan(0);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js
new file mode 100644
index 0000000000000..d45d6c835a315
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('metricbeat app', function () {
+ loadTestFile(require.resolve('./_metricbeat'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring.js b/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring.js
new file mode 100644
index 0000000000000..623937b178833
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default ({ getService, getPageObjects }) => {
+ describe('monitoring app - stack functional integration - suite', () => {
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['security', 'monitoring', 'common']);
+ const log = getService('log');
+ const testSubjects = getService('testSubjects');
+ const isSaml = !!process.env.VM.includes('saml') || !!process.env.VM.includes('oidc');
+
+ before(async () => {
+ await browser.setWindowSize(1200, 800);
+ if (process.env.SECURITY === 'YES' && !isSaml) {
+ await PageObjects.security.logout();
+ log.debug('### log in as elastic superuser to enable monitoring');
+ // Tests may be running as a non-superuser like `power` but that user
+ // doesn't have the cluster privs to enable monitoring.
+ // On the SAML config, this will fail, but the test recovers on the next
+ // navigate and logs in as the saml user.
+ }
+ // navigateToApp without a username and password will default to the superuser
+ await PageObjects.common.navigateToApp('monitoring', { insertTimestamp: false });
+ });
+
+ it('should enable Monitoring', async () => {
+ await testSubjects.click('useInternalCollection');
+ await testSubjects.click('enableCollectionEnabled');
+ });
+
+ after(async () => {
+ if (process.env.SECURITY === 'YES' && !isSaml) {
+ await PageObjects.security.forceLogout(isSaml);
+ }
+ });
+ });
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/index.js
new file mode 100644
index 0000000000000..f6ea0ae4aa2b5
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('monitoring app - stack functional integration - index', function () {
+ loadTestFile(require.resolve('./_monitoring'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/_packetbeat.js b/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/_packetbeat.js
new file mode 100644
index 0000000000000..e09ac478fccbd
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/_packetbeat.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default function ({ getService, getPageObjects }) {
+ const log = getService('log');
+ const retry = getService('retry');
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
+ const appsMenu = getService('appsMenu');
+
+ describe('check packetbeat', function () {
+ before(function () {
+ log.debug('navigateToApp Discover');
+ });
+
+ it('packetbeat- should have hit count GT 0', async function () {
+ const url = await browser.getCurrentUrl();
+ log.debug(url);
+ if (!url.includes('kibana')) {
+ await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
+ }
+ if (!url.includes('discover')) {
+ await appsMenu.clickLink('Discover');
+ }
+ await PageObjects.discover.selectIndexPattern('packetbeat-*');
+ await PageObjects.timePicker.setCommonlyUsedTime('Today');
+ await retry.try(async function () {
+ const hitCount = parseInt(await PageObjects.discover.getHitCount());
+ expect(hitCount).to.be.greaterThan(0);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/index.js
new file mode 100644
index 0000000000000..5bb4582eb16de
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('packetbeat app', function () {
+ loadTestFile(require.resolve('./_packetbeat'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/index.js
new file mode 100644
index 0000000000000..98771a57693a2
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/index.js
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ getService, loadTestFile }) {
+ describe('reporting app', function () {
+ const browser = getService('browser');
+
+ before(async () => {
+ await browser.setWindowSize(1200, 800);
+ });
+
+ loadTestFile(require.resolve('./reporting_watcher_png'));
+ loadTestFile(require.resolve('./reporting_watcher'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher.js b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher.js
new file mode 100644
index 0000000000000..c373c797bef50
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher.js
@@ -0,0 +1,94 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getWatcher, deleteWatcher, putWatcher } from './util';
+
+export default function ({ getService, getPageObjects }) {
+ describe('watcher app', function describeIndexTests() {
+ const config = getService('config');
+ const servers = config.get('servers');
+ const retry = getService('retry');
+ const log = getService('log');
+ const client = getService('es');
+
+ const KIBANAIP = process.env.KIBANAIP;
+ const VERSION_NUMBER = process.env.VERSION_NUMBER;
+ const VM = process.env.VM;
+ const VERSION_BUILD_HASH = process.env.VERSION_BUILD_HASH;
+ const STARTEDBY = process.env.STARTEDBY;
+ const REPORTING_TEST_EMAILS = process.env.REPORTING_TEST_EMAILS;
+
+ const PageObjects = getPageObjects(['common']);
+ describe('PDF Reporting watch', function () {
+ let id = 'watcher_report-';
+ id = id + new Date().getTime(); // For debugging.
+ const watch = { id };
+ const interval = 10;
+ const emails = REPORTING_TEST_EMAILS.split(',');
+
+ // https://localhost:5601/api/reporting/generate/printablePdf?jobParams=(objectType:dashboard,queryString:%27_g%3D(refreshInterval%3A(display%3AOff%2Cpause%3A!!f%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cmode%3Aquick%2Cto%3Anow))%26_a%3D(description%3A%2527%2527%2Cfilters%3A!!()%2CfullScreenMode%3A!!f%2Coptions%3A(darkTheme%3A!!f)%2Cpanels%3A!!((col%3A1%2Cid%3ASystem-Navigation%2CpanelIndex%3A9%2Crow%3A1%2Csize_x%3A8%2Csize_y%3A1%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3Ac6f2ffd0-4d17-11e7-a196-69b9a7a020a9%2CpanelIndex%3A11%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3Afe064790-1b1f-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A12%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%2527855899e0-1b1c-11e7-b09e-037021c4f8df%2527%2CpanelIndex%3A13%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%25277cdb1330-4d1a-11e7-a196-69b9a7a020a9%2527%2CpanelIndex%3A14%2Crow%3A9%2Csize_x%3A12%2Csize_y%3A6%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3A%2527522ee670-1b92-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A16%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A11%2Cid%3A%25271aae9140-1b93-11e7-8ada-3df93aab833e%2527%2CpanelIndex%3A17%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3A%2527825fdb80-4d1d-11e7-b5f2-2b7c1895bf32%2527%2CpanelIndex%3A18%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A5%2Cid%3Ad3166e80-1b91-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A19%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A3%2Cid%3A%252783e12df0-1b91-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A20%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3Ae9d22060-4d64-11e7-aa29-87a97a796de6%2CpanelIndex%3A21%2Crow%3A1%2Csize_x%3A4%2Csize_y%3A1%2Ctype%3Avisualization))%2Cquery%3A(language%3Alucene%2Cquery%3A(query_string%3A(analyze_wildcard%3A!!t%2Cquery%3A%2527*%2527)))%2CtimeRestore%3A!!f%2Ctitle%3A%2527Metricbeat%2Bsystem%2Boverview%2527%2CuiState%3A(P-11%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-12%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-14%3A(vis%3A(defaultColors%3A(%25270%2525%2B-%2B8.75%2525%2527%3A%2527rgb(247%2C252%2C245)%2527%2C%252717.5%2525%2B-%2B26.25%2525%2527%3A%2527rgb(116%2C196%2C118)%2527%2C%252726.25%2525%2B-%2B35%2525%2527%3A%2527rgb(35%2C139%2C69)%2527%2C%25278.75%2525%2B-%2B17.5%2525%2527%3A%2527rgb(199%2C233%2C192)%2527)))%2CP-16%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-2%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-3%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527))))%2CviewMode%3Aview)%27,savedObjectId:Metricbeat-system-overview)
+ // https://localhost:5601/api/reporting/generate/printablePdf?jobParams=(objectType:dashboard,queryString:%27_g%3D()%26_a%3D(description%3A%2527%2527%2Cfilters%3A!!()%2CfullScreenMode%3A!!f%2Coptions%3A(darkTheme%3A!!f)%2Cpanels%3A!!((col%3A1%2Cid%3ASystem-Navigation%2CpanelIndex%3A9%2Crow%3A1%2Csize_x%3A12%2Csize_y%3A1%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3Ac6f2ffd0-4d17-11e7-a196-69b9a7a020a9%2CpanelIndex%3A11%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3Afe064790-1b1f-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A12%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%2527855899e0-1b1c-11e7-b09e-037021c4f8df%2527%2CpanelIndex%3A13%2Crow%3A4%2Csize_x%3A6%2Csize_y%3A5%2Ctype%3Avisualization)%2C(col%3A1%2Cid%3A%25277cdb1330-4d1a-11e7-a196-69b9a7a020a9%2527%2CpanelIndex%3A14%2Crow%3A9%2Csize_x%3A12%2Csize_y%3A6%2Ctype%3Avisualization)%2C(col%3A9%2Cid%3A%2527522ee670-1b92-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A16%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A11%2Cid%3A%25271aae9140-1b93-11e7-8ada-3df93aab833e%2527%2CpanelIndex%3A17%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A7%2Cid%3A%2527825fdb80-4d1d-11e7-b5f2-2b7c1895bf32%2527%2CpanelIndex%3A18%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A5%2Cid%3Ad3166e80-1b91-11e7-bec4-a5e9ec5cab8b%2CpanelIndex%3A19%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization)%2C(col%3A3%2Cid%3A%252783e12df0-1b91-11e7-bec4-a5e9ec5cab8b%2527%2CpanelIndex%3A20%2Crow%3A2%2Csize_x%3A2%2Csize_y%3A2%2Ctype%3Avisualization))%2Cquery%3A(language%3Alucene%2Cquery%3A(query_string%3A(analyze_wildcard%3A!!t%2Cdefault_field%3A%2527*%2527%2Cquery%3A%2527*%2527)))%2CtimeRestore%3A!!f%2Ctitle%3A%2527%255BMetricbeat%2BSystem%255D%2BOverview%2527%2CuiState%3A(P-11%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-12%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-14%3A(vis%3A(defaultColors%3A(%25270%2525%2B-%2B8.75%2525%2527%3A%2527rgb(247%2C252%2C245)%2527%2C%252717.5%2525%2B-%2B26.25%2525%2527%3A%2527rgb(116%2C196%2C118)%2527%2C%252726.25%2525%2B-%2B35%2525%2527%3A%2527rgb(35%2C139%2C69)%2527%2C%25278.75%2525%2B-%2B17.5%2525%2527%3A%2527rgb(199%2C233%2C192)%2527)))%2CP-16%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-2%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527)))%2CP-3%3A(vis%3A(defaultColors%3A(%25270%2B-%2B100%2527%3A%2527rgb(0%2C104%2C55)%2527))))%2CviewMode%3Aview)%27,savedObjectId:Metricbeat-system-overview)
+ // https://localhost:5601
+ // "/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FChicago,layout:(dimensions:(height:540.5,width:633),id:preserve_layout),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2FLatency-histogram%3F_g%3D(refreshInterval:(pause:!!t,value:0),time:(from:now-24h,mode:quick,to:now))%26_a%3D(filters:!!(),linked:!!t,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(extended_bounds:(),field:responsetime,interval:10),schema:segment,type:histogram)),params:(addLegend:!!t,addTimeMarker:!!f,addTooltip:!!t,categoryAxes:!!((id:CategoryAxis-1,labels:(show:!!t,truncate:100),position:bottom,scale:(type:linear),show:!!t,style:(),title:(),type:category)),defaultYExtents:!!f,grid:(categoryLines:!!f,style:(color:%2523eee)),interpolate:linear,legendPosition:right,mode:stacked,scale:linear,seriesParams:!!((data:(id:!%271!%27,label:Count),interpolate:cardinal,mode:stacked,show:true,type:area,valueAxis:ValueAxis-1)),setYExtents:!!f,shareYAxis:!!t,smoothLines:!!t,times:!!(),type:area,valueAxes:!!((id:ValueAxis-1,labels:(filter:!!f,rotate:0,show:!!t,truncate:100),name:LeftAxis-1,position:left,scale:(defaultYExtents:!!f,mode:normal,setYExtents:!!f,type:linear),show:!!t,style:(),title:(text:Count),type:value)),yAxis:()),title:!%27Latency%2Bhistogram!%27,type:area))%27),title:%27Latency%20histogram%27)
+ const url =
+ servers.kibana.protocol +
+ '://' +
+ KIBANAIP +
+ ':' +
+ servers.kibana.port +
+ '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FChicago,layout:(dimensions:(height:2024,width:3006.400146484375),id:preserve_layout),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F722b74f0-b882-11e8-a6d9-e546fe2bba5f%3F_g%3D(refreshInterval:(pause:!!f,value:900000),time:(from:now-7d,to:now))%26_a%3D(description:!%27Analyze%2Bmock%2BeCommerce%2Borders%2Band%2Brevenue!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(vis:(colors:(!%27Men!!!%27s%2BAccessories!%27:%252382B5D8,!%27Men!!!%27s%2BClothing!%27:%2523F9BA8F,!%27Men!!!%27s%2BShoes!%27:%2523F29191,!%27Women!!!%27s%2BAccessories!%27:%2523F4D598,!%27Women!!!%27s%2BClothing!%27:%252370DBED,!%27Women!!!%27s%2BShoes!%27:%2523B7DBAB))),gridData:(h:10,i:!%271!%27,w:36,x:12,y:18),id:!%2737cc8650-b882-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%271!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(FEMALE:%25236ED0E0,MALE:%2523447EBC),legendOpen:!!f)),gridData:(h:11,i:!%272!%27,w:12,x:12,y:7),id:ed8436b0-b88b-11e8-a6d9-e546fe2bba5f,panelIndex:!%272!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:7,i:!%273!%27,w:18,x:0,y:0),id:!%2709ffee60-b88c-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%273!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:7,i:!%274!%27,w:30,x:18,y:0),id:!%271c389590-b88d-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%274!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:11,i:!%275!%27,w:48,x:0,y:28),id:!%2745e07720-b890-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%275!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:10,i:!%276!%27,w:12,x:0,y:18),id:!%2710f1a240-b891-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%276!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:11,i:!%277!%27,w:12,x:0,y:7),id:b80e6540-b891-11e8-a6d9-e546fe2bba5f,panelIndex:!%277!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(!%270%2B-%2B50!%27:%2523E24D42,!%2750%2B-%2B75!%27:%2523EAB839,!%2775%2B-%2B100!%27:%25237EB26D),defaultColors:(!%270%2B-%2B50!%27:!%27rgb(165,0,38)!%27,!%2750%2B-%2B75!%27:!%27rgb(255,255,190)!%27,!%2775%2B-%2B100!%27:!%27rgb(0,104,55)!%27),legendOpen:!!f)),gridData:(h:11,i:!%278!%27,w:12,x:24,y:7),id:!%274b3ec120-b892-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%278!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(!%270%2B-%2B2!%27:%2523E24D42,!%272%2B-%2B3!%27:%2523F2C96D,!%273%2B-%2B4!%27:%25239AC48A),defaultColors:(!%270%2B-%2B2!%27:!%27rgb(165,0,38)!%27,!%272%2B-%2B3!%27:!%27rgb(255,255,190)!%27,!%273%2B-%2B4!%27:!%27rgb(0,104,55)!%27),legendOpen:!!f)),gridData:(h:11,i:!%279!%27,w:12,x:36,y:7),id:!%279ca7aa90-b892-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%279!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:18,i:!%2710!%27,w:48,x:0,y:54),id:!%273ba638e0-b894-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%2710!%27,type:search,version:!%277.0.0-alpha1!%27),(embeddableConfig:(isLayerTOCOpen:!!f,mapCenter:(lat:45.88578,lon:-15.07605,zoom:2.11),openTOCDetails:!!()),gridData:(h:15,i:!%2711!%27,w:24,x:0,y:39),id:!%272c9c1f60-1909-11e9-919b-ffe5949a18d2!%27,panelIndex:!%2711!%27,type:map,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:15,i:!%2712!%27,w:24,x:24,y:39),id:b72dd430-bb4d-11e8-9c84-77068524bcab,panelIndex:!%2712!%27,type:visualization,version:!%277.0.0-alpha1!%27)),query:(language:kuery,query:!%27!%27),timeRestore:!!t,title:!%27%255BeCommerce%255D%2BRevenue%2BDashboard!%27,viewMode:view)%27),title:%27%5BeCommerce%5D%20Revenue%20Dashboard%27)';
+
+ const body = {
+ trigger: {
+ schedule: {
+ interval: `${interval}s`,
+ },
+ },
+ actions: {
+ email_admin: {
+ email: {
+ to: emails,
+ subject:
+ 'PDF ' +
+ VERSION_NUMBER +
+ ' ' +
+ id +
+ ', VM=' +
+ VM +
+ ' ' +
+ VERSION_BUILD_HASH +
+ ' by:' +
+ STARTEDBY,
+ attachments: {
+ 'test_report.pdf': {
+ reporting: {
+ url: url,
+ auth: {
+ basic: {
+ username: servers.elasticsearch.username,
+ password: servers.elasticsearch.password,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ it('should successfully add a new watch for PDF Reporting', async () => {
+ await putWatcher(watch, id, body, client, log);
+ });
+ it('should be successful and increment revision', async () => {
+ await getWatcher(watch, id, client, log, PageObjects.common, retry.tryForTime);
+ });
+ it('should delete watch and update revision', async () => {
+ await deleteWatcher(watch, id, client, log);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher_png.js b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher_png.js
new file mode 100644
index 0000000000000..ac247cc23900d
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher_png.js
@@ -0,0 +1,88 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getWatcher, deleteWatcher, putWatcher } from './util';
+
+export default ({ getService, getPageObjects }) => {
+ describe('watcher app', () => {
+ const config = getService('config');
+ const servers = config.get('servers');
+ const retry = getService('retry');
+ const log = getService('log');
+ const client = getService('es');
+
+ const KIBANAIP = process.env.KIBANAIP;
+ const VERSION_NUMBER = process.env.VERSION_NUMBER;
+ const VM = process.env.VM;
+ const VERSION_BUILD_HASH = process.env.VERSION_BUILD_HASH;
+ const STARTEDBY = process.env.STARTEDBY;
+ const REPORTING_TEST_EMAILS = process.env.REPORTING_TEST_EMAILS;
+
+ const PageObjects = getPageObjects(['common']);
+ describe('PNG Reporting watch', () => {
+ let id = 'watcher_png_report-';
+ id = id + new Date().getTime(); // For debugging.
+ const watch = { id };
+ const reportingUrl =
+ servers.kibana.protocol +
+ '://' +
+ KIBANAIP +
+ ':' +
+ servers.kibana.port +
+ '/api/reporting/generate/png?jobParams=(browserTimezone:America%2FChicago,layout:(dimensions:(height:2024,width:3006.400146484375),id:png),objectType:dashboard,relativeUrl:%27%2Fapp%2Fkibana%23%2Fdashboard%2F722b74f0-b882-11e8-a6d9-e546fe2bba5f%3F_g%3D(refreshInterval:(pause:!!f,value:900000),time:(from:now-7d,to:now))%26_a%3D(description:!%27Analyze%2Bmock%2BeCommerce%2Borders%2Band%2Brevenue!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(vis:(colors:(!%27Men!!!%27s%2BAccessories!%27:%252382B5D8,!%27Men!!!%27s%2BClothing!%27:%2523F9BA8F,!%27Men!!!%27s%2BShoes!%27:%2523F29191,!%27Women!!!%27s%2BAccessories!%27:%2523F4D598,!%27Women!!!%27s%2BClothing!%27:%252370DBED,!%27Women!!!%27s%2BShoes!%27:%2523B7DBAB))),gridData:(h:10,i:!%271!%27,w:36,x:12,y:18),id:!%2737cc8650-b882-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%271!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(FEMALE:%25236ED0E0,MALE:%2523447EBC),legendOpen:!!f)),gridData:(h:11,i:!%272!%27,w:12,x:12,y:7),id:ed8436b0-b88b-11e8-a6d9-e546fe2bba5f,panelIndex:!%272!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:7,i:!%273!%27,w:18,x:0,y:0),id:!%2709ffee60-b88c-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%273!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:7,i:!%274!%27,w:30,x:18,y:0),id:!%271c389590-b88d-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%274!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:11,i:!%275!%27,w:48,x:0,y:28),id:!%2745e07720-b890-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%275!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:10,i:!%276!%27,w:12,x:0,y:18),id:!%2710f1a240-b891-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%276!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:11,i:!%277!%27,w:12,x:0,y:7),id:b80e6540-b891-11e8-a6d9-e546fe2bba5f,panelIndex:!%277!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(!%270%2B-%2B50!%27:%2523E24D42,!%2750%2B-%2B75!%27:%2523EAB839,!%2775%2B-%2B100!%27:%25237EB26D),defaultColors:(!%270%2B-%2B50!%27:!%27rgb(165,0,38)!%27,!%2750%2B-%2B75!%27:!%27rgb(255,255,190)!%27,!%2775%2B-%2B100!%27:!%27rgb(0,104,55)!%27),legendOpen:!!f)),gridData:(h:11,i:!%278!%27,w:12,x:24,y:7),id:!%274b3ec120-b892-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%278!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(vis:(colors:(!%270%2B-%2B2!%27:%2523E24D42,!%272%2B-%2B3!%27:%2523F2C96D,!%273%2B-%2B4!%27:%25239AC48A),defaultColors:(!%270%2B-%2B2!%27:!%27rgb(165,0,38)!%27,!%272%2B-%2B3!%27:!%27rgb(255,255,190)!%27,!%273%2B-%2B4!%27:!%27rgb(0,104,55)!%27),legendOpen:!!f)),gridData:(h:11,i:!%279!%27,w:12,x:36,y:7),id:!%279ca7aa90-b892-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%279!%27,type:visualization,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:18,i:!%2710!%27,w:48,x:0,y:54),id:!%273ba638e0-b894-11e8-a6d9-e546fe2bba5f!%27,panelIndex:!%2710!%27,type:search,version:!%277.0.0-alpha1!%27),(embeddableConfig:(isLayerTOCOpen:!!f,mapCenter:(lat:45.88578,lon:-15.07605,zoom:2.11),openTOCDetails:!!()),gridData:(h:15,i:!%2711!%27,w:24,x:0,y:39),id:!%272c9c1f60-1909-11e9-919b-ffe5949a18d2!%27,panelIndex:!%2711!%27,type:map,version:!%277.0.0-alpha1!%27),(embeddableConfig:(),gridData:(h:15,i:!%2712!%27,w:24,x:24,y:39),id:b72dd430-bb4d-11e8-9c84-77068524bcab,panelIndex:!%2712!%27,type:visualization,version:!%277.0.0-alpha1!%27)),query:(language:kuery,query:!%27!%27),timeRestore:!!t,title:!%27%255BeCommerce%255D%2BRevenue%2BDashboard!%27,viewMode:view)%27,title:%27%5BeCommerce%5D%20Revenue%20Dashboard%27)';
+ const emails = REPORTING_TEST_EMAILS.split(',');
+ const interval = 10;
+ const body = {
+ trigger: {
+ schedule: {
+ interval: `${interval}s`,
+ },
+ },
+ actions: {
+ email_admin: {
+ email: {
+ to: emails,
+ subject:
+ 'PNG ' +
+ VERSION_NUMBER +
+ ' ' +
+ id +
+ ', VM=' +
+ VM +
+ ' ' +
+ VERSION_BUILD_HASH +
+ ' by:' +
+ STARTEDBY,
+ attachments: {
+ 'test_report.png': {
+ reporting: {
+ url: reportingUrl,
+ auth: {
+ basic: {
+ username: servers.elasticsearch.username,
+ password: servers.elasticsearch.password,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ it('should successfully add a new watch for PNG Reporting', async () => {
+ await putWatcher(watch, id, body, client, log);
+ });
+ it('should be successful and increment revision', async () => {
+ await getWatcher(watch, id, client, log, PageObjects.common, retry.tryForTime);
+ });
+ it('should delete watch and update revision', async () => {
+ await deleteWatcher(watch, id, client, log);
+ });
+ });
+ });
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/util.js b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/util.js
new file mode 100644
index 0000000000000..3c959656a3c57
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/reporting/util.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export const pretty = (x) => JSON.stringify(x, null, 2);
+export const buildUrl = ({ protocol, auth, hostname, port }) =>
+ new URL(`${protocol}://${auth}@${hostname}:${port}`);
+export const putWatcher = async (watch, id, body, client, log) => {
+ const putWatchResponse = await client.watcher.putWatch({ ...watch, body });
+ log.debug(`# putWatchResponse \n${pretty(putWatchResponse)}`);
+ expect(putWatchResponse.body._id).to.eql(id);
+ expect(putWatchResponse.statusCode).to.eql('201');
+ expect(putWatchResponse.body._version).to.eql('1');
+};
+export const getWatcher = async (watch, id, client, log, common, tryForTime) => {
+ await common.sleep(50000);
+ await tryForTime(
+ 250000,
+ async () => {
+ await common.sleep(25000);
+
+ await watcherHistory(id, client, log);
+
+ const getWatchResponse = await client.watcher.getWatch(watch);
+ log.debug(`\n getWatchResponse: ${JSON.stringify(getWatchResponse)}`);
+ expect(getWatchResponse.body._id).to.eql(id);
+ expect(getWatchResponse.body._version).to.be.above(1);
+ log.debug(`\n getWatchResponse.body._version: ${getWatchResponse.body._version}`);
+ expect(getWatchResponse.body.status.execution_state).to.eql('executed');
+ expect(getWatchResponse.body.status.actions.email_admin.last_execution.successful).to.eql(
+ true
+ );
+
+ return getWatchResponse;
+ },
+ async function onFailure(obj) {
+ log.debug(`\n### tryForTime-Failure--raw body: \n\t${pretty(obj)}`);
+ }
+ );
+};
+export const deleteWatcher = async (watch, id, client, log) => {
+ const deleteResponse = await client.watcher.deleteWatch(watch);
+ log.debug('\nDelete Response=' + pretty(deleteResponse) + '\n');
+ expect(deleteResponse.body._id).to.eql(id);
+ expect(deleteResponse.body.found).to.eql(true);
+ expect(deleteResponse.statusCode).to.eql('200');
+};
+async function watcherHistory(watchId, client, log) {
+ const { body } = await client.search({
+ index: '.watcher-history*',
+ body: {
+ query: {
+ bool: {
+ filter: [
+ {
+ bool: {
+ should: [
+ {
+ match_phrase: {
+ watchId,
+ },
+ },
+ ],
+ minimum_should_match: 1,
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+ log.debug(`\nwatcherHistoryResponse \n${pretty(body)}\n`);
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/e_commerce.js b/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/e_commerce.js
new file mode 100644
index 0000000000000..306f30133f6ee
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/e_commerce.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ getService, getPageObjects }) {
+ describe('eCommerce Sample Data', function sampleData() {
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common', 'home']);
+ const testSubjects = getService('testSubjects');
+
+ before(async () => {
+ await browser.setWindowSize(1200, 800);
+ await PageObjects.common.navigateToUrl('home', '/home/tutorial_directory/sampleData', {
+ useActualUrl: true,
+ insertTimestamp: false,
+ });
+ await PageObjects.common.sleep(3000);
+ });
+
+ it('install eCommerce sample data', async function installECommerceData() {
+ await PageObjects.home.addSampleDataSet('ecommerce');
+ await PageObjects.common.sleep(5000);
+ // verify it's installed by finding the remove link
+ await testSubjects.find('removeSampleDataSetecommerce');
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/index.js
new file mode 100644
index 0000000000000..4b9178c753b9a
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default ({ loadTestFile }) => {
+ describe('sample data', function () {
+ loadTestFile(require.resolve('./e_commerce'));
+ });
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/_telemetry.js b/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/_telemetry.js
new file mode 100644
index 0000000000000..09698675f0678
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/_telemetry.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default ({ getService, getPageObjects }) => {
+ const log = getService('log');
+ const browser = getService('browser');
+ const appsMenu = getService('appsMenu');
+ const PageObjects = getPageObjects(['common', 'monitoring', 'header']);
+
+ describe('telemetry', function () {
+ before(async () => {
+ log.debug('monitoring');
+ await browser.setWindowSize(1200, 800);
+ await appsMenu.clickLink('Stack Monitoring');
+ });
+
+ it('should show banner Help us improve Kibana and Elasticsearch', async () => {
+ const expectedMessage = `Help us improve the Elastic Stack
+To learn about how usage data helps us manage and improve our products and services, see our Privacy Statement. To stop collection, disable usage data here.
+Dismiss`;
+ const actualMessage = await PageObjects.monitoring.getWelcome();
+ log.debug(`X-Pack message = ${actualMessage}`);
+ expect(actualMessage).to.be(expectedMessage);
+ });
+ });
+};
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/index.js
new file mode 100644
index 0000000000000..0803f48ed90fe
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/index.js
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('telemetry feature', function () {
+ this.tags('ciGroup1');
+ loadTestFile(require.resolve('./_telemetry'));
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/_winlogbeat.js b/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/_winlogbeat.js
new file mode 100644
index 0000000000000..657fdf4daaeb4
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/_winlogbeat.js
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+
+export default function ({ getService, getPageObjects }) {
+ const log = getService('log');
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
+ const retry = getService('retry');
+ const appsMenu = getService('appsMenu');
+
+ describe('check winlogbeat', function () {
+ it('winlogbeat- should have hit count GT 0', async function () {
+ const url = await browser.getCurrentUrl();
+ log.debug(url);
+ if (!url.includes('kibana')) {
+ await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
+ } else if (!url.includes('discover')) {
+ await appsMenu.clickLink('Discover');
+ }
+ await PageObjects.discover.selectIndexPattern('winlogbeat-*');
+ await PageObjects.timePicker.setCommonlyUsedTime('Today');
+ await retry.try(async function () {
+ const hitCount = parseInt(await PageObjects.discover.getHitCount());
+ expect(hitCount).to.be.greaterThan(0);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/index.js b/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/index.js
new file mode 100644
index 0000000000000..a940be781ccfe
--- /dev/null
+++ b/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/index.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export default function ({ loadTestFile }) {
+ describe('winlogbeat app', function () {
+ loadTestFile(require.resolve('./_winlogbeat'));
+ });
+}
diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/status.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/status.ts
index 76cea64bffc1c..d13b9836f25a1 100644
--- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/status.ts
+++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/status.ts
@@ -6,6 +6,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../api_integration/ftr_provider_context';
+import { ClusterStateAPIResponse } from '../../../plugins/upgrade_assistant/common/types';
import { getIndexStateFromClusterState } from '../../../plugins/upgrade_assistant/common/get_index_state_from_cluster_state';
// eslint-disable-next-line import/no-default-export
@@ -28,7 +29,7 @@ export default function ({ getService }: FtrProviderContext) {
it('the _cluster/state endpoint is still what we expect', async () => {
await esArchiver.load('upgrade_assistant/reindex');
await es.indices.close({ index: '7.0-data' });
- const result = await es.cluster.state({
+ const result = await es.cluster.state({
index: '7.0-data',
metric: 'metadata',
});
diff --git a/yarn.lock b/yarn.lock
index acf7c3a1e8754..2d575634686a3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2172,6 +2172,17 @@
redux-immutable-state-invariant "^2.1.0"
redux-logger "^3.0.6"
+"@elastic/elasticsearch@7.8.0":
+ version "7.8.0"
+ resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.8.0.tgz#3f9ee54fe8ef79874ebd231db03825fa500a7111"
+ integrity sha512-rUOTNN1At0KoN0Fcjd6+J7efghuURnoMTB/od9EMK6Mcdebi6N3z5ulShTsKRn6OanS9Eq3l/OmheQY1Y+WLcg==
+ dependencies:
+ debug "^4.1.1"
+ decompress-response "^4.2.0"
+ ms "^2.1.1"
+ pump "^3.0.0"
+ secure-json-parse "^2.1.0"
+
"@elastic/elasticsearch@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.4.0.tgz#57f4066acf25e9d4e9b4f6376088433aae6f25d4"
@@ -27784,6 +27795,11 @@ scss-tokenizer@^0.2.3:
js-base64 "^2.1.8"
source-map "^0.4.2"
+secure-json-parse@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.1.0.tgz#ae76f5624256b5c497af887090a5d9e156c9fb20"
+ integrity sha512-GckO+MS/wT4UogDyoI/H/S1L0MCcKS1XX/vp48wfmU7Nw4woBmb8mIpu4zPBQjKlRT88/bt9xdoV4111jPpNJA==
+
seedrandom@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"