[Ingest Manager] Integrate beta messaging with Add Data (#71147) (#71311)

* Add methods to register directory notices and header links in tutorials, and use registered components when rendering tutorial directory

* Add methods to register module notices components in tutorial pages, and use registered components when rendering tutorial page

* Add `moduleName` field to server tutorial schema and test fixure

* Surface `moduleName` field from built in tutorials and registered apm tutorial

* Export component types

* Add KibanaContextProvider to home plugin app render

* Move setHttpClient to ingest manager plugin setup() method; add home as optional plugin dep; register tutorial module notice

* Fix key prop warnings

* Add dismissable tutorial directory notice and corresponding ingest manager global setting field

* Add tutorial directory header link and tie it to the state of the dismissible directory notice via observable

* Put spacing inside module notice component itself

* Check if ingest manager is available in current space

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
# Conflicts:
#	src/plugins/home/server/tutorials/oracle_metrics/index.ts
This commit is contained in:
Jen Huang 2020-07-09 15:30:17 -07:00 committed by GitHub
parent 8ec785a7d1
commit 924cd0c950
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 662 additions and 70 deletions

View file

@ -26,6 +26,7 @@ import { APM_STATIC_INDEX_PATTERN_ID } from '../../common/index_pattern_constant
const apmIntro = i18n.translate('apmOss.tutorial.introduction', {
defaultMessage: 'Collect in-depth performance metrics and errors from inside your applications.',
});
const moduleName = 'apm';
export const tutorialProvider = ({
indexPatternTitle,
@ -68,6 +69,7 @@ export const tutorialProvider = ({
name: i18n.translate('apmOss.tutorial.specProvider.name', {
defaultMessage: 'APM',
}),
moduleName,
category: TutorialsCategory.OTHER,
shortDescription: apmIntro,
longDescription: i18n.translate('apmOss.tutorial.specProvider.longDescription', {

View file

@ -20,14 +20,19 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { i18n } from '@kbn/i18n';
import { ScopedHistory } from 'kibana/public';
import { ScopedHistory, CoreStart } from 'kibana/public';
import { KibanaContextProvider } from '../../../kibana_react/public';
// @ts-ignore
import { HomeApp } from './components/home_app';
import { getServices } from './kibana_services';
import './index.scss';
export const renderApp = async (element: HTMLElement, history: ScopedHistory) => {
export const renderApp = async (
element: HTMLElement,
coreStart: CoreStart,
history: ScopedHistory
) => {
const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' });
const { featureCatalogue, chrome } = getServices();
@ -36,7 +41,12 @@ export const renderApp = async (element: HTMLElement, history: ScopedHistory) =>
chrome.setBreadcrumbs([{ text: homeTitle }]);
render(<HomeApp directories={directories} />, element);
render(
<KibanaContextProvider services={{ ...coreStart }}>
<HomeApp directories={directories} />
</KibanaContextProvider>,
element
);
// dispatch synthetic hash change event to update hash history objects
// this is necessary because hash updates triggered by using popState won't trigger this event naturally.

View file

@ -333,6 +333,23 @@ class TutorialUi extends React.Component {
}
};
renderModuleNotices() {
const notices = getServices().tutorialService.getModuleNotices();
if (notices.length && this.state.tutorial.moduleName) {
return (
<EuiFlexGroup direction="column" gutterSize="none">
{notices.map((ModuleNotice, index) => (
<EuiFlexItem key={index}>
<ModuleNotice moduleName={this.state.tutorial.moduleName} />
</EuiFlexItem>
))}
</EuiFlexGroup>
);
} else {
return null;
}
}
render() {
let content;
if (this.state.notFound) {
@ -381,6 +398,7 @@ class TutorialUi extends React.Component {
isBeta={this.state.tutorial.isBeta}
/>
{this.renderModuleNotices()}
<EuiSpacer />
<div className="eui-textCenter">{this.renderInstructionSetsToggle()}</div>

View file

@ -28,6 +28,9 @@ jest.mock('../../kibana_services', () => ({
chrome: {
setBreadcrumbs: () => {},
},
tutorialService: {
getModuleNotices: () => [],
},
}),
}));
jest.mock('../../../../../kibana_react/public', () => {

View file

@ -30,6 +30,7 @@ import {
EuiTab,
EuiFlexItem,
EuiFlexGrid,
EuiFlexGroup,
EuiSpacer,
EuiTitle,
EuiPageBody,
@ -102,6 +103,7 @@ class TutorialDirectoryUi extends React.Component {
this.state = {
selectedTabId: openTab,
tutorialCards: [],
notices: getServices().tutorialService.getDirectoryNotices(),
};
}
@ -227,18 +229,62 @@ class TutorialDirectoryUi extends React.Component {
);
};
renderNotices = () => {
const notices = getServices().tutorialService.getDirectoryNotices();
return notices.length ? (
<EuiFlexGroup direction="column" gutterSize="none">
{notices.map((DirectoryNotice, index) => (
<EuiFlexItem key={index}>
<DirectoryNotice />
</EuiFlexItem>
))}
</EuiFlexGroup>
) : null;
};
renderHeaderLinks = () => {
const headerLinks = getServices().tutorialService.getDirectoryHeaderLinks();
return headerLinks.length ? (
<EuiFlexGroup gutterSize="m" alignItems="center">
{headerLinks.map((HeaderLink, index) => (
<EuiFlexItem key={index}>
<HeaderLink />
</EuiFlexItem>
))}
</EuiFlexGroup>
) : null;
};
renderHeader = () => {
const notices = this.renderNotices();
const headerLinks = this.renderHeaderLinks();
return (
<>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="home.tutorial.addDataToKibanaTitle"
defaultMessage="Add data"
/>
</h1>
</EuiTitle>
</EuiFlexItem>
{headerLinks ? <EuiFlexItem grow={false}>{headerLinks}</EuiFlexItem> : null}
</EuiFlexGroup>
{notices}
</>
);
};
render() {
return (
<EuiPage restrictWidth={1200}>
<EuiPageBody>
<EuiTitle size="l">
<h1>
<FormattedMessage id="home.tutorial.addDataToKibanaTitle" defaultMessage="Add data" />
</h1>
</EuiTitle>
{this.renderHeader()}
<EuiSpacer size="m" />
<EuiTabs>{this.renderTabs()}</EuiTabs>
<EuiSpacer />
{this.renderTabContent()}

View file

@ -30,6 +30,9 @@ export {
FeatureCatalogueCategory,
Environment,
TutorialVariables,
TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './services';
export * from '../common/instruction_variant';
import { HomePublicPlugin } from './plugin';

View file

@ -104,7 +104,7 @@ export class HomePublicPlugin
i18n.translate('home.pageTitle', { defaultMessage: 'Home' })
);
const { renderApp } = await import('./application');
return await renderApp(params.element, params.history);
return await renderApp(params.element, coreStart, params.history);
},
});
kibanaLegacy.forwardApp('home', 'home');

View file

@ -17,4 +17,11 @@
* under the License.
*/
export { TutorialService, TutorialVariables, TutorialServiceSetup } from './tutorial_service';
export {
TutorialService,
TutorialVariables,
TutorialServiceSetup,
TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './tutorial_service';

View file

@ -22,6 +22,9 @@ import { TutorialService, TutorialServiceSetup } from './tutorial_service';
const createSetupMock = (): jest.Mocked<TutorialServiceSetup> => {
const setup = {
setVariable: jest.fn(),
registerDirectoryNotice: jest.fn(),
registerDirectoryHeaderLink: jest.fn(),
registerModuleNotice: jest.fn(),
};
return setup;
};
@ -30,6 +33,9 @@ const createMock = (): jest.Mocked<PublicMethodsOf<TutorialService>> => {
const service = {
setup: jest.fn(),
getVariables: jest.fn(() => ({})),
getDirectoryNotices: jest.fn(() => []),
getDirectoryHeaderLinks: jest.fn(() => []),
getModuleNotices: jest.fn(() => []),
};
service.setup.mockImplementation(createSetupMock);
return service;

View file

@ -1,55 +0,0 @@
/*
* 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 { TutorialService } from './tutorial_service';
describe('TutorialService', () => {
describe('setup', () => {
test('allows multiple set calls', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.setVariable('abc', 123);
setup.setVariable('def', 456);
}).not.toThrow();
});
test('throws when same variable is set twice', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.setVariable('abc', 123);
setup.setVariable('abc', 456);
}).toThrow();
});
});
describe('getVariables', () => {
test('returns empty object', () => {
const service = new TutorialService();
expect(service.getVariables()).toEqual({});
});
test('returns last state of update calls', () => {
const service = new TutorialService();
const setup = service.setup();
setup.setVariable('abc', 123);
setup.setVariable('def', { subKey: 456 });
expect(service.getVariables()).toEqual({ abc: 123, def: { subKey: 456 } });
});
});
});

View file

@ -0,0 +1,151 @@
/*
* 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 React from 'react';
import { TutorialService } from './tutorial_service';
describe('TutorialService', () => {
describe('setup', () => {
test('allows multiple set variable calls', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.setVariable('abc', 123);
setup.setVariable('def', 456);
}).not.toThrow();
});
test('throws when same variable is set twice', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.setVariable('abc', 123);
setup.setVariable('abc', 456);
}).toThrow();
});
test('allows multiple register directory notice calls', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerDirectoryNotice('abc', () => <div />);
setup.registerDirectoryNotice('def', () => <span />);
}).not.toThrow();
});
test('throws when same directory notice is registered twice', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerDirectoryNotice('abc', () => <div />);
setup.registerDirectoryNotice('abc', () => <span />);
}).toThrow();
});
test('allows multiple register directory header link calls', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerDirectoryHeaderLink('abc', () => <a>123</a>);
setup.registerDirectoryHeaderLink('def', () => <a>456</a>);
}).not.toThrow();
});
test('throws when same directory header link is registered twice', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerDirectoryHeaderLink('abc', () => <a>123</a>);
setup.registerDirectoryHeaderLink('abc', () => <a>456</a>);
}).toThrow();
});
test('allows multiple register module notice calls', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerModuleNotice('abc', () => <div />);
setup.registerModuleNotice('def', () => <span />);
}).not.toThrow();
});
test('throws when same module notice is registered twice', () => {
const setup = new TutorialService().setup();
expect(() => {
setup.registerModuleNotice('abc', () => <div />);
setup.registerModuleNotice('abc', () => <span />);
}).toThrow();
});
});
describe('getVariables', () => {
test('returns empty object', () => {
const service = new TutorialService();
expect(service.getVariables()).toEqual({});
});
test('returns last state of update calls', () => {
const service = new TutorialService();
const setup = service.setup();
setup.setVariable('abc', 123);
setup.setVariable('def', { subKey: 456 });
expect(service.getVariables()).toEqual({ abc: 123, def: { subKey: 456 } });
});
});
describe('getDirectoryNotices', () => {
test('returns empty array', () => {
const service = new TutorialService();
expect(service.getDirectoryNotices()).toEqual([]);
});
test('returns last state of register calls', () => {
const service = new TutorialService();
const setup = service.setup();
const notices = [() => <div />, () => <span />];
setup.registerDirectoryNotice('abc', notices[0]);
setup.registerDirectoryNotice('def', notices[1]);
expect(service.getDirectoryNotices()).toEqual(notices);
});
});
describe('getDirectoryHeaderLinks', () => {
test('returns empty array', () => {
const service = new TutorialService();
expect(service.getDirectoryHeaderLinks()).toEqual([]);
});
test('returns last state of register calls', () => {
const service = new TutorialService();
const setup = service.setup();
const links = [() => <a>123</a>, () => <a>456</a>];
setup.registerDirectoryHeaderLink('abc', links[0]);
setup.registerDirectoryHeaderLink('def', links[1]);
expect(service.getDirectoryHeaderLinks()).toEqual(links);
});
});
describe('getModuleNotices', () => {
test('returns empty array', () => {
const service = new TutorialService();
expect(service.getModuleNotices()).toEqual([]);
});
test('returns last state of register calls', () => {
const service = new TutorialService();
const setup = service.setup();
const notices = [() => <div />, () => <span />];
setup.registerModuleNotice('abc', notices[0]);
setup.registerModuleNotice('def', notices[1]);
expect(service.getModuleNotices()).toEqual(notices);
});
});
});

View file

@ -16,12 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
/** @public */
export type TutorialVariables = Partial<Record<string, unknown>>;
/** @public */
export type TutorialDirectoryNoticeComponent = React.FC;
/** @public */
export type TutorialDirectoryHeaderLinkComponent = React.FC;
/** @public */
export type TutorialModuleNoticeComponent = React.FC<{
moduleName: string;
}>;
export class TutorialService {
private tutorialVariables: TutorialVariables = {};
private tutorialDirectoryNotices: { [key: string]: TutorialDirectoryNoticeComponent } = {};
private tutorialDirectoryHeaderLinks: {
[key: string]: TutorialDirectoryHeaderLinkComponent;
} = {};
private tutorialModuleNotices: { [key: string]: TutorialModuleNoticeComponent } = {};
public setup() {
return {
@ -34,12 +51,57 @@ export class TutorialService {
}
this.tutorialVariables[key] = value;
},
/**
* Registers a component that will be rendered at the top of tutorial directory page.
*/
registerDirectoryNotice: (id: string, component: TutorialDirectoryNoticeComponent) => {
if (this.tutorialDirectoryNotices[id]) {
throw new Error(`directory notice ${id} already set`);
}
this.tutorialDirectoryNotices[id] = component;
},
/**
* Registers a component that will be rendered next to tutorial directory title/header area.
*/
registerDirectoryHeaderLink: (
id: string,
component: TutorialDirectoryHeaderLinkComponent
) => {
if (this.tutorialDirectoryHeaderLinks[id]) {
throw new Error(`directory header link ${id} already set`);
}
this.tutorialDirectoryHeaderLinks[id] = component;
},
/**
* Registers a component that will be rendered in the description of a tutorial that is associated with a module.
*/
registerModuleNotice: (id: string, component: TutorialModuleNoticeComponent) => {
if (this.tutorialModuleNotices[id]) {
throw new Error(`module notice ${id} already set`);
}
this.tutorialModuleNotices[id] = component;
},
};
}
public getVariables() {
return this.tutorialVariables;
}
public getDirectoryNotices() {
return Object.values(this.tutorialDirectoryNotices);
}
public getDirectoryHeaderLinks() {
return Object.values(this.tutorialDirectoryHeaderLinks);
}
public getModuleNotices() {
return Object.values(this.tutorialModuleNotices);
}
}
export type TutorialServiceSetup = ReturnType<TutorialService['setup']>;

View file

@ -110,6 +110,7 @@ export const tutorialSchema = {
.required(),
category: Joi.string().valid(Object.values(TUTORIAL_CATEGORY)).required(),
name: Joi.string().required(),
moduleName: Joi.string(),
isBeta: Joi.boolean().default(false),
shortDescription: Joi.string().required(),
euiIconType: Joi.string(), // EUI icon type string, one of https://elastic.github.io/eui/#/icons

View file

@ -80,6 +80,7 @@ export interface TutorialSchema {
id: string;
category: TutorialsCategory;
name: string;
moduleName?: string;
isBeta?: boolean;
shortDescription: string;
euiIconType?: IconType; // EUI icon type string, one of https://elastic.github.io/eui/#/display/icons;

View file

@ -54,6 +54,7 @@ const VALID_TUTORIAL: TutorialSchema = {
id: 'test',
category: 'logging' as TutorialsCategory,
name: 'new tutorial provider',
moduleName: 'test',
isBeta: false,
shortDescription: 'short description',
euiIconType: 'alert',

View file

@ -37,6 +37,7 @@ export function activemqLogsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.activemqLogs.nameTitle', {
defaultMessage: 'ActiveMQ logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.activemqLogs.shortDescription', {
defaultMessage: 'Collect ActiveMQ logs with Filebeat.',

View file

@ -36,6 +36,7 @@ export function activemqMetricsSpecProvider(context: TutorialContext): TutorialS
name: i18n.translate('home.tutorials.activemqMetrics.nameTitle', {
defaultMessage: 'ActiveMQ metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.activemqMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from ActiveMQ instances.',

View file

@ -36,6 +36,7 @@ export function aerospikeMetricsSpecProvider(context: TutorialContext): Tutorial
name: i18n.translate('home.tutorials.aerospikeMetrics.nameTitle', {
defaultMessage: 'Aerospike metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.aerospikeMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function apacheLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.apacheLogs.nameTitle', {
defaultMessage: 'Apache logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.apacheLogs.shortDescription', {
defaultMessage: 'Collect and parse access and error logs created by the Apache HTTP server.',

View file

@ -36,6 +36,7 @@ export function apacheMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.apacheMetrics.nameTitle', {
defaultMessage: 'Apache metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.apacheMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from the Apache 2 HTTP server.',

View file

@ -31,11 +31,13 @@ import {
export function auditbeatSpecProvider(context: TutorialContext): TutorialSchema {
const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const;
const moduleName = 'auditbeat';
return {
id: 'auditbeat',
name: i18n.translate('home.tutorials.auditbeat.nameTitle', {
defaultMessage: 'Auditbeat',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.auditbeat.shortDescription', {
defaultMessage: 'Collect audit data from your hosts.',

View file

@ -37,6 +37,7 @@ export function awsLogsSpecProvider(context: TutorialContext): TutorialSchema {
name: i18n.translate('home.tutorials.awsLogs.nameTitle', {
defaultMessage: 'AWS S3 based logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.awsLogs.shortDescription', {
defaultMessage: 'Collect AWS logs from S3 bucket with Filebeat.',

View file

@ -36,6 +36,7 @@ export function awsMetricsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.awsMetrics.nameTitle', {
defaultMessage: 'AWS metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.awsMetrics.shortDescription', {
defaultMessage:

View file

@ -37,6 +37,7 @@ export function azureLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.azureLogs.nameTitle', {
defaultMessage: 'Azure logs',
}),
moduleName,
isBeta: true,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.azureLogs.shortDescription', {

View file

@ -36,6 +36,7 @@ export function azureMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.azureMetrics.nameTitle', {
defaultMessage: 'Azure metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.azureMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function cephMetricsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.cephMetrics.nameTitle', {
defaultMessage: 'Ceph metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.cephMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function ciscoLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.ciscoLogs.nameTitle', {
defaultMessage: 'Cisco',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.ciscoLogs.shortDescription', {
defaultMessage: 'Collect and parse logs received from Cisco ASA firewalls.',

View file

@ -30,11 +30,13 @@ import {
} from '../../services/tutorials/lib/tutorials_registry_types';
export function cloudwatchLogsSpecProvider(context: TutorialContext): TutorialSchema {
const moduleName = 'aws';
return {
id: 'cloudwatchLogs',
name: i18n.translate('home.tutorials.cloudwatchLogs.nameTitle', {
defaultMessage: 'AWS Cloudwatch logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.cloudwatchLogs.shortDescription', {
defaultMessage: 'Collect Cloudwatch logs with Functionbeat.',

View file

@ -36,6 +36,7 @@ export function cockroachdbMetricsSpecProvider(context: TutorialContext): Tutori
name: i18n.translate('home.tutorials.cockroachdbMetrics.nameTitle', {
defaultMessage: 'CockroachDB metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.cockroachdbMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the CockroachDB server.',

View file

@ -36,6 +36,7 @@ export function consulMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.consulMetrics.nameTitle', {
defaultMessage: 'Consul metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.consulMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the Consul server.',

View file

@ -37,6 +37,7 @@ export function corednsLogsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.corednsLogs.nameTitle', {
defaultMessage: 'CoreDNS logs',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.corednsLogs.shortDescription', {
defaultMessage: 'Collect the logs created by Coredns.',

View file

@ -36,6 +36,7 @@ export function corednsMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.corednsMetrics.nameTitle', {
defaultMessage: 'CoreDNS metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.corednsMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the CoreDNS server.',

View file

@ -36,6 +36,7 @@ export function couchbaseMetricsSpecProvider(context: TutorialContext): Tutorial
name: i18n.translate('home.tutorials.couchbaseMetrics.nameTitle', {
defaultMessage: 'Couchbase metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.couchbaseMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function couchdbMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.couchdbMetrics.nameTitle', {
defaultMessage: 'CouchDB metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.couchdbMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the CouchdB server.',

View file

@ -36,6 +36,7 @@ export function dockerMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.dockerMetrics.nameTitle', {
defaultMessage: 'Docker metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.dockerMetrics.shortDescription', {
defaultMessage: 'Fetch metrics about your Docker containers.',

View file

@ -36,6 +36,7 @@ export function dropwizardMetricsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.dropwizardMetrics.nameTitle', {
defaultMessage: 'Dropwizard metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.dropwizardMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function elasticsearchLogsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.elasticsearchLogs.nameTitle', {
defaultMessage: 'Elasticsearch logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
isBeta: true,
shortDescription: i18n.translate('home.tutorials.elasticsearchLogs.shortDescription', {

View file

@ -36,6 +36,7 @@ export function elasticsearchMetricsSpecProvider(context: TutorialContext): Tuto
name: i18n.translate('home.tutorials.elasticsearchMetrics.nameTitle', {
defaultMessage: 'Elasticsearch metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.elasticsearchMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function envoyproxyLogsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.envoyproxyLogs.nameTitle', {
defaultMessage: 'Envoyproxy',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.envoyproxyLogs.shortDescription', {
defaultMessage: 'Collect and parse logs received from the Envoy proxy.',

View file

@ -36,6 +36,7 @@ export function envoyproxyMetricsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.envoyproxyMetrics.nameTitle', {
defaultMessage: 'Envoy Proxy metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.envoyproxyMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from Envoy Proxy.',

View file

@ -36,6 +36,7 @@ export function etcdMetricsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.etcdMetrics.nameTitle', {
defaultMessage: 'Etcd metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.etcdMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function golangMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.golangMetrics.nameTitle', {
defaultMessage: 'Golang metrics',
}),
moduleName,
isBeta: true,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.golangMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function googlecloudMetricsSpecProvider(context: TutorialContext): Tutori
name: i18n.translate('home.tutorials.googlecloudMetrics.nameTitle', {
defaultMessage: 'Google Cloud metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.googlecloudMetrics.shortDescription', {
defaultMessage:

View file

@ -36,6 +36,7 @@ export function haproxyMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.haproxyMetrics.nameTitle', {
defaultMessage: 'HAProxy metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.haproxyMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function ibmmqLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.ibmmqLogs.nameTitle', {
defaultMessage: 'IBM MQ logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.ibmmqLogs.shortDescription', {
defaultMessage: 'Collect IBM MQ logs with Filebeat.',

View file

@ -36,6 +36,7 @@ export function ibmmqMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.ibmmqMetrics.nameTitle', {
defaultMessage: 'IBM MQ metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.ibmmqMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from IBM MQ instances.',

View file

@ -37,6 +37,7 @@ export function iisLogsSpecProvider(context: TutorialContext): TutorialSchema {
name: i18n.translate('home.tutorials.iisLogs.nameTitle', {
defaultMessage: 'IIS logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.iisLogs.shortDescription', {
defaultMessage: 'Collect and parse access and error logs created by the IIS HTTP server.',

View file

@ -36,6 +36,7 @@ export function iisMetricsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.iisMetrics.nameTitle', {
defaultMessage: 'IIS Metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.iisMetrics.shortDescription', {
defaultMessage: 'Collect IIS server related metrics.',

View file

@ -37,6 +37,7 @@ export function iptablesLogsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.iptablesLogs.nameTitle', {
defaultMessage: 'Iptables / Ubiquiti',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.iptablesLogs.shortDescription', {
defaultMessage: 'Collect and parse iptables and ip6tables logs or from Ubiqiti firewalls.',

View file

@ -37,6 +37,7 @@ export function kafkaLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.kafkaLogs.nameTitle', {
defaultMessage: 'Kafka logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.kafkaLogs.shortDescription', {
defaultMessage: 'Collect and parse logs created by Kafka.',

View file

@ -36,6 +36,7 @@ export function kafkaMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.kafkaMetrics.nameTitle', {
defaultMessage: 'Kafka metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.kafkaMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function kibanaMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.kibanaMetrics.nameTitle', {
defaultMessage: 'Kibana metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.kibanaMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function kubernetesMetricsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.kubernetesMetrics.nameTitle', {
defaultMessage: 'Kubernetes metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.kubernetesMetrics.shortDescription', {
defaultMessage: 'Fetch metrics from your Kubernetes installation.',

View file

@ -37,6 +37,7 @@ export function logstashLogsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.logstashLogs.nameTitle', {
defaultMessage: 'Logstash logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.logstashLogs.shortDescription', {
defaultMessage: 'Collect and parse debug and slow logs created by Logstash itself.',

View file

@ -36,6 +36,7 @@ export function logstashMetricsSpecProvider(context: TutorialContext): TutorialS
name: i18n.translate('home.tutorials.logstashMetrics.nameTitle', {
defaultMessage: 'Logstash metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.logstashMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function memcachedMetricsSpecProvider(context: TutorialContext): Tutorial
name: i18n.translate('home.tutorials.memcachedMetrics.nameTitle', {
defaultMessage: 'Memcached metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.memcachedMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function mongodbMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.mongodbMetrics.nameTitle', {
defaultMessage: 'MongoDB metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.mongodbMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from MongoDB.',

View file

@ -36,6 +36,7 @@ export function mssqlMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.mssqlMetrics.nameTitle', {
defaultMessage: 'Microsoft SQL Server Metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.mssqlMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from a Microsoft SQL Server instance',

View file

@ -36,6 +36,7 @@ export function muninMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.muninMetrics.nameTitle', {
defaultMessage: 'Munin metrics',
}),
moduleName,
euiIconType: '/plugins/home/assets/logos/munin.svg',
isBeta: true,
category: TutorialsCategory.METRICS,

View file

@ -37,6 +37,7 @@ export function mysqlLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.mysqlLogs.nameTitle', {
defaultMessage: 'MySQL logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.mysqlLogs.shortDescription', {
defaultMessage: 'Collect and parse error and slow logs created by MySQL.',

View file

@ -36,6 +36,7 @@ export function mysqlMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.mysqlMetrics.nameTitle', {
defaultMessage: 'MySQL metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.mysqlMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from MySQL.',

View file

@ -37,6 +37,7 @@ export function natsLogsSpecProvider(context: TutorialContext): TutorialSchema {
name: i18n.translate('home.tutorials.natsLogs.nameTitle', {
defaultMessage: 'NATS logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
isBeta: true,
shortDescription: i18n.translate('home.tutorials.natsLogs.shortDescription', {

View file

@ -36,6 +36,7 @@ export function natsMetricsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.natsMetrics.nameTitle', {
defaultMessage: 'NATS metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.natsMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the Nats server.',

View file

@ -25,9 +25,11 @@ import { createElasticCloudInstructions } from './elastic_cloud';
import { createOnPremElasticCloudInstructions } from './on_prem_elastic_cloud';
export function netflowSpecProvider() {
const moduleName = 'netflow';
return {
id: 'netflow',
name: 'Netflow',
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.netflow.tutorialShortDescription', {
defaultMessage: 'Collect Netflow records sent by a Netflow exporter.',

View file

@ -37,6 +37,7 @@ export function nginxLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.nginxLogs.nameTitle', {
defaultMessage: 'Nginx logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.nginxLogs.shortDescription', {
defaultMessage: 'Collect and parse access and error logs created by the Nginx HTTP server.',

View file

@ -36,6 +36,7 @@ export function nginxMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.nginxMetrics.nameTitle', {
defaultMessage: 'Nginx metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.nginxMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from the Nginx HTTP server.',

View file

@ -36,6 +36,7 @@ export function openmetricsMetricsSpecProvider(context: TutorialContext): Tutori
name: i18n.translate('home.tutorials.openmetricsMetrics.nameTitle', {
defaultMessage: 'OpenMetrics metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.openmetricsMetrics.shortDescription', {
defaultMessage: 'Fetch metrics from an endpoint that serves metrics in OpenMetrics format.',

View file

@ -37,6 +37,7 @@ export function osqueryLogsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.osqueryLogs.nameTitle', {
defaultMessage: 'Osquery logs',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.osqueryLogs.shortDescription', {
defaultMessage: 'Collect the result logs created by osqueryd.',

View file

@ -36,6 +36,7 @@ export function phpfpmMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.phpFpmMetrics.nameTitle', {
defaultMessage: 'PHP-FPM metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
isBeta: false,
shortDescription: i18n.translate('home.tutorials.phpFpmMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function postgresqlLogsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.postgresqlLogs.nameTitle', {
defaultMessage: 'PostgreSQL logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.postgresqlLogs.shortDescription', {
defaultMessage: 'Collect and parse error and slow logs created by PostgreSQL.',

View file

@ -36,6 +36,7 @@ export function postgresqlMetricsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.postgresqlMetrics.nameTitle', {
defaultMessage: 'PostgreSQL metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
isBeta: false,
shortDescription: i18n.translate('home.tutorials.postgresqlMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function prometheusMetricsSpecProvider(context: TutorialContext): Tutoria
name: i18n.translate('home.tutorials.prometheusMetrics.nameTitle', {
defaultMessage: 'Prometheus metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.prometheusMetrics.shortDescription', {

View file

@ -36,6 +36,7 @@ export function rabbitmqMetricsSpecProvider(context: TutorialContext): TutorialS
name: i18n.translate('home.tutorials.rabbitmqMetrics.nameTitle', {
defaultMessage: 'RabbitMQ metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.rabbitmqMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from the RabbitMQ server.',

View file

@ -37,6 +37,7 @@ export function redisLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.redisLogs.nameTitle', {
defaultMessage: 'Redis logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.redisLogs.shortDescription', {
defaultMessage: 'Collect and parse error and slow logs created by Redis.',

View file

@ -36,6 +36,7 @@ export function redisMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.redisMetrics.nameTitle', {
defaultMessage: 'Redis metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.redisMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from Redis.',

View file

@ -36,6 +36,7 @@ export function redisenterpriseMetricsSpecProvider(context: TutorialContext): Tu
name: i18n.translate('home.tutorials.redisenterpriseMetrics.nameTitle', {
defaultMessage: 'Redis Enterprise metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.redisenterpriseMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from Redis Enterprise Server.',

View file

@ -36,6 +36,7 @@ export function stanMetricsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.stanMetrics.nameTitle', {
defaultMessage: 'STAN metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.stanMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from the STAN server.',

View file

@ -33,6 +33,7 @@ export function statsdMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.statsdMetrics.nameTitle', {
defaultMessage: 'Statsd metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.statsdMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from statsd.',

View file

@ -37,6 +37,7 @@ export function suricataLogsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.suricataLogs.nameTitle', {
defaultMessage: 'Suricata logs',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.suricataLogs.shortDescription', {
defaultMessage: 'Collect the result logs created by Suricata IDS/IPS/NSM.',

View file

@ -37,6 +37,7 @@ export function systemLogsSpecProvider(context: TutorialContext): TutorialSchema
name: i18n.translate('home.tutorials.systemLogs.nameTitle', {
defaultMessage: 'System logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.systemLogs.shortDescription', {
defaultMessage: 'Collect and parse logs written by the local Syslog server.',

View file

@ -36,6 +36,7 @@ export function systemMetricsSpecProvider(context: TutorialContext): TutorialSch
name: i18n.translate('home.tutorials.systemMetrics.nameTitle', {
defaultMessage: 'System metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.systemMetrics.shortDescription', {
defaultMessage: 'Collect CPU, memory, network, and disk statistics from the host.',

View file

@ -37,6 +37,7 @@ export function traefikLogsSpecProvider(context: TutorialContext): TutorialSchem
name: i18n.translate('home.tutorials.traefikLogs.nameTitle', {
defaultMessage: 'Traefik logs',
}),
moduleName,
category: TutorialsCategory.LOGGING,
shortDescription: i18n.translate('home.tutorials.traefikLogs.shortDescription', {
defaultMessage: 'Collect and parse access logs created by the Traefik Proxy.',

View file

@ -33,6 +33,7 @@ export function traefikMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.traefikMetrics.nameTitle', {
defaultMessage: 'Traefik metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.traefikMetrics.shortDescription', {
defaultMessage: 'Fetch monitoring metrics from Traefik.',

View file

@ -30,11 +30,13 @@ import {
} from '../../services/tutorials/lib/tutorials_registry_types';
export function uptimeMonitorsSpecProvider(context: TutorialContext): TutorialSchema {
const moduleName = 'uptime';
return {
id: 'uptimeMonitors',
name: i18n.translate('home.tutorials.uptimeMonitors.nameTitle', {
defaultMessage: 'Uptime Monitors',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.uptimeMonitors.shortDescription', {
defaultMessage: 'Monitor services for their availability',

View file

@ -36,6 +36,7 @@ export function uwsgiMetricsSpecProvider(context: TutorialContext): TutorialSche
name: i18n.translate('home.tutorials.uwsgiMetrics.nameTitle', {
defaultMessage: 'uWSGI metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.uwsgiMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from the uWSGI server.',

View file

@ -36,6 +36,7 @@ export function vSphereMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.vsphereMetrics.nameTitle', {
defaultMessage: 'vSphere metrics',
}),
moduleName,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.vsphereMetrics.shortDescription', {
defaultMessage: 'Fetch internal metrics from vSphere.',

View file

@ -30,11 +30,13 @@ import {
} from '../../services/tutorials/lib/tutorials_registry_types';
export function windowsEventLogsSpecProvider(context: TutorialContext): TutorialSchema {
const moduleName = 'windows';
return {
id: 'windowsEventLogs',
name: i18n.translate('home.tutorials.windowsEventLogs.nameTitle', {
defaultMessage: 'Windows Event Log',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.windowsEventLogs.shortDescription', {

View file

@ -36,6 +36,7 @@ export function windowsMetricsSpecProvider(context: TutorialContext): TutorialSc
name: i18n.translate('home.tutorials.windowsMetrics.nameTitle', {
defaultMessage: 'Windows metrics',
}),
moduleName,
isBeta: false,
category: TutorialsCategory.METRICS,
shortDescription: i18n.translate('home.tutorials.windowsMetrics.shortDescription', {

View file

@ -37,6 +37,7 @@ export function zeekLogsSpecProvider(context: TutorialContext): TutorialSchema {
name: i18n.translate('home.tutorials.zeekLogs.nameTitle', {
defaultMessage: 'Zeek logs',
}),
moduleName,
category: TutorialsCategory.SECURITY_SOLUTION,
shortDescription: i18n.translate('home.tutorials.zeekLogs.shortDescription', {
defaultMessage: 'Collect the logs created by Zeek/Bro.',

View file

@ -36,6 +36,7 @@ export function zookeeperMetricsSpecProvider(context: TutorialContext): Tutorial
name: i18n.translate('home.tutorials.zookeeperMetrics.nameTitle', {
defaultMessage: 'Zookeeper metrics',
}),
moduleName,
euiIconType: '/plugins/home/assets/logos/zookeeper.svg',
isBeta: false,
category: TutorialsCategory.METRICS,

View file

@ -10,6 +10,7 @@ interface BaseSettings {
package_auto_upgrade?: boolean;
kibana_url?: string;
kibana_ca_sha256?: string;
has_seen_add_data_notice?: boolean;
}
export interface Settings extends BaseSettings {

View file

@ -5,6 +5,6 @@
"ui": true,
"configPath": ["xpack", "ingestManager"],
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects"],
"optionalPlugins": ["security", "features", "cloud", "usageCollection"],
"optionalPlugins": ["security", "features", "cloud", "usageCollection", "home"],
"extraPublicDirs": ["common"]
}

View file

@ -0,0 +1,7 @@
/*
* 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 { TutorialDirectoryNotice, TutorialDirectoryHeaderLink } from './tutorial_directory_notice';
export { TutorialModuleNotice } from './tutorial_module_notice';

View file

@ -0,0 +1,154 @@
/*
* 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, { memo, useState, useCallback, useEffect } from 'react';
import { BehaviorSubject } from 'rxjs';
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiButton,
EuiButtonEmpty,
EuiLink,
EuiCallOut,
EuiSpacer,
} from '@elastic/eui';
import {
TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
} from 'src/plugins/home/public';
import { sendPutSettings, useGetSettings, useLink, useCapabilities } from '../../hooks';
const FlexItemButtonWrapper = styled(EuiFlexItem)`
&&& {
margin-bottom: 0;
}
`;
const tutorialDirectoryNoticeState$ = new BehaviorSubject({
settingsDataLoaded: false,
hasSeenNotice: false,
});
export const TutorialDirectoryNotice: TutorialDirectoryNoticeComponent = memo(() => {
const { getHref } = useLink();
const { show: hasIngestManager } = useCapabilities();
const { data: settingsData, isLoading } = useGetSettings();
const [dismissedNotice, setDismissedNotice] = useState<boolean>(false);
const dismissNotice = useCallback(async () => {
setDismissedNotice(true);
await sendPutSettings({
has_seen_add_data_notice: true,
});
}, []);
useEffect(() => {
tutorialDirectoryNoticeState$.next({
settingsDataLoaded: !isLoading,
hasSeenNotice: Boolean(dismissedNotice || settingsData?.item?.has_seen_add_data_notice),
});
}, [isLoading, settingsData, dismissedNotice]);
const hasSeenNotice =
isLoading || settingsData?.item?.has_seen_add_data_notice || dismissedNotice;
return hasIngestManager && !hasSeenNotice ? (
<>
<EuiSpacer size="m" />
<EuiCallOut
iconType="cheer"
title={
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.noticeTitle"
defaultMessage="{newPrefix} Elastic Agent and Ingest Manager Beta"
values={{
newPrefix: (
<strong>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.noticeTitle.newPrefix"
defaultMessage="New:"
/>
</strong>
),
}}
/>
}
>
<p>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.noticeText"
defaultMessage="The Elastic Agent provides a simple, unified way to add monitoring for logs, metrics, and other types of data to your hosts.
You no longer need to install multiple Beats and other agents, which makes it easier and faster to deploy configurations across your infrastructure.
For more information, read our {blogPostLink}."
values={{
blogPostLink: (
<EuiLink href="https://ela.st/ingest-manager-announcement" external target="_blank">
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.noticeText.blogPostLink"
defaultMessage="announcement blog post"
/>
</EuiLink>
),
}}
/>
</p>
<EuiFlexGroup gutterSize="s">
<FlexItemButtonWrapper grow={false}>
<div>
<EuiButton size="s" href={getHref('overview')}>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.ingestManagerAppButtonText"
defaultMessage="Try Ingest Manager Beta"
/>
</EuiButton>
</div>
</FlexItemButtonWrapper>
<FlexItemButtonWrapper grow={false}>
<div>
<EuiButtonEmpty
size="s"
onClick={() => {
dismissNotice();
}}
>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.dismissNoticeButtonText"
defaultMessage="Dismiss message"
/>
</EuiButtonEmpty>
</div>
</FlexItemButtonWrapper>
</EuiFlexGroup>
</EuiCallOut>
</>
) : null;
});
export const TutorialDirectoryHeaderLink: TutorialDirectoryHeaderLinkComponent = memo(() => {
const { getHref } = useLink();
const { show: hasIngestManager } = useCapabilities();
const [noticeState, setNoticeState] = useState({
settingsDataLoaded: false,
hasSeenNotice: false,
});
useEffect(() => {
const subscription = tutorialDirectoryNoticeState$.subscribe((value) => setNoticeState(value));
return () => {
subscription.unsubscribe();
};
}, []);
return hasIngestManager && noticeState.settingsDataLoaded && noticeState.hasSeenNotice ? (
<EuiButtonEmpty size="s" iconType="link" flush="right" href={getHref('overview')}>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialDirectory.ingestManagerAppButtonText"
defaultMessage="Try Ingest Manager Beta"
/>
</EuiButtonEmpty>
) : null;
});

View file

@ -0,0 +1,74 @@
/*
* 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, { memo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiLink, EuiSpacer } from '@elastic/eui';
import { TutorialModuleNoticeComponent } from 'src/plugins/home/public';
import { useGetPackages, useLink, useCapabilities } from '../../hooks';
export const TutorialModuleNotice: TutorialModuleNoticeComponent = memo(({ moduleName }) => {
const { getHref } = useLink();
const { show: hasIngestManager } = useCapabilities();
const { data: packagesData, isLoading } = useGetPackages();
const pkgInfo =
!isLoading &&
packagesData?.response &&
packagesData.response.find((pkg) => pkg.name === moduleName);
if (hasIngestManager && pkgInfo) {
return (
<>
<EuiSpacer />
<EuiText>
<p>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialModule.noticeText"
defaultMessage="{notePrefix} a newer version of this module is {availableAsIntegrationLink} in Ingest Manager Beta.
To learn more about agent configurations and the new Elastic Agent, read our {blogPostLink}."
values={{
notePrefix: (
<strong>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialModule.noticeText.notePrefix"
defaultMessage="Note:"
/>
</strong>
),
availableAsIntegrationLink: (
<EuiLink
href={getHref('integration_details', {
pkgkey: `${pkgInfo.name}-${pkgInfo.version}`,
})}
>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialModule.noticeText.integrationLink"
defaultMessage="available as an Integration"
/>
</EuiLink>
),
blogPostLink: (
<EuiLink
href="https://ela.st/ingest-manager-announcement"
external
target="_blank"
>
<FormattedMessage
id="xpack.ingestManager.homeIntegration.tutorialModule.noticeText.blogPostLink"
defaultMessage="announcement blog post"
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
</>
);
}
return null;
});

View file

@ -22,7 +22,7 @@ import { PAGE_ROUTING_PATHS } from './constants';
import { DefaultLayout, WithoutHeaderLayout } from './layouts';
import { Loading, Error } from './components';
import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp } from './sections';
import { DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks';
import { DepsContext, ConfigContext, useConfig } from './hooks';
import { PackageInstallProvider } from './sections/epm/hooks';
import { useCore, sendSetup, sendGetPermissionsCheck } from './hooks';
import { FleetStatusProvider } from './hooks/use_fleet_status';
@ -260,7 +260,6 @@ export function renderApp(
startDeps: IngestManagerStartDeps,
config: IngestManagerConfigType
) {
setHttpClient(coreStart.http);
ReactDOM.render(
<IngestManagerApp
basepath={appBasePath}

View file

@ -13,11 +13,18 @@ import {
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { LicensingPluginSetup } from '../../licensing/public';
import { PLUGIN_ID, CheckPermissionsResponse, PostIngestSetupResponse } from '../common';
import { IngestManagerConfigType } from '../common/types';
import { setupRouteService, appRoutesService } from '../common';
import { setHttpClient } from './applications/ingest_manager/hooks';
import {
TutorialDirectoryNotice,
TutorialDirectoryHeaderLink,
TutorialModuleNotice,
} from './applications/ingest_manager/components/home_integration';
import { registerPackageConfigComponent } from './applications/ingest_manager/sections/agent_config/create_package_config_page/components/custom_package_config';
export { IngestManagerConfigType } from '../common/types';
@ -38,6 +45,7 @@ export interface IngestManagerStart {
export interface IngestManagerSetupDeps {
licensing: LicensingPluginSetup;
data: DataPublicPluginSetup;
home?: HomePublicPluginSetup;
}
export interface IngestManagerStartDeps {
@ -55,6 +63,10 @@ export class IngestManagerPlugin
public setup(core: CoreSetup, deps: IngestManagerSetupDeps) {
const config = this.config;
// Set up http client
setHttpClient(core.http);
// Register main Ingest Manager app
core.application.register({
id: PLUGIN_ID,
@ -77,6 +89,13 @@ export class IngestManagerPlugin
},
});
// Register components for home/add data integration
if (deps.home) {
deps.home.tutorials.registerDirectoryNotice(PLUGIN_ID, TutorialDirectoryNotice);
deps.home.tutorials.registerDirectoryHeaderLink(PLUGIN_ID, TutorialDirectoryHeaderLink);
deps.home.tutorials.registerModuleNotice(PLUGIN_ID, TutorialModuleNotice);
}
return {};
}

View file

@ -38,6 +38,7 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = {
package_auto_upgrade: { type: 'keyword' },
kibana_url: { type: 'keyword' },
kibana_ca_sha256: { type: 'keyword' },
has_seen_add_data_notice: { type: 'boolean', index: false },
},
},
},

View file

@ -13,5 +13,6 @@ export const PutSettingsRequestSchema = {
package_auto_upgrade: schema.maybe(schema.boolean()),
kibana_url: schema.maybe(schema.uri({ scheme: ['http', 'https'] })),
kibana_ca_sha256: schema.maybe(schema.string()),
has_seen_add_data_notice: schema.maybe(schema.boolean()),
}),
};