mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge branch 'master' into reporting/test-better
This commit is contained in:
commit
890128c47d
463 changed files with 6625 additions and 3897 deletions
20
.eslintrc.js
20
.eslintrc.js
|
@ -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: {
|
||||
|
|
|
@ -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",
|
||||
|
|
BIN
docs/apm/images/service-maps-java.png
Normal file
BIN
docs/apm/images/service-maps-java.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 524 KiB |
BIN
docs/apm/images/service-maps.png
Normal file
BIN
docs/apm/images/service-maps.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 483 KiB |
48
docs/apm/service-maps.asciidoc
Normal file
48
docs/apm/service-maps.asciidoc
Normal file
|
@ -0,0 +1,48 @@
|
|||
[[service-maps]]
|
||||
=== Service maps
|
||||
|
||||
beta::[]
|
||||
|
||||
A service map is a real-time diagram of the interactions occurring in your application’s 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`.
|
|
@ -31,6 +31,8 @@ include::transactions.asciidoc[]
|
|||
|
||||
include::spans.asciidoc[]
|
||||
|
||||
include::service-maps.asciidoc[]
|
||||
|
||||
include::errors.asciidoc[]
|
||||
|
||||
include::metrics.asciidoc[]
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
});
|
||||
}
|
||||
-----------
|
||||
|
|
|
@ -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 |
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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' }],
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}–{endItem} of {totalItems}"
|
||||
values={{ startItem, endItem, totalItems }}
|
||||
/>
|
||||
</div>
|
||||
<I18nProvider>
|
||||
<div className="kuiToolBarText" data-test-subj="toolBarPagerText">
|
||||
<FormattedMessage
|
||||
id="kbn.docTable.pagerControl.pagesCountLabel"
|
||||
defaultMessage="{startItem}–{endItem} of {totalItems}"
|
||||
values={{ startItem, endItem, totalItems }}
|
||||
/>
|
||||
</div>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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' }],
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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' }],
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "vis_default_editor",
|
||||
"version": "kibana"
|
||||
}
|
|
@ -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>) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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 = [
|
||||
{
|
||||
|
|
|
@ -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), [
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -95,6 +95,7 @@ describe('MetricsAxisOptions component', () => {
|
|||
schemas: { metrics: [{ name: 'metric' }] },
|
||||
},
|
||||
setState: jest.fn(),
|
||||
serialize: jest.fn(),
|
||||
},
|
||||
stateParams: {
|
||||
valueAxes: [axis],
|
||||
|
|
|
@ -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 ? (
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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' });
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 },
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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
|
||||
);
|
||||
}
|
76
src/plugins/telemetry/server/collectors/kibana/index.test.ts
Normal file
76
src/plugins/telemetry/server/collectors/kibana/index.test.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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';
|
|
@ -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$));
|
||||
}
|
43
src/plugins/telemetry/server/collectors/ops_stats/__snapshots__/index.test.ts.snap
generated
Normal file
43
src/plugins/telemetry/server/collectors/ops_stats/__snapshots__/index.test.ts.snap
generated
Normal 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>,
|
||||
}
|
||||
`;
|
132
src/plugins/telemetry/server/collectors/ops_stats/index.test.ts
Normal file
132
src/plugins/telemetry/server/collectors/ops_stats/index.test.ts
Normal 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),
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { registerOpsStatsCollector } from './get_ops_stats_collector';
|
||||
export { registerOpsStatsCollector } from './ops_stats_collector';
|
|
@ -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$));
|
||||
}
|
|
@ -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$,
|
||||
|
|
|
@ -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 }],
|
||||
|
|
16
src/plugins/vis_default_editor/README.md
Normal file
16
src/plugins/vis_default_editor/README.md
Normal 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
|
||||
);
|
||||
```
|
|
@ -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 {
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue