Merge branch 'master' into reporting/test-better

This commit is contained in:
Timothy Sullivan 2020-04-08 10:31:09 -07:00
commit 890128c47d
463 changed files with 6625 additions and 3897 deletions

View file

@ -69,26 +69,6 @@ module.exports = {
'jsx-a11y/no-onchange': 'off',
},
},
{
files: ['src/legacy/core_plugins/expressions/**/*.{js,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: [
'src/legacy/core_plugins/vis_default_editor/public/components/controls/**/*.{ts,tsx}',
],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: ['src/legacy/ui/public/vis/**/*.{js,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: ['src/plugins/es_ui_shared/**/*.{js,ts,tsx}'],
rules: {

View file

@ -43,7 +43,7 @@
"tileMap": "src/legacy/core_plugins/tile_map",
"timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion", "src/plugins/timelion"],
"uiActions": "src/plugins/ui_actions",
"visDefaultEditor": "src/legacy/core_plugins/vis_default_editor",
"visDefaultEditor": "src/plugins/vis_default_editor",
"visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown",
"visTypeMetric": "src/legacy/core_plugins/vis_type_metric",
"visTypeTable": "src/legacy/core_plugins/vis_type_table",

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

View file

@ -0,0 +1,48 @@
[[service-maps]]
=== Service maps
beta::[]
A service map is a real-time diagram of the interactions occurring in your applications architecture.
It allows you to easily visualize data flow and high-level statistics, like average transaction duration,
requests per minute, errors per minute, and metrics, allowing you to quickly assess the status of your services.
Our beta offering creates two types of service maps:
* Global: All services and connections are shown.
* Service-specific: Selecting a specific service will highlight it's connections.
[role="screenshot"]
image::apm/images/service-maps.png[Example view of service maps in the APM app in Kibana]
[float]
[[visualize-your-architecture]]
=== Visualize your architecture
Select the **Service Map** tab to get started.
By default, all services and connections are shown.
Whether your onboarding a new engineer, or just trying to grasp the big picture,
click around, zoom in and out, and begin to visualize how your services are connected.
If there's a specific service that interests you, select that service to highlight its connections.
Clicking **Focus map** will refocus the map on that specific service and lock the connection highlighting.
From here, select **Service Details**, or click on the **Transaction** tab to jump to the Transaction overview.
You can also use the tabs at the top of the page to easily jump to the **Errors** or **Metrics** overview.
While it's not possible to query in service maps, it is possible to filter by environment.
This can be useful if you have two or more services, in separate environments, but with the same name.
Use the environment drop down to only see the data you're interested in, like `dev` or `production`.
[role="screenshot"]
image::apm/images/service-maps-java.png[Example view of service maps with Java highlighted in the APM app in Kibana]
[float]
[[service-maps-legend]]
=== Legend
Nodes appear on the map in one of two shapes:
* **Circle**: Instrumented services. Interior icons are based on the language of the agent used.
* **Diamond**: Databases, external, and messaging. Interior icons represent the generic type,
with specific icons for known entities, like Elasticsearch.
Type and subtype are based on `span.type`, and `span.subtype`.

View file

@ -31,6 +31,8 @@ include::transactions.asciidoc[]
include::spans.asciidoc[]
include::service-maps.asciidoc[]
include::errors.asciidoc[]
include::metrics.asciidoc[]

View file

@ -45,10 +45,15 @@ Registering a feature consists of the following fields. For more information, co
|An array of applications this feature enables. Typically, all of your plugin's apps (from `uiExports`) will be included here.
|`privileges` (required)
|{repo}blob/{branch}/x-pack/plugins/features/server/feature.ts[`FeatureWithAllOrReadPrivileges`].
|{repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`].
|See <<example-1-canvas,Example 1>> and <<example-2-dev-tools,Example 2>>
|The set of privileges this feature requires to function.
|`subFeatures` (optional)
|{repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`].
|See <<example-3-discover,Example 3>>
|The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher.
|`icon`
|`string`
|"discoverApp"
@ -192,3 +197,78 @@ server.route({
}
});
-----------
[[example-3-discover]]
==== Example 3: Discover
Discover takes advantage of subfeature privileges to allow fine-grained access control. In this example,
a single "Create Short URLs" subfeature privilege is defined, which allows users to grant access to this feature without having to grant the `all` privilege to Discover. In other words, you can grant `read` access to Discover, and also grant the ability to create short URLs.
["source","javascript"]
-----------
init(server) {
const xpackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
{
id: 'discover',
name: i18n.translate('xpack.features.discoverFeatureName', {
defaultMessage: 'Discover',
}),
order: 100,
icon: 'discoverApp',
navLinkId: 'kibana:discover',
app: ['kibana'],
catalogue: ['discover'],
privileges: {
all: {
app: ['kibana'],
catalogue: ['discover'],
savedObject: {
all: ['search', 'query'],
read: ['index-pattern'],
},
ui: ['show', 'save', 'saveQuery'],
},
read: {
app: ['kibana'],
catalogue: ['discover'],
savedObject: {
all: [],
read: ['index-pattern', 'search', 'query'],
},
ui: ['show'],
},
},
subFeatures: [
{
name: i18n.translate('xpack.features.ossFeatures.discoverShortUrlSubFeatureName', {
defaultMessage: 'Short URLs',
}),
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'url_create',
name: i18n.translate(
'xpack.features.ossFeatures.discoverCreateShortUrlPrivilegeName',
{
defaultMessage: 'Create Short URLs',
}
),
includeIn: 'all',
savedObject: {
all: ['url'],
read: [],
},
ui: ['createShortUrl'],
},
],
},
],
},
],
}
});
}
-----------

View file

@ -43,6 +43,10 @@ Assigning a feature privilege grants access to a specific feature.
`all`:: Grants full read-write access.
`read`:: Grants read-only access.
===== Sub-feature privileges
Some features allow for finer access control than the `all` and `read` privileges.
This additional level of control is available in the Gold subscription level and higher.
===== Assigning feature privileges
From the role management screen:
@ -62,7 +66,8 @@ PUT /api/security/role/my_kibana_role
{
"base": [],
"feature": {
"dashboard": ["all"]
"visualize": ["all"],
"dashboard": ["read", "url_create"]
},
"spaces": ["marketing"]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 636 KiB

View file

@ -30,7 +30,7 @@ import {
EuiSelect,
} from '@elastic/eui';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { IIndexPattern } from 'src/plugins/data/public';
import { ControlEditor } from './control_editor';
import {

View file

@ -23,7 +23,7 @@ import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSwitchEvent } from '@elastic/eui';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
interface OptionsTabParams {
updateFiltersOnChange: boolean;

View file

@ -201,7 +201,6 @@ function createDocTableModule() {
.directive('docTable', createDocTableDirective)
.directive('kbnTableHeader', createTableHeaderDirective)
.directive('toolBarPagerText', createToolBarPagerTextDirective)
.directive('toolBarPagerText', createToolBarPagerTextDirective)
.directive('kbnTableRow', createTableRowDirective)
.directive('toolBarPagerButtons', createToolBarPagerButtonsDirective)
.directive('kbnInfiniteScroll', createInfiniteScrollDirective)

View file

@ -50,8 +50,6 @@ export const [getUrlTracker, setUrlTracker] = createGetterSetter<{
setTrackedUrl: (url: string) => void;
}>('urlTracker');
// EXPORT legacy static dependencies, should be migrated when available in a new version;
export { wrapInI18nContext } from 'ui/i18n';
import { search } from '../../../../../plugins/data/public';
import { createGetterSetter } from '../../../../../plugins/kibana_utils/common';
export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search;

View file

@ -18,7 +18,7 @@
*/
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import {
EuiButtonEmpty,
EuiFieldNumber,
@ -88,77 +88,83 @@ export function ActionBar({
};
return (
<form onSubmit={onSubmit}>
{isSuccessor && <EuiSpacer size="s" />}
{isSuccessor && showWarning && <ActionBarWarning docCount={docCountAvailable} type={type} />}
{isSuccessor && showWarning && <EuiSpacer size="s" />}
<EuiFlexGroup direction="row" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`${type}LoadMoreButton`}
iconType={isSuccessor ? 'arrowDown' : 'arrowUp'}
isDisabled={isDisabled}
isLoading={isLoading}
onClick={() => {
const value = newDocCount + defaultStepSize;
if (isValid(value)) {
setNewDocCount(value);
onChangeCount(value);
}
}}
flush="right"
>
<FormattedMessage id="kbn.context.loadButtonLabel" defaultMessage="Load" />
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow>
<EuiFieldNumber
aria-label={
isSuccessor
? i18n.translate('kbn.context.olderDocumentsAriaLabel', {
defaultMessage: 'Number of older documents',
})
: i18n.translate('kbn.context.newerDocumentsAriaLabel', {
defaultMessage: 'Number of newer documents',
})
}
className="cxtSizePicker"
data-test-subj={`${type}CountPicker`}
disabled={isDisabled}
min={MIN_CONTEXT_SIZE}
max={MAX_CONTEXT_SIZE}
onChange={ev => {
setNewDocCount(ev.target.valueAsNumber);
}}
onBlur={() => {
if (newDocCount !== docCount && isValid(newDocCount)) {
onChangeCount(newDocCount);
<I18nProvider>
<form onSubmit={onSubmit}>
{isSuccessor && <EuiSpacer size="s" />}
{isSuccessor && showWarning && (
<ActionBarWarning docCount={docCountAvailable} type={type} />
)}
{isSuccessor && showWarning && <EuiSpacer size="s" />}
<EuiFlexGroup direction="row" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`${type}LoadMoreButton`}
iconType={isSuccessor ? 'arrowDown' : 'arrowUp'}
isDisabled={isDisabled}
isLoading={isLoading}
onClick={() => {
const value = newDocCount + defaultStepSize;
if (isValid(value)) {
setNewDocCount(value);
onChangeCount(value);
}
}}
type="number"
value={newDocCount >= 0 ? newDocCount : ''}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow displayOnly>
{isSuccessor ? (
<FormattedMessage
id="kbn.context.olderDocumentsDescription"
defaultMessage="older documents"
flush="right"
>
<FormattedMessage id="kbn.context.loadButtonLabel" defaultMessage="Load" />
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow>
<EuiFieldNumber
aria-label={
isSuccessor
? i18n.translate('kbn.context.olderDocumentsAriaLabel', {
defaultMessage: 'Number of older documents',
})
: i18n.translate('kbn.context.newerDocumentsAriaLabel', {
defaultMessage: 'Number of newer documents',
})
}
className="cxtSizePicker"
data-test-subj={`${type}CountPicker`}
disabled={isDisabled}
min={MIN_CONTEXT_SIZE}
max={MAX_CONTEXT_SIZE}
onChange={ev => {
setNewDocCount(ev.target.valueAsNumber);
}}
onBlur={() => {
if (newDocCount !== docCount && isValid(newDocCount)) {
onChangeCount(newDocCount);
}
}}
type="number"
value={newDocCount >= 0 ? newDocCount : ''}
/>
) : (
<FormattedMessage
id="kbn.context.newerDocumentsDescription"
defaultMessage="newer documents"
/>
)}
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
{!isSuccessor && showWarning && <ActionBarWarning docCount={docCountAvailable} type={type} />}
{!isSuccessor && <EuiSpacer size="s" />}
</form>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow displayOnly>
{isSuccessor ? (
<FormattedMessage
id="kbn.context.olderDocumentsDescription"
defaultMessage="older documents"
/>
) : (
<FormattedMessage
id="kbn.context.newerDocumentsDescription"
defaultMessage="newer documents"
/>
)}
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
{!isSuccessor && showWarning && (
<ActionBarWarning docCount={docCountAvailable} type={type} />
)}
{!isSuccessor && <EuiSpacer size="s" />}
</form>
</I18nProvider>
);
}

View file

@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import { getAngularModule, wrapInI18nContext } from '../../../../../kibana_services';
import { getAngularModule } from '../../../../../kibana_services';
import { ActionBar } from './action_bar';
getAngularModule().directive('contextActionBar', function(reactDirective: any) {
return reactDirective(wrapInI18nContext(ActionBar));
return reactDirective(ActionBar);
});

View file

@ -20,16 +20,12 @@
import { DiscoverNoResults } from './no_results';
import { DiscoverUninitialized } from './uninitialized';
import { DiscoverHistogram } from './histogram';
import { getAngularModule, wrapInI18nContext } from '../../../kibana_services';
import { getAngularModule } from '../../../kibana_services';
const app = getAngularModule();
app.directive('discoverNoResults', reactDirective =>
reactDirective(wrapInI18nContext(DiscoverNoResults))
);
app.directive('discoverNoResults', reactDirective => reactDirective(DiscoverNoResults));
app.directive('discoverUninitialized', reactDirective =>
reactDirective(wrapInI18nContext(DiscoverUninitialized))
);
app.directive('discoverUninitialized', reactDirective => reactDirective(DiscoverUninitialized));
app.directive('discoverHistogram', reactDirective => reactDirective(DiscoverHistogram));

View file

@ -18,7 +18,7 @@
*/
import React, { Component, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import PropTypes from 'prop-types';
import {
@ -247,29 +247,31 @@ export class DiscoverNoResults extends Component {
}
return (
<Fragment>
<EuiSpacer size="xl" />
<I18nProvider>
<Fragment>
<EuiSpacer size="xl" />
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false} className="dscNoResults">
<EuiCallOut
title={
<FormattedMessage
id="kbn.discover.noResults.searchExamples.noResultsMatchSearchCriteriaTitle"
defaultMessage="No results match your search criteria"
/>
}
color="warning"
iconType="help"
data-test-subj="discoverNoResults"
/>
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false} className="dscNoResults">
<EuiCallOut
title={
<FormattedMessage
id="kbn.discover.noResults.searchExamples.noResultsMatchSearchCriteriaTitle"
defaultMessage="No results match your search criteria"
/>
}
color="warning"
iconType="help"
data-test-subj="discoverNoResults"
/>
{shardFailuresMessage}
{timeFieldMessage}
{luceneQueryMessage}
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
{shardFailuresMessage}
{timeFieldMessage}
{luceneQueryMessage}
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
</I18nProvider>
);
}
}

View file

@ -18,7 +18,7 @@
*/
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { EuiButton, EuiEmptyPrompt, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';
@ -28,38 +28,40 @@ interface Props {
export const DiscoverUninitialized = ({ onRefresh }: Props) => {
return (
<EuiPage>
<EuiPageBody>
<EuiPageContent horizontalPosition="center">
<EuiEmptyPrompt
iconType="discoverApp"
title={
<h2>
<FormattedMessage
id="kbn.discover.uninitializedTitle"
defaultMessage="Start searching"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="kbn.discover.uninitializedText"
defaultMessage="Write a query, add some filters, or simply hit Refresh to retrieve results for the current query."
/>
</p>
}
actions={
<EuiButton color="primary" fill onClick={onRefresh}>
<FormattedMessage
id="kbn.discover.uninitializedRefreshButtonText"
defaultMessage="Refresh data"
/>
</EuiButton>
}
/>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
<I18nProvider>
<EuiPage>
<EuiPageBody>
<EuiPageContent horizontalPosition="center">
<EuiEmptyPrompt
iconType="discoverApp"
title={
<h2>
<FormattedMessage
id="kbn.discover.uninitializedTitle"
defaultMessage="Start searching"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="kbn.discover.uninitializedText"
defaultMessage="Write a query, add some filters, or simply hit Refresh to retrieve results for the current query."
/>
</p>
}
actions={
<EuiButton color="primary" fill onClick={onRefresh}>
<FormattedMessage
id="kbn.discover.uninitializedRefreshButtonText"
defaultMessage="Refresh data"
/>
</EuiButton>
}
/>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</I18nProvider>
);
};

View file

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { getAngularModule, wrapInI18nContext, getServices } from '../../kibana_services';
import { getAngularModule, getServices } from '../../kibana_services';
// @ts-ignore
import { getRootBreadcrumbs } from '../helpers/breadcrumbs';
import html from './doc.html';
@ -30,7 +30,7 @@ const { timefilter } = getServices();
const app = getAngularModule();
app.directive('discoverDoc', function(reactDirective: any) {
return reactDirective(
wrapInI18nContext(Doc),
Doc,
[
['id', { watchDepth: 'value' }],
['index', { watchDepth: 'value' }],

View file

@ -16,14 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import { wrapInI18nContext } from '../../../../../kibana_services';
import { ToolBarPagerText } from './tool_bar_pager_text';
import { ToolBarPagerButtons } from './tool_bar_pager_buttons';
export function createToolBarPagerTextDirective(reactDirective: any) {
return reactDirective(wrapInI18nContext(ToolBarPagerText));
return reactDirective(ToolBarPagerText);
}
export function createToolBarPagerButtonsDirective(reactDirective: any) {
return reactDirective(wrapInI18nContext(ToolBarPagerButtons));
return reactDirective(ToolBarPagerButtons);
}

View file

@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
interface Props {
startItem: number;
@ -27,12 +27,14 @@ interface Props {
export function ToolBarPagerText({ startItem, endItem, totalItems }: Props) {
return (
<div className="kuiToolBarText" data-test-subj="toolBarPagerText">
<FormattedMessage
id="kbn.docTable.pagerControl.pagesCountLabel"
defaultMessage="{startItem}&ndash;{endItem} of {totalItems}"
values={{ startItem, endItem, totalItems }}
/>
</div>
<I18nProvider>
<div className="kuiToolBarText" data-test-subj="toolBarPagerText">
<FormattedMessage
id="kbn.docTable.pagerControl.pagesCountLabel"
defaultMessage="{startItem}&ndash;{endItem} of {totalItems}"
values={{ startItem, endItem, totalItems }}
/>
</div>
</I18nProvider>
);
}

View file

@ -17,13 +17,13 @@
* under the License.
*/
import { TableHeader } from './table_header/table_header';
import { wrapInI18nContext, getServices } from '../../../../kibana_services';
import { getServices } from '../../../../kibana_services';
export function createTableHeaderDirective(reactDirective: any) {
const { uiSettings: config } = getServices();
return reactDirective(
wrapInI18nContext(TableHeader),
TableHeader,
[
['columns', { watchDepth: 'collection' }],
['hideTimeColumn', { watchDepth: 'value' }],

View file

@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent } from '@elastic/eui';
import { IndexPatternsContract } from 'src/plugins/data/public';
import { ElasticRequestState, useEsDocSearch } from './use_es_doc_search';
@ -65,83 +65,85 @@ export function Doc(props: DocProps) {
const [reqState, hit, indexPattern] = useEsDocSearch(props);
return (
<EuiPageContent>
{reqState === ElasticRequestState.NotFoundIndexPattern && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-notFoundIndexPattern`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToLocateIndexPattern"
defaultMessage="No index pattern matches ID {indexPatternId}"
values={{ indexPatternId: props.indexPatternId }}
/>
}
/>
)}
{reqState === ElasticRequestState.NotFound && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-notFound`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToLocateDocumentDescription"
defaultMessage="Cannot find document"
/>
}
>
<FormattedMessage
id="kbn.doc.couldNotFindDocumentsDescription"
defaultMessage="No documents match that ID."
<I18nProvider>
<EuiPageContent>
{reqState === ElasticRequestState.NotFoundIndexPattern && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-notFoundIndexPattern`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToLocateIndexPattern"
defaultMessage="No index pattern matches ID {indexPatternId}"
values={{ indexPatternId: props.indexPatternId }}
/>
}
/>
</EuiCallOut>
)}
{reqState === ElasticRequestState.Error && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-error`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToExecuteQueryDescription"
defaultMessage="Cannot run search"
/>
}
>
<FormattedMessage
id="kbn.doc.somethingWentWrongDescription"
defaultMessage="{indexName} is missing."
values={{ indexName: props.index }}
/>{' '}
<EuiLink
href={`https://www.elastic.co/guide/en/elasticsearch/reference/${
getServices().metadata.branch
}/indices-exists.html`}
target="_blank"
)}
{reqState === ElasticRequestState.NotFound && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-notFound`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToLocateDocumentDescription"
defaultMessage="Cannot find document"
/>
}
>
<FormattedMessage
id="kbn.doc.somethingWentWrongDescriptionAddon"
defaultMessage="Please ensure the index exists."
id="kbn.doc.couldNotFindDocumentsDescription"
defaultMessage="No documents match that ID."
/>
</EuiLink>
</EuiCallOut>
)}
</EuiCallOut>
)}
{reqState === ElasticRequestState.Loading && (
<EuiCallOut data-test-subj={`doc-msg-loading`}>
<EuiLoadingSpinner size="m" />{' '}
<FormattedMessage id="kbn.doc.loadingDescription" defaultMessage="Loading…" />
</EuiCallOut>
)}
{reqState === ElasticRequestState.Error && (
<EuiCallOut
color="danger"
data-test-subj={`doc-msg-error`}
iconType="alert"
title={
<FormattedMessage
id="kbn.doc.failedToExecuteQueryDescription"
defaultMessage="Cannot run search"
/>
}
>
<FormattedMessage
id="kbn.doc.somethingWentWrongDescription"
defaultMessage="{indexName} is missing."
values={{ indexName: props.index }}
/>{' '}
<EuiLink
href={`https://www.elastic.co/guide/en/elasticsearch/reference/${
getServices().metadata.branch
}/indices-exists.html`}
target="_blank"
>
<FormattedMessage
id="kbn.doc.somethingWentWrongDescriptionAddon"
defaultMessage="Please ensure the index exists."
/>
</EuiLink>
</EuiCallOut>
)}
{reqState === ElasticRequestState.Found && hit !== null && indexPattern && (
<div data-test-subj="doc-hit">
<DocViewer hit={hit} indexPattern={indexPattern} />
</div>
)}
</EuiPageContent>
{reqState === ElasticRequestState.Loading && (
<EuiCallOut data-test-subj={`doc-msg-loading`}>
<EuiLoadingSpinner size="m" />{' '}
<FormattedMessage id="kbn.doc.loadingDescription" defaultMessage="Loading…" />
</EuiCallOut>
)}
{reqState === ElasticRequestState.Found && hit !== null && indexPattern && (
<div data-test-subj="doc-hit">
<DocViewer hit={hit} indexPattern={indexPattern} />
</div>
)}
</EuiPageContent>
</I18nProvider>
);
}

View file

@ -17,9 +17,9 @@
* under the License.
*/
import React, { Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui';
import { getAngularModule, wrapInI18nContext, getServices } from '../../../kibana_services';
import { getAngularModule, getServices } from '../../../kibana_services';
interface Props {
fetchError: {
@ -72,26 +72,28 @@ const DiscoverFetchError = ({ fetchError }: Props) => {
}
return (
<Fragment>
<EuiSpacer size="xl" />
<I18nProvider>
<Fragment>
<EuiSpacer size="xl" />
<EuiFlexGroup justifyContent="center" data-test-subj="discoverFetchError">
<EuiFlexItem grow={false} className="discoverFetchError">
<EuiCallOut title={fetchError.message} color="danger" iconType="cross">
{body}
<EuiFlexGroup justifyContent="center" data-test-subj="discoverFetchError">
<EuiFlexItem grow={false} className="discoverFetchError">
<EuiCallOut title={fetchError.message} color="danger" iconType="cross">
{body}
<EuiCodeBlock>{fetchError.error}</EuiCodeBlock>
</EuiCallOut>
</EuiFlexItem>
</EuiFlexGroup>
<EuiCodeBlock>{fetchError.error}</EuiCodeBlock>
</EuiCallOut>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
</Fragment>
<EuiSpacer size="xl" />
</Fragment>
</I18nProvider>
);
};
export function createFetchErrorDirective(reactDirective: any) {
return reactDirective(wrapInI18nContext(DiscoverFetchError));
return reactDirective(DiscoverFetchError);
}
getAngularModule().directive('discoverFetchError', createFetchErrorDirective);

View file

@ -20,7 +20,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButtonIcon, EuiTitle } from '@elastic/eui';
import { sortBy } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { DiscoverField } from './discover_field';
import { DiscoverIndexPattern } from './discover_index_pattern';
import { DiscoverFieldSearch } from './discover_field_search';
@ -162,165 +162,175 @@ export function DiscoverSidebar({
}
return (
<section
className="sidebar-list"
aria-label={i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel',
{
defaultMessage: 'Index and fields',
}
)}
>
<DiscoverIndexPattern
selectedIndexPattern={selectedIndexPattern}
setIndexPattern={setIndexPattern}
indexPatternList={sortBy(indexPatternList, o => o.attributes.title)}
/>
<div className="dscSidebar__item">
<form>
<DiscoverFieldSearch
onChange={onChangeFieldSearch}
value={fieldFilterState.name}
types={fieldTypes}
/>
</form>
</div>
<div className="sidebar-list">
{fields.length > 0 && (
<>
<EuiTitle size="xxxs" id="selected_fields">
<h3>
<FormattedMessage
id="kbn.discover.fieldChooser.filter.selectedFieldsTitle"
defaultMessage="Selected fields"
/>
</h3>
</EuiTitle>
<ul
className="dscSidebarList dscFieldList--selected"
aria-labelledby="selected_fields"
data-test-subj={`fieldList-selected`}
>
{selectedFields.map((field: IndexPatternField, idx: number) => {
return (
<li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item">
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
selected={true}
useShortDots={useShortDots}
/>
</li>
);
})}
</ul>
<div className="euiFlexGroup euiFlexGroup--gutterMedium">
<EuiTitle size="xxxs" id="available_fields" className="euiFlexItem">
<I18nProvider>
<section
className="sidebar-list"
aria-label={i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel',
{
defaultMessage: 'Index and fields',
}
)}
>
<DiscoverIndexPattern
selectedIndexPattern={selectedIndexPattern}
setIndexPattern={setIndexPattern}
indexPatternList={sortBy(indexPatternList, o => o.attributes.title)}
/>
<div className="dscSidebar__item">
<form>
<DiscoverFieldSearch
onChange={onChangeFieldSearch}
value={fieldFilterState.name}
types={fieldTypes}
/>
</form>
</div>
<div className="sidebar-list">
{fields.length > 0 && (
<>
<EuiTitle size="xxxs" id="selected_fields">
<h3>
<FormattedMessage
id="kbn.discover.fieldChooser.filter.availableFieldsTitle"
defaultMessage="Available fields"
id="kbn.discover.fieldChooser.filter.selectedFieldsTitle"
defaultMessage="Selected fields"
/>
</h3>
</EuiTitle>
<div className="euiFlexItem euiFlexItem--flexGrowZero">
<EuiButtonIcon
className={'visible-xs visible-sm dscFieldChooser__toggle'}
iconType={showFields ? 'arrowDown' : 'arrowRight'}
onClick={() => setShowFields(!showFields)}
aria-label={
showFields
? i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel',
{
defaultMessage: 'Hide fields',
}
)
: i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel',
{
defaultMessage: 'Show fields',
}
)
}
/>
</div>
</div>
</>
)}
{popularFields.length > 0 && (
<div>
<EuiTitle
size="xxxs"
className={`dscFieldListHeader ${!showFields ? 'hidden-sm hidden-xs' : ''}`}
>
<h4 style={{ fontWeight: 'normal' }} id="available_fields_popular">
<FormattedMessage
id="kbn.discover.fieldChooser.filter.popularTitle"
defaultMessage="Popular"
/>
</h4>
</EuiTitle>
<ul
className={`dscFieldList dscFieldList--popular ${
!showFields ? 'hidden-sm hidden-xs' : ''
}`}
aria-labelledby="available_fields available_fields_popular"
data-test-subj={`fieldList-popular`}
>
{popularFields.map((field: IndexPatternField, idx: number) => {
return (
<li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item">
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
useShortDots={useShortDots}
<ul
className="dscSidebarList dscFieldList--selected"
aria-labelledby="selected_fields"
data-test-subj={`fieldList-selected`}
>
{selectedFields.map((field: IndexPatternField, idx: number) => {
return (
<li
key={`field${idx}`}
data-attr-field={field.name}
className="dscSidebar__item"
>
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
selected={true}
useShortDots={useShortDots}
/>
</li>
);
})}
</ul>
<div className="euiFlexGroup euiFlexGroup--gutterMedium">
<EuiTitle size="xxxs" id="available_fields" className="euiFlexItem">
<h3>
<FormattedMessage
id="kbn.discover.fieldChooser.filter.availableFieldsTitle"
defaultMessage="Available fields"
/>
</li>
);
})}
</ul>
</div>
)}
</h3>
</EuiTitle>
<div className="euiFlexItem euiFlexItem--flexGrowZero">
<EuiButtonIcon
className={'visible-xs visible-sm dscFieldChooser__toggle'}
iconType={showFields ? 'arrowDown' : 'arrowRight'}
onClick={() => setShowFields(!showFields)}
aria-label={
showFields
? i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel',
{
defaultMessage: 'Hide fields',
}
)
: i18n.translate(
'kbn.discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel',
{
defaultMessage: 'Show fields',
}
)
}
/>
</div>
</div>
</>
)}
{popularFields.length > 0 && (
<div>
<EuiTitle
size="xxxs"
className={`dscFieldListHeader ${!showFields ? 'hidden-sm hidden-xs' : ''}`}
>
<h4 style={{ fontWeight: 'normal' }} id="available_fields_popular">
<FormattedMessage
id="kbn.discover.fieldChooser.filter.popularTitle"
defaultMessage="Popular"
/>
</h4>
</EuiTitle>
<ul
className={`dscFieldList dscFieldList--popular ${
!showFields ? 'hidden-sm hidden-xs' : ''
}`}
aria-labelledby="available_fields available_fields_popular"
data-test-subj={`fieldList-popular`}
>
{popularFields.map((field: IndexPatternField, idx: number) => {
return (
<li
key={`field${idx}`}
data-attr-field={field.name}
className="dscSidebar__item"
>
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
useShortDots={useShortDots}
/>
</li>
);
})}
</ul>
</div>
)}
<ul
className={`dscFieldList dscFieldList--unpopular ${
!showFields ? 'hidden-sm hidden-xs' : ''
}`}
aria-labelledby="available_fields"
data-test-subj={`fieldList-unpopular`}
>
{unpopularFields.map((field: IndexPatternField, idx: number) => {
return (
<li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item">
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
useShortDots={useShortDots}
/>
</li>
);
})}
</ul>
</div>
</section>
<ul
className={`dscFieldList dscFieldList--unpopular ${
!showFields ? 'hidden-sm hidden-xs' : ''
}`}
aria-labelledby="available_fields"
data-test-subj={`fieldList-unpopular`}
>
{unpopularFields.map((field: IndexPatternField, idx: number) => {
return (
<li key={`field${idx}`} data-attr-field={field.name} className="dscSidebar__item">
<DiscoverField
field={field}
indexPattern={selectedIndexPattern}
onAddField={onAddField}
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
onShowDetails={onShowDetails}
getDetails={getDetailsByField}
showDetails={openFieldMap.get(field.name) || false}
useShortDots={useShortDots}
/>
</li>
);
})}
</ul>
</div>
</section>
</I18nProvider>
);
}

View file

@ -16,11 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import { wrapInI18nContext } from '../../../kibana_services';
import { DiscoverSidebar } from './discover_sidebar';
export function createDiscoverSidebarDirective(reactDirective: any) {
return reactDirective(wrapInI18nContext(DiscoverSidebar), [
return reactDirective(DiscoverSidebar, [
['columns', { watchDepth: 'reference' }],
['fieldCounts', { watchDepth: 'reference' }],
['hits', { watchDepth: 'reference' }],

View file

@ -1,2 +1,5 @@
// Visualize plugin styles
@import 'np_ready/index';
// should be removed while moving the visualize into NP
@import '../../../../../plugins/vis_default_editor/public/index'

View file

@ -36,7 +36,7 @@ import { VisualizationsStart } from '../../../../../plugins/visualizations/publi
import { SavedVisualizations } from './np_ready/types';
import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public';
import { DefaultEditorController } from '../../../vis_default_editor/public';
import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public';
export interface VisualizeKibanaServices {
pluginInitializerContext: PluginInitializerContext;

View file

@ -51,7 +51,7 @@ import {
HomePublicPluginSetup,
} from '../../../../../plugins/home/public';
import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
import { DefaultEditorController } from '../../../vis_default_editor/public';
import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public';
export interface VisualizePluginStartDependencies {
data: DataPublicPluginStart;

View file

@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { FileLayerField, VectorLayer, ServiceSettings } from 'ui/vis/map/service_settings';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { NumberInputOption, SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { WmsOptions } from '../../../tile_map/public/components/wms_options';
import { RegionMapVisParams } from '../types';

View file

@ -22,7 +22,7 @@ import { mapToLayerWithId } from './util';
import { createRegionMapVisualization } from './region_map_visualization';
import { RegionMapOptions } from './components/region_map_options';
import { truncatedColorSchemas } from '../../../../plugins/charts/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
// TODO: reference to TILE_MAP plugin should be removed
import { ORIGIN } from '../../tile_map/common/origin';

View file

@ -164,7 +164,8 @@ export function createRegionMapVisualization({ serviceSettings, $injector, uiSet
}
this._choroplethLayer.on('select', event => {
const rowIndex = this._chartData.rows.findIndex(row => row[0] === event);
const { rows, columns } = this._chartData;
const rowIndex = rows.findIndex(row => row[columns[0].id] === event);
this._vis.API.events.filter({
table: this._chartData,
column: 0,

View file

@ -21,7 +21,7 @@ import React, { useEffect } from 'react';
import { EuiPanel, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import {
BasicOptions,
RangeOption,

View file

@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import { createTileMapVisualization } from './tile_map_visualization';
import { TileMapOptions } from './components/tile_map_options';
import { MapTypes } from './map_types';

View file

@ -1,42 +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 { resolve } from 'path';
import { Legacy } from 'kibana';
import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types';
const vidDefaultEditorPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
new Plugin({
id: 'vis_default_editor',
require: [],
publicDir: resolve(__dirname, 'public'),
uiExports: {
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
},
init: (server: Legacy.Server) => ({}),
config(Joi: any) {
return Joi.object({
enabled: Joi.boolean().default(true),
}).default();
},
} as Legacy.PluginSpecOptions);
// eslint-disable-next-line import/no-default-export
export default vidDefaultEditorPluginInitializer;

View file

@ -1,4 +0,0 @@
{
"name": "vis_default_editor",
"version": "kibana"
}

View file

@ -30,7 +30,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { MarkdownVisParams } from './types';
function MarkdownOptions({ stateParams, setValue }: VisOptionsProps<MarkdownVisParams>) {

View file

@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { MarkdownVisWrapper } from './markdown_vis_controller';
import { MarkdownOptions } from './markdown_options';
import { SettingsOptions } from './settings_options';
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
export const markdownVisDefinition = {
name: 'markdown',

View file

@ -21,7 +21,7 @@ import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { RangeOption, SwitchOption } from '../../vis_type_vislib/public';
import { MarkdownVisParams } from './types';

View file

@ -29,7 +29,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import {
ColorModes,
ColorRanges,

View file

@ -23,10 +23,6 @@ import { functionWrapper } from '../../../../plugins/expressions/common/expressi
jest.mock('ui/new_platform');
jest.mock('../../vis_default_editor/public', () => ({
Schemas: class {},
}));
describe('interpreter/functions#metric', () => {
const fn = functionWrapper(createMetricVisFn());
const context = {

View file

@ -22,10 +22,6 @@ import { MetricVisComponent } from './components/metric_vis_component';
jest.mock('ui/new_platform');
jest.mock('../../vis_default_editor/public', () => ({
Schemas: class {},
}));
describe('metric_vis - createMetricVisTypeDefinition', () => {
it('has metric vis component set', () => {
const def = createMetricVisTypeDefinition();

View file

@ -24,7 +24,7 @@ import { MetricVisOptions } from './components/metric_vis_options';
import { ColorModes } from '../../vis_type_vislib/public';
import { ColorSchemas, colorSchemas } from '../../../../plugins/charts/public';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
export const createMetricVisTypeDefinition = () => ({
name: 'metric',

View file

@ -23,7 +23,7 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { search } from '../../../../../plugins/data/public';
import { NumberInputOption, SwitchOption, SelectOption } from '../../../vis_type_vislib/public';
import { TableVisParams } from '../types';

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import { Vis } from '../../../../plugins/visualizations/public';
import { tableVisResponseHandler } from './table_vis_response_handler';
// @ts-ignore

View file

@ -20,8 +20,8 @@
import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { ValidatedDualRange } from '../../../../../../src/plugins/kibana_react/public';
import { VisOptionsProps } from '../../../vis_default_editor/public';
import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { TagCloudVisParams } from '../types';

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import { TagCloudOptions } from './components/tag_cloud_options';

View file

@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
import { search } from '../../../../../plugins/data/public';
const { isValidEsInterval } = search.aggs;
import { useValidation } from '../../../vis_default_editor/public';
import { useValidation } from '../../../../../plugins/vis_default_editor/public';
const intervalOptions = [
{

View file

@ -20,9 +20,9 @@
import React, { useCallback } from 'react';
import { EuiPanel } from '@elastic/eui';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { VisParams } from './timelion_vis_fn';
import { TimelionInterval, TimelionExpressionInput } from './components';
import { VisOptionsProps } from '../../vis_default_editor/public';
function TimelionOptions({ stateParams, setValue, setValidity }: VisOptionsProps<VisParams>) {
const setInterval = useCallback((value: VisParams['interval']) => setValue('interval', value), [

View file

@ -21,7 +21,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { KibanaContextProvider } from '../../../../plugins/kibana_react/public';
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
import { getTimelionRequestHandler } from './helpers/timelion_request_handler';
import { TimelionVisComponent, TimelionVisComponentProp } from './components';
import { TimelionOptions } from './timelion_options';

View file

@ -24,11 +24,11 @@ import compactStringify from 'json-stringify-pretty-compact';
import hjson from 'hjson';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { getNotifications } from '../services';
import { VisParams } from '../vega_fn';
import { VegaHelpMenu } from './vega_help_menu';
import { VegaActionsMenu } from './vega_actions_menu';
import { VisOptionsProps } from '../../../vis_default_editor/public';
const aceOptions = {
maxLines: Infinity,

View file

@ -18,8 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin';
import { VegaVisEditor } from './components';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common';

View file

@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services';
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import {
Positions,
ChartTypes,

View file

@ -20,7 +20,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from '../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { SwitchOption } from './switch';
import { SelectOption } from './select';

View file

@ -22,7 +22,10 @@ import { last } from 'lodash';
import { i18n } from '@kbn/i18n';
import { RangeValues, RangesParamEditor } from '../../../../vis_default_editor/public';
import {
RangeValues,
RangesParamEditor,
} from '../../../../../../plugins/vis_default_editor/public';
export type SetColorRangeValue = (paramName: string, value: RangeValues[]) => void;

View file

@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { SelectOption } from './select';
import { SwitchOption } from './switch';
import { ColorSchemaVislibParams } from '../../types';

View file

@ -19,7 +19,7 @@
import React, { useEffect, useState, useCallback } from 'react';
import { VisOptionsProps } from '../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
export interface ValidationVisOptionsProps<T> extends VisOptionsProps<T> {
setMultipleValidity(paramName: string, isValid: boolean): void;

View file

@ -20,7 +20,7 @@
import React, { useCallback } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { GaugeVisParams } from '../../../gauge';
import { RangesPanel } from './ranges_panel';
import { StylePanel } from './style_panel';

View file

@ -23,7 +23,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import {
BasicOptions,
ColorRanges,

View file

@ -23,7 +23,7 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { ValueAxis } from '../../../types';
import { HeatmapVisParams } from '../../../heatmap';
import { SwitchOption } from '../../common';

View file

@ -54,6 +54,7 @@ exports[`MetricsAxisOptions component should init with the default set of props
}
vis={
Object {
"serialize": [MockFunction],
"setState": [MockFunction],
"type": Object {
"schemas": Object {
@ -126,6 +127,7 @@ exports[`MetricsAxisOptions component should init with the default set of props
}
vis={
Object {
"serialize": [MockFunction],
"setState": [MockFunction],
"type": Object {
"schemas": Object {
@ -169,6 +171,7 @@ exports[`MetricsAxisOptions component should init with the default set of props
setCategoryAxis={[Function]}
vis={
Object {
"serialize": [MockFunction],
"setState": [MockFunction],
"type": Object {
"schemas": Object {

View file

@ -23,7 +23,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { Axis } from '../../../types';
import { SelectOption, SwitchOption } from '../../common';
import { LabelOptions, SetAxisLabel } from './label_options';

View file

@ -95,6 +95,7 @@ describe('MetricsAxisOptions component', () => {
schemas: { metrics: [{ name: 'metric' }] },
},
setState: jest.fn(),
serialize: jest.fn(),
},
stateParams: {
valueAxes: [axis],

View file

@ -299,7 +299,7 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps<BasicVislibParams>)
}, [stateParams.seriesParams]);
useEffect(() => {
vis.setState({ type: visType } as any);
vis.setState({ ...vis.serialize(), type: visType });
}, [vis, visType]);
return isTabSelected ? (

View file

@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { BasicOptions, TruncateLabelsOption, SwitchOption } from '../common';
import { PieVisParams } from '../../pie';

View file

@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { SelectOption, SwitchOption } from '../../common';
import { BasicVislibParams, ValueAxis } from '../../../types';

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { RangeValues, Schemas } from '../../vis_default_editor/public';
import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public';
import { AggGroupNames } from '../../../../plugins/data/public';
import { GaugeOptions } from './components/options';
import { getGaugeCollections, Alignments, ColorModes, GaugeTypes } from './utils/collections';

View file

@ -25,7 +25,7 @@ import { createVislibVisController } from './vis_controller';
import { VisTypeVislibDependencies } from './plugin';
import { ColorSchemas } from '../../../../plugins/charts/public';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
export const createGoalVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({
name: 'goal',

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { RangeValues, Schemas } from '../../vis_default_editor/public';
import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public';
import { AggGroupNames } from '../../../../plugins/data/public';
import { AxisTypes, getHeatmapCollections, Positions, ScaleTypes } from './utils/collections';
import { HeatmapOptions } from './components/options';

View file

@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services';
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import {
Positions,
ChartTypes,

View file

@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services';
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import {
Positions,
ChartTypes,

View file

@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services';
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import {
Positions,
ChartTypes,

View file

@ -20,7 +20,7 @@
import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../../../plugins/data/public';
import { Schemas } from '../../vis_default_editor/public';
import { Schemas } from '../../../../plugins/vis_default_editor/public';
import { PieOptions } from './components/options';
import { getPositions, Positions } from './utils/collections';
import { createVislibVisController } from './vis_controller';

View file

@ -20,7 +20,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from '../../../vis_default_editor/public';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { PointSeriesOptions, MetricsAxisOptions } from '../components/options';
import { ValidationWrapper } from '../components/common';
import { BasicVislibParams } from '../types';

View file

@ -1,56 +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 { KIBANA_STATS_TYPE } from '../constants';
import { getKibanaInfoForStats } from '../lib';
/*
* Initialize a collector for Kibana Ops Stats
*
* NOTE this collector's fetch method returns the latest stats from the
* Hapi/Good/Even-Better ops event listener. Therefore, the stats reset
* every 5 seconds (the default value of the ops.interval configuration
* setting). That makes it geared for providing the latest "real-time"
* stats. In the long-term, fetch should return stats that constantly
* accumulate over the server's uptime for better machine readability.
* Since the data is captured, timestamped and stored, the historical
* data can provide "real-time" stats by calculating a derivative of
* the metrics.
* See PR comment in https://github.com/elastic/kibana/pull/20577/files#r202416647
*/
export function getOpsStatsCollector(usageCollection, server, kbnServer) {
return usageCollection.makeStatsCollector({
type: KIBANA_STATS_TYPE,
fetch: () => {
return {
kibana: getKibanaInfoForStats(server, kbnServer),
...kbnServer.metrics, // latest metrics captured from the ops event listener in src/legacy/server/status/index
};
},
isReady: () => true,
ignoreForInternalUploader: true, // Ignore this one from internal uploader. A different stats collector is used there.
});
}
export function registerOpsStatsCollector(usageCollection, server, kbnServer) {
if (usageCollection) {
const collector = getOpsStatsCollector(usageCollection, server, kbnServer);
usageCollection.registerCollector(collector);
}
}

View file

@ -20,7 +20,6 @@
import ServerStatus from './server_status';
import { Metrics } from './lib/metrics';
import { registerStatusPage, registerStatusApi, registerStatsApi } from './routes';
import { registerOpsStatsCollector } from './collectors';
import Oppsy from 'oppsy';
import { cloneDeep } from 'lodash';
import { getOSInfo } from './lib/get_os_info';
@ -28,7 +27,6 @@ import { getOSInfo } from './lib/get_os_info';
export function statusMixin(kbnServer, server, config) {
kbnServer.status = new ServerStatus(kbnServer.server);
const { usageCollection } = server.newPlatform.setup.plugins;
registerOpsStatsCollector(usageCollection, server, kbnServer);
const metrics = new Metrics(config, server);

View file

@ -20,8 +20,6 @@ import React from 'react';
import { ErrorEmbeddable } from './error_embeddable';
import { EmbeddableRoot } from './embeddable_root';
import { mount } from 'enzyme';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
test('ErrorEmbeddable renders an embeddable', async () => {
const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' });

View file

@ -80,3 +80,14 @@ export const APPLICATION_USAGE_TYPE = 'application_usage';
* The type name used within the Monitoring index to publish management stats.
*/
export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management';
/**
* The type name used to publish Kibana usage stats.
* NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats
*/
export const KIBANA_USAGE_TYPE = 'kibana';
/**
* The type name used to publish Kibana usage stats in the formatted as bulk.
*/
export const KIBANA_STATS_TYPE = 'kibana_stats';

View file

@ -22,3 +22,5 @@ export { registerUiMetricUsageCollector } from './ui_metric';
export { registerTelemetryPluginUsageCollector } from './telemetry_plugin';
export { registerManagementUsageCollector } from './management';
export { registerApplicationUsageCollector } from './application_usage';
export { registerKibanaUsageCollector } from './kibana';
export { registerOpsStatsCollector } from './ops_stats';

View file

@ -0,0 +1,61 @@
/*
* 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 { getSavedObjectsCounts } from './get_saved_object_counts';
describe('getSavedObjectsCounts', () => {
test('Get all the saved objects equal to 0 because no results were found', async () => {
const callCluster = jest.fn(() => ({}));
const results = await getSavedObjectsCounts(callCluster as any, '.kibana');
expect(results).toStrictEqual({
dashboard: { total: 0 },
visualization: { total: 0 },
search: { total: 0 },
index_pattern: { total: 0 },
graph_workspace: { total: 0 },
timelion_sheet: { total: 0 },
});
});
test('Merge the zeros with the results', async () => {
const callCluster = jest.fn(() => ({
aggregations: {
types: {
buckets: [
{ key: 'dashboard', doc_count: 1 },
{ key: 'timelion-sheet', doc_count: 2 },
{ key: 'index-pattern', value: 2 }, // Malformed on purpose
{ key: 'graph_workspace', doc_count: 3 }, // already snake_cased
],
},
},
}));
const results = await getSavedObjectsCounts(callCluster as any, '.kibana');
expect(results).toStrictEqual({
dashboard: { total: 1 },
visualization: { total: 0 },
search: { total: 0 },
index_pattern: { total: 0 },
graph_workspace: { total: 3 },
timelion_sheet: { total: 2 },
});
});
});

View file

@ -0,0 +1,82 @@
/*
* 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.
*/
/**
* Moved from /x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_kibana_usage_collector.ts
*
* The PR https://github.com/elastic/kibana/pull/62665 proved what the issue https://github.com/elastic/kibana/issues/58249
* was claiming: the structure and payload for common telemetry bits differs between Monitoring and OSS/X-Pack collections.
*
* Unifying this logic from Monitoring that makes sense to have in OSS here and we will import it on the monitoring side to reuse it.
*/
import { snakeCase } from 'lodash';
import { APICaller } from 'kibana/server';
const TYPES = [
'dashboard',
'visualization',
'search',
'index-pattern',
'graph-workspace',
'timelion-sheet',
];
export interface KibanaSavedObjectCounts {
[pluginName: string]: {
total: number;
};
}
export async function getSavedObjectsCounts(
callCluster: APICaller,
kibanaIndex: string // Typically '.kibana'. We might need a way to obtain it from the SavedObjects client (or the SavedObjects client to provide a way to run aggregations?)
): Promise<KibanaSavedObjectCounts> {
const savedObjectCountSearchParams = {
index: kibanaIndex,
ignoreUnavailable: true,
filterPath: 'aggregations.types.buckets',
body: {
size: 0,
query: {
terms: { type: TYPES },
},
aggs: {
types: {
terms: { field: 'type', size: TYPES.length },
},
},
},
};
const resp = await callCluster('search', savedObjectCountSearchParams);
const buckets: Array<{ key: string; doc_count: number }> =
resp.aggregations?.types?.buckets || [];
// Initialise the object with all zeros for all the types
const allZeros: KibanaSavedObjectCounts = TYPES.reduce(
(acc, type) => ({ ...acc, [snakeCase(type)]: { total: 0 } }),
{}
);
// Add the doc_count from each bucket
return buckets.reduce(
(acc, { key, doc_count: total }) => (total ? { ...acc, [snakeCase(key)]: { total } } : acc),
allZeros
);
}

View file

@ -0,0 +1,76 @@
/*
* 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 { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server';
import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector';
import { registerKibanaUsageCollector } from './';
describe('telemetry_kibana', () => {
let collector: CollectorOptions;
const usageCollectionMock: jest.Mocked<UsageCollectionSetup> = {
makeUsageCollector: jest.fn().mockImplementation(config => (collector = config)),
registerCollector: jest.fn(),
} as any;
const legacyConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$;
const callCluster = jest.fn().mockImplementation(() => ({}));
beforeAll(() => registerKibanaUsageCollector(usageCollectionMock, legacyConfig$));
afterAll(() => jest.clearAllTimers());
test('registered collector is set', () => {
expect(collector).not.toBeUndefined();
expect(collector.type).toBe('kibana');
});
test('fetch', async () => {
expect(await collector.fetch(callCluster)).toStrictEqual({
index: '.kibana-tests',
dashboard: { total: 0 },
visualization: { total: 0 },
search: { total: 0 },
index_pattern: { total: 0 },
graph_workspace: { total: 0 },
timelion_sheet: { total: 0 },
});
});
test('formatForBulkUpload', async () => {
const resultFromFetch = {
index: '.kibana-tests',
dashboard: { total: 0 },
visualization: { total: 0 },
search: { total: 0 },
index_pattern: { total: 0 },
graph_workspace: { total: 0 },
timelion_sheet: { total: 0 },
};
expect(collector.formatForBulkUpload!(resultFromFetch)).toStrictEqual({
type: 'kibana_stats',
payload: {
usage: resultFromFetch,
},
});
});
});

View file

@ -17,4 +17,4 @@
* under the License.
*/
export const KIBANA_STATS_TYPE = 'oss_kibana_stats'; // kibana stats per 5s intervals
export { registerKibanaUsageCollector } from './kibana_usage_collector';

View file

@ -0,0 +1,65 @@
/*
* 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 { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { SharedGlobalConfig } from 'kibana/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { KIBANA_STATS_TYPE, KIBANA_USAGE_TYPE } from '../../../common/constants';
import { getSavedObjectsCounts } from './get_saved_object_counts';
export function getKibanaUsageCollector(
usageCollection: UsageCollectionSetup,
legacyConfig$: Observable<SharedGlobalConfig>
) {
return usageCollection.makeUsageCollector({
type: KIBANA_USAGE_TYPE,
isReady: () => true,
async fetch(callCluster) {
const {
kibana: { index },
} = await legacyConfig$.pipe(take(1)).toPromise();
return {
index,
...(await getSavedObjectsCounts(callCluster, index)),
};
},
/*
* Format the response data into a model for internal upload
* 1. Make this data part of the "kibana_stats" type
* 2. Organize the payload in the usage namespace of the data payload (usage.index, etc)
*/
formatForBulkUpload: result => {
return {
type: KIBANA_STATS_TYPE,
payload: {
usage: result,
},
};
},
});
}
export function registerKibanaUsageCollector(
usageCollection: UsageCollectionSetup,
legacyConfig$: Observable<SharedGlobalConfig>
) {
usageCollection.registerCollector(getKibanaUsageCollector(usageCollection, legacyConfig$));
}

View file

@ -0,0 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`telemetry_ops_stats should return something when there is a metric 1`] = `
Object {
"concurrent_connections": 20,
"os": Object {
"load": Object {
"15m": 3,
"1m": 0.5,
"5m": 1,
},
"memory": Object {
"free_in_bytes": 10,
"total_in_bytes": 10,
"used_in_bytes": 10,
},
"platform": "darwin",
"platformRelease": "test",
"uptime_in_millis": 1000,
},
"process": Object {
"event_loop_delay": 10,
"memory": Object {
"heap": Object {
"size_limit": 0,
"total_in_bytes": 0,
"used_in_bytes": 0,
},
"resident_set_size_in_bytes": 0,
},
"uptime_in_millis": 1000,
},
"requests": Object {
"disconnects": 10,
"total": 100,
},
"response_times": Object {
"average": 100,
"max": 200,
},
"timestamp": Any<String>,
}
`;

View file

@ -0,0 +1,132 @@
/*
* 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 { Subject } from 'rxjs';
import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector';
import { registerOpsStatsCollector } from './';
import { OpsMetrics } from '../../../../../core/server';
describe('telemetry_ops_stats', () => {
let collector: CollectorOptions;
const usageCollectionMock: jest.Mocked<UsageCollectionSetup> = {
makeStatsCollector: jest.fn().mockImplementation(config => (collector = config)),
registerCollector: jest.fn(),
} as any;
const metrics$ = new Subject<OpsMetrics>();
const callCluster = jest.fn();
const metric: OpsMetrics = {
process: {
memory: {
heap: {
total_in_bytes: 0,
used_in_bytes: 0,
size_limit: 0,
},
resident_set_size_in_bytes: 0,
},
event_loop_delay: 10,
pid: 10,
uptime_in_millis: 1000,
},
os: {
platform: 'darwin',
platformRelease: 'test',
load: {
'1m': 0.5,
'5m': 1,
'15m': 3,
},
memory: {
total_in_bytes: 10,
free_in_bytes: 10,
used_in_bytes: 10,
},
uptime_in_millis: 1000,
},
response_times: { avg_in_millis: 100, max_in_millis: 200 },
requests: {
disconnects: 10,
total: 100,
statusCodes: { 200: 100 },
},
concurrent_connections: 20,
};
beforeAll(() => registerOpsStatsCollector(usageCollectionMock, metrics$));
afterAll(() => jest.clearAllTimers());
test('registered collector is set', () => {
expect(collector).not.toBeUndefined();
expect(collector.type).toBe('kibana_stats');
});
test('isReady should return false because no metrics have been provided yet', () => {
expect(collector.isReady()).toBe(false);
});
test('should return something when there is a metric', async () => {
metrics$.next(metric);
expect(collector.isReady()).toBe(true);
expect(await collector.fetch(callCluster)).toMatchSnapshot({
concurrent_connections: 20,
os: {
load: {
'15m': 3,
'1m': 0.5,
'5m': 1,
},
memory: {
free_in_bytes: 10,
total_in_bytes: 10,
used_in_bytes: 10,
},
platform: 'darwin',
platformRelease: 'test',
uptime_in_millis: 1000,
},
process: {
event_loop_delay: 10,
memory: {
heap: {
size_limit: 0,
total_in_bytes: 0,
used_in_bytes: 0,
},
resident_set_size_in_bytes: 0,
},
uptime_in_millis: 1000,
},
requests: {
disconnects: 10,
total: 100,
},
response_times: {
average: 100,
max: 200,
},
timestamp: expect.any(String),
});
});
});

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { registerOpsStatsCollector } from './get_ops_stats_collector';
export { registerOpsStatsCollector } from './ops_stats_collector';

View file

@ -0,0 +1,71 @@
/*
* 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 { Observable } from 'rxjs';
import { cloneDeep } from 'lodash';
import moment from 'moment';
import { OpsMetrics } from 'kibana/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { KIBANA_STATS_TYPE } from '../../../common/constants';
interface OpsStatsMetrics extends Omit<OpsMetrics, 'response_times'> {
timestamp: string;
response_times: {
average: number;
max: number;
};
}
/**
* Initialize a collector for Kibana Ops Stats
*/
export function getOpsStatsCollector(
usageCollection: UsageCollectionSetup,
metrics$: Observable<OpsMetrics>
) {
let lastMetrics: OpsStatsMetrics | null = null;
metrics$.subscribe(_metrics => {
const metrics = cloneDeep(_metrics);
// Ensure we only include the same data that Metricbeat collection would get
delete metrics.process.pid;
const responseTimes = {
average: metrics.response_times.avg_in_millis,
max: metrics.response_times.max_in_millis,
};
delete metrics.requests.statusCodes;
lastMetrics = {
...metrics,
response_times: responseTimes,
timestamp: moment.utc().toISOString(),
};
});
return usageCollection.makeStatsCollector({
type: KIBANA_STATS_TYPE,
isReady: () => !!lastMetrics,
fetch: () => lastMetrics,
});
}
export function registerOpsStatsCollector(
usageCollection: UsageCollectionSetup,
metrics$: Observable<OpsMetrics>
) {
usageCollection.registerCollector(getOpsStatsCollector(usageCollection, metrics$));
}

View file

@ -32,6 +32,8 @@ import {
SavedObjectsClient,
Plugin,
Logger,
SharedGlobalConfig,
MetricsServiceSetup,
} from '../../../core/server';
import { registerRoutes } from './routes';
import { registerCollection } from './telemetry_collection';
@ -41,6 +43,8 @@ import {
registerTelemetryPluginUsageCollector,
registerManagementUsageCollector,
registerApplicationUsageCollector,
registerKibanaUsageCollector,
registerOpsStatsCollector,
} from './collectors';
import { TelemetryConfigType } from './config';
import { FetcherTask } from './fetcher';
@ -61,6 +65,7 @@ export class TelemetryPlugin implements Plugin {
private readonly logger: Logger;
private readonly currentKibanaVersion: string;
private readonly config$: Observable<TelemetryConfigType>;
private readonly legacyConfig$: Observable<SharedGlobalConfig>;
private readonly isDev: boolean;
private readonly fetcherTask: FetcherTask;
private savedObjectsClient?: ISavedObjectsRepository;
@ -71,6 +76,7 @@ export class TelemetryPlugin implements Plugin {
this.isDev = initializerContext.env.mode.dev;
this.currentKibanaVersion = initializerContext.env.packageInfo.version;
this.config$ = initializerContext.config.create();
this.legacyConfig$ = initializerContext.config.legacy.globalConfig$;
this.fetcherTask = new FetcherTask({
...initializerContext,
logger: this.logger,
@ -78,15 +84,15 @@ export class TelemetryPlugin implements Plugin {
}
public async setup(
core: CoreSetup,
{ elasticsearch, http, savedObjects, metrics }: CoreSetup,
{ usageCollection, telemetryCollectionManager }: TelemetryPluginsSetup
) {
const currentKibanaVersion = this.currentKibanaVersion;
const config$ = this.config$;
const isDev = this.isDev;
registerCollection(telemetryCollectionManager, core.elasticsearch.dataClient);
const router = core.http.createRouter();
registerCollection(telemetryCollectionManager, elasticsearch.dataClient);
const router = http.createRouter();
registerRoutes({
config$,
@ -96,8 +102,8 @@ export class TelemetryPlugin implements Plugin {
telemetryCollectionManager,
});
this.registerMappings(opts => core.savedObjects.registerType(opts));
this.registerUsageCollectors(usageCollection, opts => core.savedObjects.registerType(opts));
this.registerMappings(opts => savedObjects.registerType(opts));
this.registerUsageCollectors(usageCollection, metrics, opts => savedObjects.registerType(opts));
}
public async start(core: CoreStart, { telemetryCollectionManager }: TelemetryPluginsStart) {
@ -153,11 +159,14 @@ export class TelemetryPlugin implements Plugin {
private registerUsageCollectors(
usageCollection: UsageCollectionSetup,
metrics: MetricsServiceSetup,
registerType: SavedObjectsRegisterType
) {
const getSavedObjectsClient = () => this.savedObjectsClient;
const getUiSettingsClient = () => this.uiSettingsClient;
registerOpsStatsCollector(usageCollection, metrics.getOpsMetrics$());
registerKibanaUsageCollector(usageCollection, this.legacyConfig$);
registerTelemetryPluginUsageCollector(usageCollection, {
currentKibanaVersion: this.currentKibanaVersion,
config$: this.config$,

View file

@ -55,6 +55,10 @@ export function handleKibanaStats(
...kibanaStats.os,
};
const formattedOsStats = Object.entries(os).reduce((acc, [key, value]) => {
if (typeof value !== 'string') {
// There are new fields reported now from the "os" property like "load", "memory", etc. They are objects.
return acc;
}
return {
...acc,
[`${key}s`]: [{ [key]: value, count: 1 }],

View file

@ -0,0 +1,16 @@
# Visualization Deafult Editor plugin
The default editor is used in most primary visualizations, e.x. `Area`, `Data table`, `Pie`, etc.
It acts as a container for a particular visualization and options tabs. Contains the default "Data" tab in `public/components/sidebar/data_tab.tsx`.
The plugin exposes the static `DefaultEditorController` class to consume.
```ts
import { DefaultEditorController } from '../../vis_default_editor/public';
const editor = new DefaultEditorController(
element,
vis,
eventEmitter,
embeddableHandler
);
```

View file

@ -28,14 +28,13 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { IAggConfig } from 'src/plugins/data/public';
import { IAggConfig, TimeRange } from 'src/plugins/data/public';
import { DefaultEditorAggParams } from './agg_params';
import { DefaultEditorAggCommonProps } from './agg_common_props';
import { AGGS_ACTION_KEYS, AggsAction } from './agg_group_state';
import { RowsOrColumnsControl } from './controls/rows_or_columns';
import { RadiusRatioOptionControl } from './controls/radius_ratio_option';
import { getSchemaByName } from '../schemas';
import { TimeRange } from '../../../../../plugins/data/public';
import { buildAggDescription } from './agg_params_helper';
export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps {

View file

@ -29,7 +29,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { IAggConfig, AggGroupNames } from '../../../../../plugins/data/public';
import { IAggConfig, AggGroupNames } from '../../../data/public';
import { Schema } from '../schemas';
interface DefaultEditorAggAddProps {

Some files were not shown because too many files have changed in this diff Show more