mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Created management plugin under core_plugins. Filled the plugin with 2 services: IndexPatternManagementService and SavedObjectsManagementService. Removed related codes in ui/management and changed the paths.
This commit is contained in:
parent
80d35bdd10
commit
eec700909e
56 changed files with 927 additions and 457 deletions
|
@ -17,6 +17,7 @@
|
|||
"kbn": "src/legacy/core_plugins/kibana",
|
||||
"kbnDocViews": "src/legacy/core_plugins/kbn_doc_views",
|
||||
"kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types",
|
||||
"management": "src/legacy/core_plugins/management",
|
||||
"kibana_react": "src/legacy/core_plugins/kibana_react",
|
||||
"kibana-react": "src/plugins/kibana_react",
|
||||
"kibana_utils": "src/plugins/kibana_utils",
|
||||
|
|
|
@ -50,8 +50,6 @@ export class StepTimeField extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { getIndexPatternType, getIndexPatternName } = props.indexPatternCreationType;
|
||||
|
||||
this.state = {
|
||||
error: '',
|
||||
timeFields: [],
|
||||
|
@ -61,8 +59,8 @@ export class StepTimeField extends Component {
|
|||
isFetchingTimeFields: false,
|
||||
isCreating: false,
|
||||
indexPatternId: '',
|
||||
indexPatternType: getIndexPatternType(),
|
||||
indexPatternName: getIndexPatternName(),
|
||||
indexPatternType: props.indexPatternCreationType.getIndexPatternType(),
|
||||
indexPatternName: props.indexPatternCreationType.getIndexPatternName(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
|||
import uiRoutes from 'ui/routes';
|
||||
import angularTemplate from './angular_template.html';
|
||||
import 'ui/index_patterns';
|
||||
import { IndexPatternCreationFactory } from 'ui/management/index_pattern_creation';
|
||||
import { setup as managementSetup } from '../../../../../../management/public/legacy';
|
||||
import { getCreateBreadcrumbs } from '../breadcrumbs';
|
||||
|
||||
import { renderCreateIndexPatternWizard, destroyCreateIndexPatternWizard } from './render';
|
||||
|
@ -35,8 +35,9 @@ uiRoutes.when('/management/kibana/index_pattern', {
|
|||
const Private = $injector.get('Private');
|
||||
$scope.$$postDigest(() => {
|
||||
const $routeParams = $injector.get('$routeParams');
|
||||
const indexPatternCreationProvider = Private(IndexPatternCreationFactory)($routeParams.type);
|
||||
const indexPatternCreationType = indexPatternCreationProvider.getType();
|
||||
const indexPatternCreationType = managementSetup.indexPattern.creation.getType(
|
||||
$routeParams.type
|
||||
);
|
||||
const services = {
|
||||
config: $injector.get('config'),
|
||||
es: $injector.get('es'),
|
||||
|
@ -52,12 +53,9 @@ uiRoutes.when('/management/kibana/index_pattern', {
|
|||
|
||||
const initialQuery = $routeParams.id ? decodeURIComponent($routeParams.id) : undefined;
|
||||
|
||||
renderCreateIndexPatternWizard(
|
||||
initialQuery,
|
||||
services
|
||||
);
|
||||
renderCreateIndexPatternWizard(initialQuery, services);
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', destroyCreateIndexPatternWizard);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -28,7 +28,7 @@ import uiRoutes from 'ui/routes';
|
|||
import { uiModules } from 'ui/modules';
|
||||
import template from './edit_index_pattern.html';
|
||||
import { fieldWildcardMatcher } from 'ui/field_wildcard';
|
||||
import { IndexPatternListFactory } from 'ui/management/index_pattern_list';
|
||||
import { setup as managementSetup } from '../../../../../../management/public/legacy';
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { SourceFiltersTable } from './source_filters_table';
|
||||
|
@ -58,13 +58,17 @@ function updateSourceFiltersTable($scope, $state) {
|
|||
filterFilter={$scope.fieldFilter}
|
||||
fieldWildcardMatcher={$scope.fieldWildcardMatcher}
|
||||
onAddOrRemoveFilter={() => {
|
||||
$scope.editSections = $scope.editSectionsProvider($scope.indexPattern, $scope.fieldFilter, $scope.indexPatternListProvider);
|
||||
$scope.editSections = $scope.editSectionsProvider(
|
||||
$scope.indexPattern,
|
||||
$scope.fieldFilter,
|
||||
$scope.indexPatternListProvider
|
||||
);
|
||||
$scope.refreshFilters();
|
||||
$scope.$apply();
|
||||
}}
|
||||
/>
|
||||
</I18nContext>,
|
||||
node,
|
||||
node
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
@ -77,7 +81,6 @@ function destroySourceFiltersTable() {
|
|||
node && unmountComponentAtNode(node);
|
||||
}
|
||||
|
||||
|
||||
function updateScriptedFieldsTable($scope, $state) {
|
||||
if ($state.tab === 'scriptedFields') {
|
||||
$scope.$$postDigest(() => {
|
||||
|
@ -100,13 +103,17 @@ function updateScriptedFieldsTable($scope, $state) {
|
|||
getRouteHref: (obj, route) => $scope.kbnUrl.getRouteHref(obj, route),
|
||||
}}
|
||||
onRemoveField={() => {
|
||||
$scope.editSections = $scope.editSectionsProvider($scope.indexPattern, $scope.fieldFilter, $scope.indexPatternListProvider);
|
||||
$scope.editSections = $scope.editSectionsProvider(
|
||||
$scope.indexPattern,
|
||||
$scope.fieldFilter,
|
||||
$scope.indexPatternListProvider
|
||||
);
|
||||
$scope.refreshFilters();
|
||||
$scope.$apply();
|
||||
}}
|
||||
/>
|
||||
</I18nContext>,
|
||||
node,
|
||||
node
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
@ -144,7 +151,7 @@ function updateIndexedFieldsTable($scope, $state) {
|
|||
}}
|
||||
/>
|
||||
</I18nContext>,
|
||||
node,
|
||||
node
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
@ -157,34 +164,36 @@ function destroyIndexedFieldsTable() {
|
|||
node && unmountComponentAtNode(node);
|
||||
}
|
||||
|
||||
uiRoutes
|
||||
.when('/management/kibana/index_patterns/:indexPatternId', {
|
||||
template,
|
||||
k7Breadcrumbs: getEditBreadcrumbs,
|
||||
resolve: {
|
||||
indexPattern: function ($route, Promise, redirectWhenMissing, indexPatterns) {
|
||||
return Promise.resolve(indexPatterns.get($route.current.params.indexPatternId))
|
||||
.catch(redirectWhenMissing('/management/kibana/index_patterns'));
|
||||
}
|
||||
uiRoutes.when('/management/kibana/index_patterns/:indexPatternId', {
|
||||
template,
|
||||
k7Breadcrumbs: getEditBreadcrumbs,
|
||||
resolve: {
|
||||
indexPattern: function ($route, Promise, redirectWhenMissing, indexPatterns) {
|
||||
return Promise.resolve(indexPatterns.get($route.current.params.indexPatternId)).catch(
|
||||
redirectWhenMissing('/management/kibana/index_patterns')
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
uiModules.get('apps/management')
|
||||
uiModules
|
||||
.get('apps/management')
|
||||
.controller('managementIndexPatternsEdit', function (
|
||||
$scope, $location, $route, Promise, config, indexPatterns, Private, AppState, confirmModal) {
|
||||
const $state = $scope.state = new AppState();
|
||||
const indexPatternListProvider = Private(IndexPatternListFactory)();
|
||||
|
||||
$scope.fieldWildcardMatcher = (...args) => fieldWildcardMatcher(...args, config.get('metaFields'));
|
||||
$scope.editSectionsProvider = Private(IndicesEditSectionsProvider);
|
||||
$scope.kbnUrl = Private(KbnUrlProvider);
|
||||
$scope.indexPattern = $route.current.locals.indexPattern;
|
||||
$scope.indexPatternListProvider = indexPatternListProvider;
|
||||
$scope.indexPattern.tags = indexPatternListProvider.getIndexPatternTags(
|
||||
$scope.indexPatternListProvider = managementSetup.indexPattern.list;
|
||||
$scope.indexPattern.tags = managementSetup.indexPattern.list.getIndexPatternTags(
|
||||
$scope.indexPattern,
|
||||
$scope.indexPattern.id === config.get('defaultIndex')
|
||||
);
|
||||
$scope.getFieldInfo = indexPatternListProvider.getFieldInfo;
|
||||
$scope.getFieldInfo = managementSetup.indexPattern.list.getFieldInfo.bind(
|
||||
managementSetup.indexPattern.list
|
||||
);
|
||||
docTitle.change($scope.indexPattern.title);
|
||||
|
||||
const otherPatterns = _.filter($route.current.locals.indexPatterns, pattern => {
|
||||
|
@ -192,7 +201,11 @@ uiModules.get('apps/management')
|
|||
});
|
||||
|
||||
$scope.$watch('indexPattern.fields', function () {
|
||||
$scope.editSections = $scope.editSectionsProvider($scope.indexPattern, $scope.fieldFilter, indexPatternListProvider);
|
||||
$scope.editSections = $scope.editSectionsProvider(
|
||||
$scope.indexPattern,
|
||||
$scope.fieldFilter,
|
||||
managementSetup.indexPattern.list
|
||||
);
|
||||
$scope.refreshFilters();
|
||||
$scope.fields = $scope.indexPattern.getNonScriptedFields();
|
||||
updateIndexedFieldsTable($scope, $state);
|
||||
|
@ -243,26 +256,26 @@ uiModules.get('apps/management')
|
|||
});
|
||||
|
||||
$scope.$watchCollection('indexPattern.fields', function () {
|
||||
$scope.conflictFields = $scope.indexPattern.fields
|
||||
.filter(field => field.type === 'conflict');
|
||||
$scope.conflictFields = $scope.indexPattern.fields.filter(field => field.type === 'conflict');
|
||||
});
|
||||
|
||||
$scope.refreshFields = function () {
|
||||
const confirmMessage = i18n.translate('kbn.management.editIndexPattern.refreshLabel', {
|
||||
defaultMessage: 'This action resets the popularity counter of each field.'
|
||||
defaultMessage: 'This action resets the popularity counter of each field.',
|
||||
});
|
||||
const confirmModalOptions = {
|
||||
confirmButtonText: i18n.translate('kbn.management.editIndexPattern.refreshButton', { defaultMessage: 'Refresh' }),
|
||||
confirmButtonText: i18n.translate('kbn.management.editIndexPattern.refreshButton', {
|
||||
defaultMessage: 'Refresh',
|
||||
}),
|
||||
onConfirm: async () => {
|
||||
await $scope.indexPattern.init(true);
|
||||
$scope.fields = $scope.indexPattern.getNonScriptedFields();
|
||||
},
|
||||
title: i18n.translate('kbn.management.editIndexPattern.refreshHeader', { defaultMessage: 'Refresh field list?' })
|
||||
title: i18n.translate('kbn.management.editIndexPattern.refreshHeader', {
|
||||
defaultMessage: 'Refresh field list?',
|
||||
}),
|
||||
};
|
||||
confirmModal(
|
||||
confirmMessage,
|
||||
confirmModalOptions
|
||||
);
|
||||
confirmModal(confirmMessage, confirmModalOptions);
|
||||
};
|
||||
|
||||
$scope.removePattern = function () {
|
||||
|
@ -283,9 +296,13 @@ uiModules.get('apps/management')
|
|||
}
|
||||
|
||||
const confirmModalOptions = {
|
||||
confirmButtonText: i18n.translate('kbn.management.editIndexPattern.deleteButton', { defaultMessage: 'Delete' }),
|
||||
confirmButtonText: i18n.translate('kbn.management.editIndexPattern.deleteButton', {
|
||||
defaultMessage: 'Delete',
|
||||
}),
|
||||
onConfirm: doRemove,
|
||||
title: i18n.translate('kbn.management.editIndexPattern.deleteHeader', { defaultMessage: 'Delete index pattern?' })
|
||||
title: i18n.translate('kbn.management.editIndexPattern.deleteHeader', {
|
||||
defaultMessage: 'Delete index pattern?',
|
||||
}),
|
||||
};
|
||||
confirmModal('', confirmModalOptions);
|
||||
};
|
||||
|
@ -297,7 +314,8 @@ uiModules.get('apps/management')
|
|||
$scope.setIndexPatternsTimeField = function (field) {
|
||||
if (field.type !== 'date') {
|
||||
const errorMessage = i18n.translate('kbn.management.editIndexPattern.notDateErrorMessage', {
|
||||
defaultMessage: 'That field is a {fieldType} not a date.', values: { fieldType: field.type }
|
||||
defaultMessage: 'That field is a {fieldType} not a date.',
|
||||
values: { fieldType: field.type },
|
||||
});
|
||||
toastNotifications.addDanger(errorMessage);
|
||||
return;
|
||||
|
@ -307,12 +325,16 @@ uiModules.get('apps/management')
|
|||
};
|
||||
|
||||
$scope.$watch('fieldFilter', () => {
|
||||
$scope.editSections = $scope.editSectionsProvider($scope.indexPattern, $scope.fieldFilter, indexPatternListProvider);
|
||||
$scope.editSections = $scope.editSectionsProvider(
|
||||
$scope.indexPattern,
|
||||
$scope.fieldFilter,
|
||||
managementSetup.indexPattern.list
|
||||
);
|
||||
if ($scope.fieldFilter === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch($state.tab) {
|
||||
switch ($state.tab) {
|
||||
case 'indexedFields':
|
||||
updateIndexedFieldsTable($scope, $state);
|
||||
case 'scriptedFields':
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
*/
|
||||
|
||||
import { management } from 'ui/management';
|
||||
import { IndexPatternListFactory } from 'ui/management/index_pattern_list';
|
||||
import { IndexPatternCreationFactory } from 'ui/management/index_pattern_creation';
|
||||
import { setup as managementSetup } from '../../../../../management/public/legacy';
|
||||
import './create_index_pattern_wizard';
|
||||
import './edit_index_pattern';
|
||||
import uiRoutes from 'ui/routes';
|
||||
|
@ -28,7 +27,10 @@ import indexTemplate from './index.html';
|
|||
import indexPatternListTemplate from './list.html';
|
||||
import { IndexPatternTable } from './index_pattern_table';
|
||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
|
||||
import {
|
||||
FeatureCatalogueRegistryProvider,
|
||||
FeatureCatalogueCategory,
|
||||
} from 'ui/registry/feature_catalogue';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { UICapabilitiesProvider } from 'ui/capabilities/react';
|
||||
|
@ -39,11 +41,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
|
|||
|
||||
const INDEX_PATTERN_LIST_DOM_ELEMENT_ID = 'indexPatternListReact';
|
||||
|
||||
export function updateIndexPatternList(
|
||||
indexPatterns,
|
||||
kbnUrl,
|
||||
indexPatternCreationOptions,
|
||||
) {
|
||||
export function updateIndexPatternList(indexPatterns, kbnUrl, indexPatternCreationOptions) {
|
||||
const node = document.getElementById(INDEX_PATTERN_LIST_DOM_ELEMENT_ID);
|
||||
if (!node) {
|
||||
return;
|
||||
|
@ -59,7 +57,7 @@ export function updateIndexPatternList(
|
|||
/>
|
||||
</UICapabilitiesProvider>
|
||||
</I18nContext>,
|
||||
node,
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -72,55 +70,56 @@ const indexPatternsResolutions = {
|
|||
indexPatterns: function (Private) {
|
||||
const savedObjectsClient = Private(SavedObjectsClientProvider);
|
||||
|
||||
return savedObjectsClient.find({
|
||||
type: 'index-pattern',
|
||||
fields: ['title', 'type'],
|
||||
perPage: 10000
|
||||
}).then(response => response.savedObjects);
|
||||
}
|
||||
return savedObjectsClient
|
||||
.find({
|
||||
type: 'index-pattern',
|
||||
fields: ['title', 'type'],
|
||||
perPage: 10000,
|
||||
})
|
||||
.then(response => response.savedObjects);
|
||||
},
|
||||
};
|
||||
|
||||
// add a dependency to all of the subsection routes
|
||||
uiRoutes
|
||||
.defaults(/management\/kibana\/(index_patterns|index_pattern)/, {
|
||||
resolve: indexPatternsResolutions,
|
||||
requireUICapability: 'management.kibana.index_patterns',
|
||||
badge: uiCapabilities => {
|
||||
if (uiCapabilities.indexPatterns.save) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
text: i18n.translate('kbn.management.indexPatterns.badge.readOnly.text', {
|
||||
defaultMessage: 'Read only',
|
||||
}),
|
||||
tooltip: i18n.translate('kbn.management.indexPatterns.badge.readOnly.tooltip', {
|
||||
defaultMessage: 'Unable to save index patterns',
|
||||
}),
|
||||
iconType: 'glasses'
|
||||
};
|
||||
uiRoutes.defaults(/management\/kibana\/(index_patterns|index_pattern)/, {
|
||||
resolve: indexPatternsResolutions,
|
||||
requireUICapability: 'management.kibana.index_patterns',
|
||||
badge: uiCapabilities => {
|
||||
if (uiCapabilities.indexPatterns.save) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
uiRoutes
|
||||
.when('/management/kibana/index_patterns', {
|
||||
template: indexPatternListTemplate,
|
||||
k7Breadcrumbs: getListBreadcrumbs
|
||||
});
|
||||
return {
|
||||
text: i18n.translate('kbn.management.indexPatterns.badge.readOnly.text', {
|
||||
defaultMessage: 'Read only',
|
||||
}),
|
||||
tooltip: i18n.translate('kbn.management.indexPatterns.badge.readOnly.tooltip', {
|
||||
defaultMessage: 'Unable to save index patterns',
|
||||
}),
|
||||
iconType: 'glasses',
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
uiRoutes.when('/management/kibana/index_patterns', {
|
||||
template: indexPatternListTemplate,
|
||||
k7Breadcrumbs: getListBreadcrumbs,
|
||||
});
|
||||
|
||||
// wrapper directive, which sets some global stuff up like the left nav
|
||||
uiModules.get('apps/management')
|
||||
.directive('kbnManagementIndexPatterns', function ($route, config, kbnUrl, Private) {
|
||||
uiModules
|
||||
.get('apps/management')
|
||||
.directive('kbnManagementIndexPatterns', function ($route, config, kbnUrl) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
template: indexTemplate,
|
||||
link: async function ($scope) {
|
||||
const indexPatternListProvider = Private(IndexPatternListFactory)();
|
||||
const indexPatternCreationProvider = Private(IndexPatternCreationFactory)();
|
||||
const indexPatternCreationOptions = await indexPatternCreationProvider.getIndexPatternCreationOptions((url) => {
|
||||
$scope.$evalAsync(() => kbnUrl.change(url));
|
||||
});
|
||||
const indexPatternCreationOptions = await managementSetup.indexPattern.creation.getIndexPatternCreationOptions(
|
||||
url => {
|
||||
$scope.$evalAsync(() => kbnUrl.change(url));
|
||||
}
|
||||
);
|
||||
|
||||
const renderList = () => {
|
||||
$scope.indexPatternList =
|
||||
|
@ -129,7 +128,7 @@ uiModules.get('apps/management')
|
|||
const id = pattern.id;
|
||||
const title = pattern.get('title');
|
||||
const isDefault = $scope.defaultIndex === id;
|
||||
const tags = indexPatternListProvider.getIndexPatternTags(
|
||||
const tags = managementSetup.indexPattern.list.getIndexPatternTags(
|
||||
pattern,
|
||||
isDefault
|
||||
);
|
||||
|
@ -165,25 +164,30 @@ uiModules.get('apps/management')
|
|||
$scope.$watch('defaultIndex', () => renderList());
|
||||
config.bindToScope($scope, 'defaultIndex');
|
||||
$scope.$apply();
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
management.getSection('kibana').register('index_patterns', {
|
||||
display: i18n.translate('kbn.management.indexPattern.sectionsHeader', { defaultMessage: 'Index Patterns' }),
|
||||
display: i18n.translate('kbn.management.indexPattern.sectionsHeader', {
|
||||
defaultMessage: 'Index Patterns',
|
||||
}),
|
||||
order: 0,
|
||||
url: '#/management/kibana/index_patterns/'
|
||||
url: '#/management/kibana/index_patterns/',
|
||||
});
|
||||
|
||||
FeatureCatalogueRegistryProvider.register(() => {
|
||||
return {
|
||||
id: 'index_patterns',
|
||||
title: i18n.translate('kbn.management.indexPatternHeader', { defaultMessage: 'Index Patterns' }),
|
||||
description: i18n.translate('kbn.management.indexPatternLabel',
|
||||
{ defaultMessage: 'Manage the index patterns that help retrieve your data from Elasticsearch.' }),
|
||||
title: i18n.translate('kbn.management.indexPatternHeader', {
|
||||
defaultMessage: 'Index Patterns',
|
||||
}),
|
||||
description: i18n.translate('kbn.management.indexPatternLabel', {
|
||||
defaultMessage: 'Manage the index patterns that help retrieve your data from Elasticsearch.',
|
||||
}),
|
||||
icon: 'indexPatternApp',
|
||||
path: '/app/kibana#/management/kibana/index_patterns',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
|
||||
import { mockManagementPlugin } from '../../../../../../../../management/public/np_ready/mocks';
|
||||
import { Query } from '@elastic/eui';
|
||||
|
||||
import { ObjectsTable, POSSIBLE_TYPES } from '../objects_table';
|
||||
|
@ -29,6 +30,11 @@ import { extractExportDetails } from '../../../lib/extract_export_details';
|
|||
|
||||
jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() }));
|
||||
|
||||
jest.mock('../../../../../../../../management/public/legacy', () => ({
|
||||
setup: mockManagementPlugin.createSetupContract(),
|
||||
start: mockManagementPlugin.createStartContract(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../lib/find_objects', () => ({
|
||||
findObjects: jest.fn(),
|
||||
}));
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
|
||||
|
||||
import { mockManagementPlugin } from '../../../../../../../../../../management/public/np_ready/mocks';
|
||||
import { Flyout } from '../flyout';
|
||||
|
||||
jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() }));
|
||||
|
@ -48,6 +48,11 @@ jest.mock('../../../../../lib/resolve_saved_objects', () => ({
|
|||
saveObjects: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../../../../../../management/public/legacy', () => ({
|
||||
setup: mockManagementPlugin.createSetupContract(),
|
||||
start: mockManagementPlugin.createStartContract(),
|
||||
}));
|
||||
|
||||
jest.mock('ui/notify', () => ({}));
|
||||
|
||||
const defaultProps = {
|
||||
|
|
|
@ -21,6 +21,7 @@ import React from 'react';
|
|||
import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { keyCodes } from '@elastic/eui/lib/services';
|
||||
import { mockManagementPlugin } from '../../../../../../../../../../management/public/np_ready/mocks';
|
||||
|
||||
jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() }));
|
||||
|
||||
|
@ -28,6 +29,11 @@ jest.mock('ui/chrome', () => ({
|
|||
addBasePath: () => '',
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../../../../../../management/public/legacy', () => ({
|
||||
setup: mockManagementPlugin.createSetupContract(),
|
||||
start: mockManagementPlugin.createStartContract(),
|
||||
}));
|
||||
|
||||
import { Table } from '../table';
|
||||
|
||||
const defaultProps = {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { SavedObjectsManagementActionRegistry } from 'ui/management/saved_objects_management';
|
||||
import { setup as managementSetup } from '../../../../../../../../../management/public/legacy';
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
@ -79,7 +79,7 @@ export class Table extends PureComponent {
|
|||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.extraActions = SavedObjectsManagementActionRegistry.get();
|
||||
this.extraActions = managementSetup.savedObjects.registry.get();
|
||||
}
|
||||
|
||||
onChange = ({ query, error }) => {
|
||||
|
|
37
src/legacy/core_plugins/management/index.ts
Normal file
37
src/legacy/core_plugins/management/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ManagementPlugin(kibana: any) {
|
||||
const config: Legacy.PluginSpecOptions = {
|
||||
id: 'management',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
config: (Joi: any) => {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
}).default();
|
||||
},
|
||||
init: (server: Legacy.Server) => ({}),
|
||||
};
|
||||
|
||||
return new kibana.Plugin(config);
|
||||
}
|
5
src/legacy/core_plugins/management/package.json
Normal file
5
src/legacy/core_plugins/management/package.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "management",
|
||||
"version": "kibana"
|
||||
}
|
||||
|
|
@ -17,13 +17,24 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { SavedObjectsManagementActionRegistry } from './saved_objects_management_action_registry';
|
||||
/**
|
||||
* Static np-ready code, re-exported here so consumers can import from
|
||||
* `src/legacy/core_plugins/management/public`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
export {
|
||||
ManagementSetup,
|
||||
ManagementStart,
|
||||
plugin,
|
||||
IndexPatternCreationConfig,
|
||||
IndexPatternListConfig,
|
||||
SavedObjectsManagementAction,
|
||||
SavedObjectsManagementRecord,
|
||||
SavedObjectsManagementRecordReference,
|
||||
} from './saved_objects_management_action';
|
||||
} from './np_ready';
|
||||
|
||||
export {
|
||||
processImportResponse,
|
||||
ProcessedImportResponse,
|
||||
} from '../../../../core_plugins/kibana/public/management/sections/objects/lib/process_import_response';
|
||||
} from '../../kibana/public/management/sections/objects/lib/process_import_response';
|
45
src/legacy/core_plugins/management/public/legacy.ts
Normal file
45
src/legacy/core_plugins/management/public/legacy.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* New Platform Shim
|
||||
*
|
||||
* In this file, we import any legacy dependencies we have, and shim them into
|
||||
* our plugin by manually constructing the values that the new platform will
|
||||
* eventually be passing to the `setup/start` method of our plugin definition.
|
||||
*
|
||||
* The idea is that our `plugin.ts` can stay "pure" and not contain any legacy
|
||||
* world code. Then when it comes time to migrate to the new platform, we can
|
||||
* simply delete this shim file.
|
||||
*
|
||||
* We are also calling `setup/start` here and exporting our public contract so that
|
||||
* other legacy plugins are able to import from '../core_plugins/visualizations/legacy'
|
||||
* and receive the response value of the `setup/start` contract, mimicking the
|
||||
* data that will eventually be injected by the new platform.
|
||||
*/
|
||||
|
||||
import { PluginInitializerContext } from 'src/core/public';
|
||||
import { npSetup, npStart } from 'ui/new_platform';
|
||||
|
||||
import { plugin } from '.';
|
||||
|
||||
const pluginInstance = plugin({} as PluginInitializerContext);
|
||||
|
||||
export const setup = pluginInstance.setup(npSetup.core, {});
|
||||
export const start = pluginInstance.start(npStart.core, {});
|
47
src/legacy/core_plugins/management/public/np_ready/index.ts
Normal file
47
src/legacy/core_plugins/management/public/np_ready/index.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Management Plugin - public
|
||||
*
|
||||
* This is the entry point for the entire client-side public contract of the plugin.
|
||||
* If something is not explicitly exported here, you can safely assume it is private
|
||||
* to the plugin and not considered stable.
|
||||
*
|
||||
* All stateful contracts will be injected by the platform at runtime, and are defined
|
||||
* in the setup/start interfaces in `plugin.ts`. The remaining items exported here are
|
||||
* either types, or static code.
|
||||
*/
|
||||
import { PluginInitializerContext } from 'src/core/public';
|
||||
import { ManagementPlugin } from './plugin';
|
||||
export { ManagementSetup, ManagementStart } from './plugin';
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new ManagementPlugin(initializerContext);
|
||||
}
|
||||
|
||||
export {
|
||||
IndexPatternCreationConfig,
|
||||
IndexPatternListConfig,
|
||||
} from './services/index_pattern_management';
|
||||
|
||||
export {
|
||||
SavedObjectsManagementAction,
|
||||
SavedObjectsManagementRecord,
|
||||
} from './services/saved_objects_management';
|
66
src/legacy/core_plugins/management/public/np_ready/mocks.ts
Normal file
66
src/legacy/core_plugins/management/public/np_ready/mocks.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { PluginInitializerContext } from 'src/core/public';
|
||||
import { coreMock } from '../../../../../core/public/mocks';
|
||||
import { ManagementSetup, ManagementStart, ManagementPlugin } from './plugin';
|
||||
|
||||
const createSetupContract = (): ManagementSetup => ({
|
||||
indexPattern: {
|
||||
creation: {
|
||||
add: jest.fn(),
|
||||
getType: jest.fn(),
|
||||
getIndexPatternCreationOptions: jest.fn(),
|
||||
} as any,
|
||||
list: {
|
||||
add: jest.fn(),
|
||||
getIndexPatternTags: jest.fn(),
|
||||
getFieldInfo: jest.fn(),
|
||||
areScriptedFieldsEnabled: jest.fn(),
|
||||
} as any,
|
||||
},
|
||||
savedObjects: {
|
||||
registry: {
|
||||
register: jest.fn(),
|
||||
has: jest.fn(),
|
||||
get: jest.fn(() => []),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const createStartContract = (): ManagementStart => ({});
|
||||
|
||||
const createInstance = async () => {
|
||||
const plugin = new ManagementPlugin({} as PluginInitializerContext);
|
||||
|
||||
const setup = plugin.setup(coreMock.createSetup(), {});
|
||||
const doStart = () => plugin.start(coreMock.createStart(), {});
|
||||
|
||||
return {
|
||||
plugin,
|
||||
setup,
|
||||
doStart,
|
||||
};
|
||||
};
|
||||
|
||||
export const mockManagementPlugin = {
|
||||
createSetupContract,
|
||||
createStartContract,
|
||||
createInstance,
|
||||
};
|
67
src/legacy/core_plugins/management/public/np_ready/plugin.ts
Normal file
67
src/legacy/core_plugins/management/public/np_ready/plugin.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
|
||||
import { IndexPatternManagementService, IndexPatternManagementSetup } from './services';
|
||||
import {
|
||||
SavedObjectsManagementService,
|
||||
SavedObjectsManagementServiceSetup,
|
||||
} from './services/saved_objects_management';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface ManagementPluginSetupDependencies {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface ManagementPluginStartDependencies {}
|
||||
|
||||
export interface ManagementSetup {
|
||||
indexPattern: IndexPatternManagementSetup;
|
||||
savedObjects: SavedObjectsManagementServiceSetup;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface ManagementStart {}
|
||||
|
||||
export class ManagementPlugin
|
||||
implements
|
||||
Plugin<
|
||||
ManagementSetup,
|
||||
ManagementStart,
|
||||
ManagementPluginSetupDependencies,
|
||||
ManagementPluginStartDependencies
|
||||
> {
|
||||
private readonly indexPattern = new IndexPatternManagementService();
|
||||
private readonly savedObjects = new SavedObjectsManagementService();
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {}
|
||||
|
||||
public setup(core: CoreSetup, deps: ManagementPluginSetupDependencies) {
|
||||
return {
|
||||
indexPattern: this.indexPattern.setup({ httpClient: core.http }),
|
||||
savedObjects: this.savedObjects.setup(),
|
||||
};
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: ManagementPluginStartDependencies) {
|
||||
return {};
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.indexPattern.stop();
|
||||
}
|
||||
}
|
|
@ -17,5 +17,5 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export const indexPatternTypes = [];
|
||||
export const addIndexPatternType = (type) => indexPatternTypes.push(type);
|
||||
export * from './index_pattern_management';
|
||||
export * from './saved_objects_management';
|
|
@ -19,17 +19,39 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const indexPatternTypeName = i18n.translate('common.ui.management.editIndexPattern.createIndex.defaultTypeName',
|
||||
{ defaultMessage: 'index pattern' });
|
||||
const indexPatternTypeName = i18n.translate(
|
||||
'management.editIndexPattern.createIndex.defaultTypeName',
|
||||
{ defaultMessage: 'index pattern' }
|
||||
);
|
||||
|
||||
const indexPatternButtonText = i18n.translate('common.ui.management.editIndexPattern.createIndex.defaultButtonText',
|
||||
{ defaultMessage: 'Standard index pattern' });
|
||||
const indexPatternButtonText = i18n.translate(
|
||||
'management.editIndexPattern.createIndex.defaultButtonText',
|
||||
{ defaultMessage: 'Standard index pattern' }
|
||||
);
|
||||
|
||||
const indexPatternButtonDescription = i18n.translate('common.ui.management.editIndexPattern.createIndex.defaultButtonDescription',
|
||||
{ defaultMessage: 'Perform full aggregations against any data' });
|
||||
const indexPatternButtonDescription = i18n.translate(
|
||||
'management.editIndexPattern.createIndex.defaultButtonDescription',
|
||||
{ defaultMessage: 'Perform full aggregations against any data' }
|
||||
);
|
||||
|
||||
export type UrlHandler = (url: string) => void;
|
||||
|
||||
export interface IndexPatternCreationOption {
|
||||
text: string;
|
||||
description: string;
|
||||
testSubj: string;
|
||||
onClick: () => void;
|
||||
isBeta?: boolean;
|
||||
}
|
||||
|
||||
export class IndexPatternCreationConfig {
|
||||
static key = 'default';
|
||||
public readonly key = 'default';
|
||||
|
||||
protected type?: string;
|
||||
protected name: string;
|
||||
protected showSystemIndices: boolean;
|
||||
protected httpClient: object | null;
|
||||
protected isBeta: boolean;
|
||||
|
||||
constructor({
|
||||
type = undefined,
|
||||
|
@ -37,6 +59,12 @@ export class IndexPatternCreationConfig {
|
|||
showSystemIndices = true,
|
||||
httpClient = null,
|
||||
isBeta = false,
|
||||
}: {
|
||||
type?: string;
|
||||
name?: string;
|
||||
showSystemIndices?: boolean;
|
||||
httpClient?: object | null;
|
||||
isBeta?: boolean;
|
||||
}) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
|
@ -45,7 +73,9 @@ export class IndexPatternCreationConfig {
|
|||
this.isBeta = isBeta;
|
||||
}
|
||||
|
||||
async getIndexPatternCreationOption(urlHandler) {
|
||||
public async getIndexPatternCreationOption(
|
||||
urlHandler: UrlHandler
|
||||
): Promise<IndexPatternCreationOption> {
|
||||
return {
|
||||
text: indexPatternButtonText,
|
||||
description: indexPatternButtonDescription,
|
||||
|
@ -56,39 +86,39 @@ export class IndexPatternCreationConfig {
|
|||
};
|
||||
}
|
||||
|
||||
getIndexPatternType = () => {
|
||||
public getIndexPatternType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
getIndexPatternName = () => {
|
||||
public getIndexPatternName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getIsBeta = () => {
|
||||
public getIsBeta() {
|
||||
return this.isBeta;
|
||||
}
|
||||
|
||||
getShowSystemIndices = () => {
|
||||
public getShowSystemIndices() {
|
||||
return this.showSystemIndices;
|
||||
}
|
||||
|
||||
getIndexTags() {
|
||||
public getIndexTags() {
|
||||
return [];
|
||||
}
|
||||
|
||||
checkIndicesForErrors = () => {
|
||||
public checkIndicesForErrors() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getIndexPatternMappings = () => {
|
||||
public getIndexPatternMappings() {
|
||||
return {};
|
||||
}
|
||||
|
||||
renderPrompt = () => {
|
||||
public renderPrompt() {
|
||||
return null;
|
||||
}
|
||||
|
||||
getFetchForWildcardOptions = () => {
|
||||
public getFetchForWildcardOptions() {
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -17,10 +17,5 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { uiRegistry } from 'ui/registry/_registry';
|
||||
|
||||
export const IndexPatternListConfigRegistry = uiRegistry({
|
||||
name: 'indexPatternList',
|
||||
index: ['name'],
|
||||
order: ['order'],
|
||||
});
|
||||
export { IndexPatternCreationConfig } from './config';
|
||||
export { IndexPatternCreationManager } from './manager';
|
|
@ -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 { HttpServiceBase } from '../../../../../../../../core/public';
|
||||
import { IndexPatternCreationConfig, UrlHandler, IndexPatternCreationOption } from './config';
|
||||
|
||||
export class IndexPatternCreationManager {
|
||||
private configs: IndexPatternCreationConfig[];
|
||||
|
||||
constructor(private readonly httpClient: HttpServiceBase) {
|
||||
this.configs = [];
|
||||
}
|
||||
|
||||
public add(Config: typeof IndexPatternCreationConfig) {
|
||||
const config = new Config({ httpClient: this.httpClient });
|
||||
if (this.configs.findIndex(c => c.key === config.key) !== -1) {
|
||||
throw new Error(`${config.key} exists in IndexPatternCreationManager.`);
|
||||
}
|
||||
this.configs.push(config);
|
||||
}
|
||||
|
||||
public getType(key: string | undefined): IndexPatternCreationConfig | null {
|
||||
if (key) {
|
||||
const index = this.configs.findIndex(config => config.key === key);
|
||||
return this.configs[index] || null;
|
||||
} else {
|
||||
return this.getType('default');
|
||||
}
|
||||
}
|
||||
|
||||
public async getIndexPatternCreationOptions(urlHandler: UrlHandler) {
|
||||
const options: IndexPatternCreationOption[] = [];
|
||||
await Promise.all(
|
||||
this.configs.map(async config => {
|
||||
const option = config.getIndexPatternCreationOption
|
||||
? await config.getIndexPatternCreationOption(urlHandler)
|
||||
: null;
|
||||
if (option) {
|
||||
options.push(option);
|
||||
}
|
||||
})
|
||||
);
|
||||
return options;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { IndexPatternCreationConfig } from './index_pattern_creation_config';
|
||||
import { addIndexPatternType } from './index_pattern_types';
|
||||
|
||||
addIndexPatternType(IndexPatternCreationConfig);
|
||||
export * from './index_pattern_management_service';
|
||||
export { IndexPatternCreationConfig } from './creation';
|
||||
export { IndexPatternListConfig } from './list';
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 { HttpServiceBase } from '../../../../../../../core/public';
|
||||
import { IndexPatternCreationManager, IndexPatternCreationConfig } from './creation';
|
||||
import { IndexPatternListManager, IndexPatternListConfig } from './list';
|
||||
|
||||
interface SetupDependencies {
|
||||
httpClient: HttpServiceBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Index patterns management service
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class IndexPatternManagementService {
|
||||
public setup({ httpClient }: SetupDependencies) {
|
||||
const creation = new IndexPatternCreationManager(httpClient);
|
||||
const list = new IndexPatternListManager();
|
||||
|
||||
creation.add(IndexPatternCreationConfig);
|
||||
list.add(IndexPatternListConfig);
|
||||
|
||||
return {
|
||||
creation,
|
||||
list,
|
||||
};
|
||||
}
|
||||
|
||||
public stop() {
|
||||
// nothing to do here yet.
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type IndexPatternManagementSetup = ReturnType<IndexPatternManagementService['setup']>;
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { IIndexPattern, IFieldType } from 'src/plugins/data/public';
|
||||
|
||||
export interface IndexPatternTag {
|
||||
key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class IndexPatternListConfig {
|
||||
public readonly key = 'default';
|
||||
|
||||
public getIndexPatternTags(indexPattern: IIndexPattern, isDefault: boolean): IndexPatternTag[] {
|
||||
return isDefault
|
||||
? [
|
||||
{
|
||||
key: 'default',
|
||||
name: i18n.translate('management.editIndexPattern.list.defaultIndexPatternListName', {
|
||||
defaultMessage: 'Default',
|
||||
}),
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
public getFieldInfo(indexPattern: IIndexPattern, field: IFieldType): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
public areScriptedFieldsEnabled(indexPattern: IIndexPattern): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,5 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { IndexPatternListConfig } from './index_pattern_list_config';
|
||||
import { IndexPatternListConfigRegistry } from './index_pattern_list_config_registry';
|
||||
|
||||
IndexPatternListConfigRegistry.register(() => IndexPatternListConfig);
|
||||
export { IndexPatternListConfig } from './config';
|
||||
export { IndexPatternListManager } from './manager';
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { IIndexPattern, IFieldType } from 'src/plugins/data/public';
|
||||
import { IndexPatternListConfig, IndexPatternTag } from './config';
|
||||
|
||||
export class IndexPatternListManager {
|
||||
private configs: IndexPatternListConfig[];
|
||||
|
||||
constructor() {
|
||||
this.configs = [];
|
||||
}
|
||||
|
||||
public add(Config: typeof IndexPatternListConfig) {
|
||||
const config = new Config();
|
||||
if (this.configs.findIndex(c => c.key === config.key) !== -1) {
|
||||
throw new Error(`${config.key} exists in IndexPatternListManager.`);
|
||||
}
|
||||
this.configs.push(config);
|
||||
}
|
||||
|
||||
public getIndexPatternTags(indexPattern: IIndexPattern, isDefault: boolean) {
|
||||
return this.configs.reduce((tags: IndexPatternTag[], config) => {
|
||||
return config.getIndexPatternTags
|
||||
? tags.concat(config.getIndexPatternTags(indexPattern, isDefault))
|
||||
: tags;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public getFieldInfo(indexPattern: IIndexPattern, field: IFieldType): string[] {
|
||||
return this.configs.reduce((info: string[], config) => {
|
||||
return config.getFieldInfo ? info.concat(config.getFieldInfo(indexPattern, field)) : info;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public areScriptedFieldsEnabled(indexPattern: IIndexPattern): boolean {
|
||||
return this.configs.every(config => {
|
||||
return config.areScriptedFieldsEnabled ? config.areScriptedFieldsEnabled(indexPattern) : true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './saved_objects_management_action_registry';
|
||||
export * from './saved_objects_management_action';
|
||||
export * from './saved_objects_management_service';
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ReactNode } from '@elastic/eui/node_modules/@types/react';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface SavedObjectsManagementRecordReference {
|
||||
type: string;
|
|
@ -16,22 +16,17 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { SavedObjectsManagementActionRegistry } from './saved_objects_management_action_registry';
|
||||
|
||||
export class IndexPatternListConfig {
|
||||
static key = 'default';
|
||||
|
||||
getIndexPatternTags = (indexPattern, isDefault) => {
|
||||
return isDefault ? [{
|
||||
key: 'default',
|
||||
name: 'Default',
|
||||
}] : [];
|
||||
export class SavedObjectsManagementService {
|
||||
public setup() {
|
||||
return {
|
||||
registry: SavedObjectsManagementActionRegistry,
|
||||
};
|
||||
}
|
||||
|
||||
getFieldInfo = () => {
|
||||
return [];
|
||||
}
|
||||
|
||||
areScriptedFieldsEnabled = () => {
|
||||
return true;
|
||||
}
|
||||
public stop() {}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type SavedObjectsManagementServiceSetup = ReturnType<SavedObjectsManagementService['setup']>;
|
|
@ -1,23 +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 './register';
|
||||
export { IndexPatternCreationFactory } from './index_pattern_creation';
|
||||
export { IndexPatternCreationConfig } from './index_pattern_creation_config';
|
||||
export { indexPatternTypes, addIndexPatternType } from './index_pattern_types';
|
|
@ -1,54 +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 { indexPatternTypes } from './index_pattern_types';
|
||||
|
||||
class IndexPatternCreation {
|
||||
constructor(httpClient, type) {
|
||||
this._allTypes = indexPatternTypes.map(Plugin => new Plugin({ httpClient }));
|
||||
this._setCurrentType(type);
|
||||
}
|
||||
|
||||
_setCurrentType = (type) => {
|
||||
const index = type ? indexPatternTypes.findIndex(Plugin => Plugin.key === type) : -1;
|
||||
this._currentType = index > -1 && this._allTypes[index] ? this._allTypes[index] : null;
|
||||
}
|
||||
|
||||
getType = () => {
|
||||
return this._currentType || null;
|
||||
}
|
||||
|
||||
getIndexPatternCreationOptions = async (urlHandler) => {
|
||||
const options = [];
|
||||
await Promise.all(this._allTypes.map(async type => {
|
||||
const option = type.getIndexPatternCreationOption ? await type.getIndexPatternCreationOption(urlHandler) : null;
|
||||
if(option) {
|
||||
options.push(option);
|
||||
}
|
||||
}));
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
export const IndexPatternCreationFactory = (Private, $http) => {
|
||||
return (type = 'default') => {
|
||||
const indexPatternCreationProvider = new IndexPatternCreation($http, type);
|
||||
return indexPatternCreationProvider;
|
||||
};
|
||||
};
|
|
@ -1,23 +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 './register';
|
||||
export { IndexPatternListFactory } from './index_pattern_list';
|
||||
export { IndexPatternListConfig } from './index_pattern_list_config';
|
||||
export { IndexPatternListConfigRegistry } from './index_pattern_list_config_registry';
|
|
@ -1,52 +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 { IndexPatternListConfigRegistry } from './index_pattern_list_config_registry';
|
||||
|
||||
class IndexPatternList {
|
||||
constructor(registry) {
|
||||
this._plugins = registry.inOrder.map(Plugin => new Plugin());
|
||||
}
|
||||
|
||||
getIndexPatternTags = (indexPattern, isDefault) => {
|
||||
return this._plugins.reduce((tags, plugin) => {
|
||||
return plugin.getIndexPatternTags ? tags.concat(plugin.getIndexPatternTags(indexPattern, isDefault)) : tags;
|
||||
}, []);
|
||||
}
|
||||
|
||||
getFieldInfo = (indexPattern, field) => {
|
||||
return this._plugins.reduce((info, plugin) => {
|
||||
return plugin.getFieldInfo ? info.concat(plugin.getFieldInfo(indexPattern, field)) : info;
|
||||
}, []);
|
||||
}
|
||||
|
||||
areScriptedFieldsEnabled = (indexPattern) => {
|
||||
return this._plugins.every((plugin) => {
|
||||
return plugin.areScriptedFieldsEnabled ? plugin.areScriptedFieldsEnabled(indexPattern) : true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const IndexPatternListFactory = (Private) => {
|
||||
return function () {
|
||||
const indexPatternListRegistry = Private(IndexPatternListConfigRegistry);
|
||||
const indexPatternListProvider = new IndexPatternList(indexPatternListRegistry);
|
||||
return indexPatternListProvider;
|
||||
};
|
||||
};
|
|
@ -42,7 +42,7 @@ export class ManagementSection {
|
|||
this.id = id;
|
||||
this.items = new IndexedArray({
|
||||
index: ['id'],
|
||||
order: ['order']
|
||||
order: ['order'],
|
||||
});
|
||||
this.visible = true;
|
||||
this.disabled = false;
|
||||
|
@ -51,13 +51,14 @@ export class ManagementSection {
|
|||
this.url = '';
|
||||
|
||||
assign(this, options);
|
||||
|
||||
}
|
||||
|
||||
get visibleItems() {
|
||||
return this.items.inOrder.filter(item => {
|
||||
const capabilityManagementSection = capabilities.get().management[this.id];
|
||||
const itemCapability = capabilityManagementSection ? capabilityManagementSection[item.id] : null;
|
||||
const itemCapability = capabilityManagementSection
|
||||
? capabilityManagementSection[item.id]
|
||||
: null;
|
||||
|
||||
return item.visible && itemCapability !== false;
|
||||
});
|
||||
|
@ -95,10 +96,10 @@ export class ManagementSection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deregisters a section
|
||||
*
|
||||
* @param {string} id
|
||||
*/
|
||||
* Deregisters a section
|
||||
*
|
||||
* @param {string} id
|
||||
*/
|
||||
deregister(id) {
|
||||
this.items.remove(item => item.id === id);
|
||||
listeners.forEach(fn => fn(this.items));
|
||||
|
|
|
@ -24,10 +24,10 @@ jest.mock('ui/capabilities', () => ({
|
|||
kibana: {
|
||||
sampleFeature1: true,
|
||||
sampleFeature2: false,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
import { ManagementSection } from './section';
|
||||
|
@ -115,7 +115,9 @@ describe('ManagementSection', () => {
|
|||
|
||||
it('calls listener when item added', () => {
|
||||
let listerCalled = false;
|
||||
const listenerFn = () => { listerCalled = true; };
|
||||
const listenerFn = () => {
|
||||
listerCalled = true;
|
||||
};
|
||||
|
||||
section.addListener(listenerFn);
|
||||
section.register('about');
|
||||
|
@ -131,12 +133,12 @@ describe('ManagementSection', () => {
|
|||
section.register('about');
|
||||
});
|
||||
|
||||
it ('deregisters an existing section', () => {
|
||||
it('deregisters an existing section', () => {
|
||||
section.deregister('about');
|
||||
expect(section.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it ('allows deregistering a section more than once', () => {
|
||||
it('allows deregistering a section more than once', () => {
|
||||
section.deregister('about');
|
||||
section.deregister('about');
|
||||
expect(section.items).toHaveLength(0);
|
||||
|
@ -144,7 +146,9 @@ describe('ManagementSection', () => {
|
|||
|
||||
it('calls listener when item added', () => {
|
||||
let listerCalled = false;
|
||||
const listenerFn = () => { listerCalled = true; };
|
||||
const listenerFn = () => {
|
||||
listerCalled = true;
|
||||
};
|
||||
|
||||
section.addListener(listenerFn);
|
||||
section.deregister('about');
|
||||
|
@ -202,7 +206,9 @@ describe('ManagementSection', () => {
|
|||
});
|
||||
|
||||
it('can be ordered', () => {
|
||||
const ids = section.items.inOrder.map((i) => { return i.id; });
|
||||
const ids = section.items.inOrder.map(i => {
|
||||
return i.id;
|
||||
});
|
||||
expect(ids).toEqual(['one', 'two', 'three']);
|
||||
});
|
||||
});
|
||||
|
@ -256,20 +262,26 @@ describe('ManagementSection', () => {
|
|||
});
|
||||
|
||||
it('maintains the order', () => {
|
||||
const ids = section.visibleItems.map((i) => { return i.id; });
|
||||
const ids = section.visibleItems.map(i => {
|
||||
return i.id;
|
||||
});
|
||||
expect(ids).toEqual(['one', 'two', 'three']);
|
||||
});
|
||||
|
||||
it('does not include hidden items', () => {
|
||||
section.getSection('two').hide();
|
||||
|
||||
const ids = section.visibleItems.map((i) => { return i.id; });
|
||||
const ids = section.visibleItems.map(i => {
|
||||
return i.id;
|
||||
});
|
||||
expect(ids).toEqual(['one', 'three']);
|
||||
});
|
||||
|
||||
it('does not include visible items hidden via uiCapabilities', () => {
|
||||
section.register('sampleFeature2', { order: 4, visible: true });
|
||||
const ids = section.visibleItems.map((i) => { return i.id; });
|
||||
const ids = section.visibleItems.map(i => {
|
||||
return i.id;
|
||||
});
|
||||
expect(ids).toEqual(['one', 'two', 'three']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,15 +22,15 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
export const management = new ManagementSection('management', {
|
||||
display: i18n.translate('common.ui.management.displayName', {
|
||||
defaultMessage: 'Management'
|
||||
})
|
||||
defaultMessage: 'Management',
|
||||
}),
|
||||
});
|
||||
|
||||
management.register('data', {
|
||||
display: i18n.translate('common.ui.management.connectDataDisplayName', {
|
||||
defaultMessage: 'Connect Data'
|
||||
defaultMessage: 'Connect Data',
|
||||
}),
|
||||
order: 0
|
||||
order: 0,
|
||||
});
|
||||
|
||||
management.register('elasticsearch', {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { addIndexPatternType } from 'ui/management/index_pattern_creation';
|
||||
import { setup as managementSetup } from '../../../../../../src/legacy/core_plugins/management/public/legacy';
|
||||
import { RollupIndexPatternCreationConfig } from './rollup_index_pattern_creation_config';
|
||||
|
||||
export function initIndexPatternCreation() {
|
||||
addIndexPatternType(RollupIndexPatternCreationConfig);
|
||||
managementSetup.indexPattern.creation.add(RollupIndexPatternCreationConfig);
|
||||
}
|
||||
|
|
|
@ -5,32 +5,44 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { IndexPatternCreationConfig } from 'ui/management/index_pattern_creation';
|
||||
import { IndexPatternCreationConfig } from '../../../../../../src/legacy/core_plugins/management/public';
|
||||
|
||||
import { RollupPrompt } from './components/rollup_prompt';
|
||||
import { setHttpClient, getRollupIndices } from '../services/api';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const rollupIndexPatternTypeName = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultTypeName',
|
||||
{ defaultMessage: 'rollup index pattern' });
|
||||
const rollupIndexPatternTypeName = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultTypeName',
|
||||
{ defaultMessage: 'rollup index pattern' }
|
||||
);
|
||||
|
||||
const rollupIndexPatternButtonText = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultButtonText',
|
||||
{ defaultMessage: 'Rollup index pattern' });
|
||||
const rollupIndexPatternButtonText = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultButtonText',
|
||||
{ defaultMessage: 'Rollup index pattern' }
|
||||
);
|
||||
|
||||
const rollupIndexPatternButtonDescription = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultButtonDescription',
|
||||
{ defaultMessage: 'Perform limited aggregations against summarized data' });
|
||||
const rollupIndexPatternButtonDescription = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultButtonDescription',
|
||||
{ defaultMessage: 'Perform limited aggregations against summarized data' }
|
||||
);
|
||||
|
||||
const rollupIndexPatternNoMatchError = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.noMatchError',
|
||||
{ defaultMessage: 'Rollup index pattern error: must match one rollup index' });
|
||||
const rollupIndexPatternNoMatchError = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.noMatchError',
|
||||
{ defaultMessage: 'Rollup index pattern error: must match one rollup index' }
|
||||
);
|
||||
|
||||
const rollupIndexPatternTooManyMatchesError = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.tooManyMatchesError',
|
||||
{ defaultMessage: 'Rollup index pattern error: can only match one rollup index' });
|
||||
const rollupIndexPatternTooManyMatchesError = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.tooManyMatchesError',
|
||||
{ defaultMessage: 'Rollup index pattern error: can only match one rollup index' }
|
||||
);
|
||||
|
||||
const rollupIndexPatternIndexLabel = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.indexLabel',
|
||||
{ defaultMessage: 'Rollup' });
|
||||
const rollupIndexPatternIndexLabel = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.indexLabel',
|
||||
{ defaultMessage: 'Rollup' }
|
||||
);
|
||||
|
||||
export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig {
|
||||
static key = 'rollup';
|
||||
key = 'rollup';
|
||||
|
||||
constructor(options) {
|
||||
super({
|
||||
|
@ -60,76 +72,85 @@ export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig
|
|||
|
||||
async getIndexPatternCreationOption(urlHandler) {
|
||||
await this.settingUp;
|
||||
return this.rollupIndices && this.rollupIndices.length ? {
|
||||
text: rollupIndexPatternButtonText,
|
||||
description: rollupIndexPatternButtonDescription,
|
||||
testSubj: `createRollupIndexPatternButton`,
|
||||
isBeta: this.isBeta,
|
||||
onClick: () => {
|
||||
urlHandler('/management/kibana/index_pattern?type=rollup');
|
||||
},
|
||||
} : null;
|
||||
return this.rollupIndices && this.rollupIndices.length
|
||||
? {
|
||||
text: rollupIndexPatternButtonText,
|
||||
description: rollupIndexPatternButtonDescription,
|
||||
testSubj: `createRollupIndexPatternButton`,
|
||||
isBeta: this.isBeta,
|
||||
onClick: () => {
|
||||
urlHandler('/management/kibana/index_pattern?type=rollup');
|
||||
},
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
isRollupIndex = (indexName) => {
|
||||
isRollupIndex = indexName => {
|
||||
return this.rollupIndices.includes(indexName);
|
||||
}
|
||||
};
|
||||
|
||||
getIndexTags(indexName) {
|
||||
return this.isRollupIndex(indexName) ? [{
|
||||
key: this.type,
|
||||
name: rollupIndexPatternIndexLabel,
|
||||
}] : [];
|
||||
return this.isRollupIndex(indexName)
|
||||
? [
|
||||
{
|
||||
key: this.type,
|
||||
name: rollupIndexPatternIndexLabel,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
checkIndicesForErrors = (indices) => {
|
||||
checkIndicesForErrors = indices => {
|
||||
this.rollupIndex = null;
|
||||
|
||||
if(!indices || !indices.length) {
|
||||
if (!indices || !indices.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rollupIndices = indices.filter(index => this.isRollupIndex(index.name));
|
||||
|
||||
if(!rollupIndices.length) {
|
||||
if (!rollupIndices.length) {
|
||||
return [rollupIndexPatternNoMatchError];
|
||||
} else if(rollupIndices.length > 1) {
|
||||
} else if (rollupIndices.length > 1) {
|
||||
return [rollupIndexPatternTooManyMatchesError];
|
||||
}
|
||||
|
||||
const rollupIndexName = rollupIndices[0].name;
|
||||
const error = this.rollupIndicesCapabilities[rollupIndexName].error;
|
||||
|
||||
if(error) {
|
||||
const errorMessage = i18n.translate('xpack.rollupJobs.editRollupIndexPattern.createIndex.uncaughtError', {
|
||||
defaultMessage: 'Rollup index pattern error: {error}',
|
||||
values: {
|
||||
error
|
||||
if (error) {
|
||||
const errorMessage = i18n.translate(
|
||||
'xpack.rollupJobs.editRollupIndexPattern.createIndex.uncaughtError',
|
||||
{
|
||||
defaultMessage: 'Rollup index pattern error: {error}',
|
||||
values: {
|
||||
error,
|
||||
},
|
||||
}
|
||||
});
|
||||
);
|
||||
return [errorMessage];
|
||||
}
|
||||
|
||||
this.rollupIndex = rollupIndexName;
|
||||
}
|
||||
};
|
||||
|
||||
getIndexPatternMappings = () => {
|
||||
return this.rollupIndex ? {
|
||||
type: this.type,
|
||||
typeMeta: {
|
||||
params: {
|
||||
rollup_index: this.rollupIndex,
|
||||
return this.rollupIndex
|
||||
? {
|
||||
type: this.type,
|
||||
typeMeta: {
|
||||
params: {
|
||||
rollup_index: this.rollupIndex,
|
||||
},
|
||||
aggs: this.rollupIndicesCapabilities[this.rollupIndex].aggs,
|
||||
},
|
||||
aggs: this.rollupIndicesCapabilities[this.rollupIndex].aggs,
|
||||
},
|
||||
} : {};
|
||||
}
|
||||
}
|
||||
: {};
|
||||
};
|
||||
|
||||
renderPrompt = () => {
|
||||
return (
|
||||
<RollupPrompt />
|
||||
);
|
||||
}
|
||||
return <RollupPrompt />;
|
||||
};
|
||||
|
||||
getFetchForWildcardOptions = () => {
|
||||
return {
|
||||
|
@ -138,5 +159,5 @@ export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig
|
|||
rollup_index: this.rollupIndex,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IndexPatternListConfigRegistry } from 'ui/management/index_pattern_list';
|
||||
import { setup as managementSetup } from '../../../../../../src/legacy/core_plugins/management/public/legacy';
|
||||
import { RollupIndexPatternListConfig } from './rollup_index_pattern_list_config';
|
||||
|
||||
export function initIndexPatternList() {
|
||||
IndexPatternListConfigRegistry.register(() => RollupIndexPatternListConfig);
|
||||
managementSetup.indexPattern.list.add(RollupIndexPatternListConfig);
|
||||
}
|
||||
|
|
|
@ -3,49 +3,59 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { IndexPatternListConfig } from 'ui/management/index_pattern_list';
|
||||
import { IndexPatternListConfig } from '../../../../../../src/legacy/core_plugins/management/public';
|
||||
|
||||
function isRollup(indexPattern) {
|
||||
return indexPattern.type === 'rollup' || (indexPattern.get && indexPattern.get('type') === 'rollup');
|
||||
return (
|
||||
indexPattern.type === 'rollup' || (indexPattern.get && indexPattern.get('type') === 'rollup')
|
||||
);
|
||||
}
|
||||
|
||||
export class RollupIndexPatternListConfig extends IndexPatternListConfig {
|
||||
static key = 'rollup';
|
||||
key = 'rollup';
|
||||
|
||||
getIndexPatternTags = (indexPattern) => {
|
||||
return isRollup(indexPattern) ? [{
|
||||
key: 'rollup',
|
||||
name: 'Rollup',
|
||||
}] : [];
|
||||
}
|
||||
getIndexPatternTags = indexPattern => {
|
||||
return isRollup(indexPattern)
|
||||
? [
|
||||
{
|
||||
key: 'rollup',
|
||||
name: 'Rollup',
|
||||
},
|
||||
]
|
||||
: [];
|
||||
};
|
||||
|
||||
getFieldInfo = (indexPattern, field) => {
|
||||
if(!isRollup(indexPattern)) {
|
||||
if (!isRollup(indexPattern)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const allAggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs;
|
||||
const fieldAggs = allAggs && Object.keys(allAggs).filter(agg => allAggs[agg][field]);
|
||||
|
||||
if(!fieldAggs || !fieldAggs.length) {
|
||||
if (!fieldAggs || !fieldAggs.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ['Rollup aggregations:'].concat(fieldAggs.map(aggName => {
|
||||
const agg = allAggs[aggName][field];
|
||||
switch(aggName) {
|
||||
case 'date_histogram':
|
||||
return `${aggName} (interval: ${agg.interval}, ${agg.delay ? `delay: ${agg.delay},` : ''} ${agg.time_zone})`;
|
||||
break;
|
||||
case 'histogram':
|
||||
return `${aggName} (interval: ${agg.interval})`;
|
||||
default:
|
||||
return aggName;
|
||||
}
|
||||
}));
|
||||
}
|
||||
return ['Rollup aggregations:'].concat(
|
||||
fieldAggs.map(aggName => {
|
||||
const agg = allAggs[aggName][field];
|
||||
switch (aggName) {
|
||||
case 'date_histogram':
|
||||
return `${aggName} (interval: ${agg.interval}, ${
|
||||
agg.delay ? `delay: ${agg.delay},` : ''
|
||||
} ${agg.time_zone})`;
|
||||
break;
|
||||
case 'histogram':
|
||||
return `${aggName} (interval: ${agg.interval})`;
|
||||
default:
|
||||
return aggName;
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
areScriptedFieldsEnabled = (indexPattern) => {
|
||||
areScriptedFieldsEnabled = indexPattern => {
|
||||
return !isRollup(indexPattern);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
let httpClient;
|
||||
export const setHttpClient = (client) => {
|
||||
export const setHttpClient = client => {
|
||||
httpClient = client;
|
||||
};
|
||||
const apiPrefix = chrome.addBasePath('/api/rollup');
|
||||
|
||||
export async function getRollupIndices() {
|
||||
const response = await httpClient.get(`${apiPrefix}/indices`);
|
||||
return response.data || {};
|
||||
const response = await httpClient.get('/api/rollup/indices');
|
||||
return response || {};
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import {
|
||||
SavedObjectsManagementAction,
|
||||
SavedObjectsManagementRecord,
|
||||
} from 'ui/management/saved_objects_management';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
} from '../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { CopySavedObjectsToSpaceFlyout } from '../../views/management/components/copy_saved_objects_to_space';
|
||||
import { Space } from '../../../common/model/space';
|
||||
import { SpacesManager } from '../spaces_manager';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { summarizeCopyResult } from './summarize_copy_result';
|
||||
import { ProcessedImportResponse } from 'ui/management/saved_objects_management';
|
||||
import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public';
|
||||
|
||||
const createSavedObjectsManagementRecord = () => ({
|
||||
type: 'dashboard',
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ProcessedImportResponse,
|
||||
SavedObjectsManagementRecord,
|
||||
} from 'ui/management/saved_objects_management';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { ProcessedImportResponse } from '../../../../../../../src/legacy/core_plugins/management/public';
|
||||
|
||||
export interface SummarizedSavedObjectResult {
|
||||
type: string;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { EventEmitter } from 'events';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { SavedObjectsManagementRecord } from 'ui/management/saved_objects_management';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { Space } from '../../common/model/space';
|
||||
import { GetSpacePurpose } from '../../common/model/types';
|
||||
import { CopySavedObjectsToSpaceResponse } from './copy_saved_objects_to_space/types';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import React from 'react';
|
||||
import Boom from 'boom';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { mockManagementPlugin } from '../../../../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks';
|
||||
import { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout';
|
||||
import { CopyToSpaceForm } from './copy_to_space_form';
|
||||
import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
@ -18,6 +19,11 @@ import { spacesManagerMock } from '../../../../lib/mocks';
|
|||
import { SpacesManager } from '../../../../lib';
|
||||
import { ToastNotifications } from 'ui/notify/toasts/toast_notifications';
|
||||
|
||||
jest.mock('../../../../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({
|
||||
setup: mockManagementPlugin.createSetupContract(),
|
||||
start: mockManagementPlugin.createStartContract(),
|
||||
}));
|
||||
|
||||
interface SetupOpts {
|
||||
mockSpaces?: Space[];
|
||||
returnBeforeSpacesLoad?: boolean;
|
||||
|
|
|
@ -21,12 +21,12 @@ import {
|
|||
import { mapValues } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
SavedObjectsManagementRecord,
|
||||
processImportResponse,
|
||||
ProcessedImportResponse,
|
||||
} from 'ui/management/saved_objects_management';
|
||||
import { ToastNotifications } from 'ui/notify/toasts/toast_notifications';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import {
|
||||
ProcessedImportResponse,
|
||||
processImportResponse,
|
||||
} from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { Space } from '../../../../../common/model/space';
|
||||
import { SpacesManager } from '../../../../lib';
|
||||
import { ProcessingCopyToSpace } from './processing_copy_to_space';
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import { ProcessedImportResponse } from 'ui/management/saved_objects_management';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiStat, EuiHorizontalRule } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ProcessedImportResponse } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
ProcessedImportResponse,
|
||||
SavedObjectsManagementRecord,
|
||||
} from 'ui/management/saved_objects_management';
|
||||
import {
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
|
@ -17,6 +13,8 @@ import {
|
|||
EuiHorizontalRule,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { ProcessedImportResponse } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { summarizeCopyResult } from '../../../../lib/copy_saved_objects_to_space';
|
||||
import { Space } from '../../../../../common/model/space';
|
||||
import { CopyOptions, ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import { SavedObjectsManagementRecord } from 'ui/management/saved_objects_management';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { SummarizedCopyToSpaceResult } from '../../../../lib/copy_saved_objects_to_space';
|
||||
import { SpaceAvatar } from '../../../../components';
|
||||
import { Space } from '../../../../../common/model/space';
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { SavedObjectsManagementRecord } from 'ui/management/saved_objects_management';
|
||||
import { SummarizedCopyToSpaceResult } from 'plugins/spaces/lib/copy_saved_objects_to_space';
|
||||
import { EuiText, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { SavedObjectsManagementRecord } from '../../../../../../../../../src/legacy/core_plugins/management/public';
|
||||
import { Space } from '../../../../../common/model/space';
|
||||
import { CopyStatusIndicator } from './copy_status_indicator';
|
||||
import { ImportRetry } from '../../../../lib/copy_saved_objects_to_space/types';
|
||||
|
|
|
@ -12,9 +12,9 @@ import {
|
|||
PAGE_TITLE_COMPONENT,
|
||||
registerSettingsComponent,
|
||||
} from 'ui/management';
|
||||
import { SavedObjectsManagementActionRegistry } from 'ui/management/saved_objects_management';
|
||||
// @ts-ignore
|
||||
import routes from 'ui/routes';
|
||||
import { setup as managementSetup } from '../../../../../../../src/legacy/core_plugins/management/public/legacy';
|
||||
import { SpacesManager } from '../../lib';
|
||||
import { AdvancedSettingsSubtitle } from './components/advanced_settings_subtitle';
|
||||
import { AdvancedSettingsTitle } from './components/advanced_settings_title';
|
||||
|
@ -54,8 +54,8 @@ routes.defaults(/\/management/, {
|
|||
);
|
||||
// This route resolve function executes any time the management screen is loaded, and we want to ensure
|
||||
// that this action is only registered once.
|
||||
if (!SavedObjectsManagementActionRegistry.has(action.id)) {
|
||||
SavedObjectsManagementActionRegistry.register(action);
|
||||
if (!managementSetup.savedObjects.registry.has(action.id)) {
|
||||
managementSetup.savedObjects.registry.register(action);
|
||||
}
|
||||
|
||||
// Customize Advanced Settings
|
||||
|
|
|
@ -463,9 +463,9 @@
|
|||
"common.ui.management.breadcrumb": "管理",
|
||||
"common.ui.management.connectDataDisplayName": "データに接続",
|
||||
"common.ui.management.displayName": "管理",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン",
|
||||
"management.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行",
|
||||
"management.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン",
|
||||
"management.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン",
|
||||
"common.ui.management.nav.menu": "管理メニュー",
|
||||
"common.ui.modals.cancelButtonLabel": "キャンセル",
|
||||
"common.ui.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}",
|
||||
|
|
|
@ -463,9 +463,9 @@
|
|||
"common.ui.management.breadcrumb": "管理",
|
||||
"common.ui.management.connectDataDisplayName": "连接数据",
|
||||
"common.ui.management.displayName": "管理",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultButtonText": "标准索引模式",
|
||||
"common.ui.management.editIndexPattern.createIndex.defaultTypeName": "索引模式",
|
||||
"management.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合",
|
||||
"management.editIndexPattern.createIndex.defaultButtonText": "标准索引模式",
|
||||
"management.editIndexPattern.createIndex.defaultTypeName": "索引模式",
|
||||
"common.ui.management.nav.menu": "管理菜单",
|
||||
"common.ui.modals.cancelButtonLabel": "取消",
|
||||
"common.ui.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}",
|
||||
|
|
|
@ -153,10 +153,10 @@ export default async function ({ readConfigFile }) {
|
|||
pathname: '/app/uptime',
|
||||
},
|
||||
apm: {
|
||||
pathname: '/app/apm'
|
||||
pathname: '/app/apm',
|
||||
},
|
||||
ml: {
|
||||
pathname: '/app/ml'
|
||||
pathname: '/app/ml',
|
||||
},
|
||||
rollupJob: {
|
||||
pathname: '/app/kibana',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue