diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 0c4930592b233..959ffaa7e7e08 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -48,6 +48,7 @@ export { overlayServiceMock } from './overlays/overlay_service.mock'; export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; export { scopedHistoryMock } from './application/scoped_history.mock'; +export { applicationServiceMock } from './application/application_service.mock'; function createCoreSetupMock({ basePath = '', @@ -62,9 +63,8 @@ function createCoreSetupMock({ application: applicationServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), fatalErrors: fatalErrorsServiceMock.createSetupContract(), - getStartServices: jest.fn, object, any]>, []>( - () => - Promise.resolve([createCoreStartMock({ basePath }), pluginStartDeps, pluginStartContract]) + getStartServices: jest.fn, any, any]>, []>(() => + Promise.resolve([createCoreStartMock({ basePath }), pluginStartDeps, pluginStartContract]) ), http: httpServiceMock.createSetupContract({ basePath }), notifications: notificationServiceMock.createSetupContract(), diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts index f44824238aa21..9f625b4732e26 100644 --- a/src/core/server/saved_objects/service/index.ts +++ b/src/core/server/saved_objects/service/index.ts @@ -36,7 +36,6 @@ export interface SavedObjectsLegacyService { getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; SavedObjectsClient: typeof SavedObjectsClient; types: string[]; - importAndExportableTypes: string[]; schema: SavedObjectsSchema; getSavedObjectsRepository(...rest: any[]): any; importExport: { diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a35bca7375286..37051da4b17da 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2084,8 +2084,6 @@ export interface SavedObjectsLegacyService { // (undocumented) getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; // (undocumented) - importAndExportableTypes: string[]; - // (undocumented) importExport: { objectLimit: number; importSavedObjects(options: SavedObjectsImportOptions): Promise; diff --git a/src/legacy/core_plugins/kibana/inject_vars.js b/src/legacy/core_plugins/kibana/inject_vars.js index 76d1704907ab5..c3b906ee842e3 100644 --- a/src/legacy/core_plugins/kibana/inject_vars.js +++ b/src/legacy/core_plugins/kibana/inject_vars.js @@ -20,10 +20,7 @@ export function injectVars(server) { const serverConfig = server.config(); - const { importAndExportableTypes } = server.savedObjects; - return { - importAndExportableTypes, autocompleteTerminateAfter: serverConfig.get('kibana.autocompleteTerminateAfter'), autocompleteTimeout: serverConfig.get('kibana.autocompleteTimeout'), }; diff --git a/src/legacy/core_plugins/kibana/public/index.ts b/src/legacy/core_plugins/kibana/public/index.ts index a4fffc6eec26d..be22652ab2c1c 100644 --- a/src/legacy/core_plugins/kibana/public/index.ts +++ b/src/legacy/core_plugins/kibana/public/index.ts @@ -20,4 +20,4 @@ export { ProcessedImportResponse, processImportResponse, -} from './management/sections/objects/lib/process_import_response'; +} from '../../../../plugins/saved_objects_management/public/lib'; diff --git a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts index 705be68a141e7..587a372f91555 100644 --- a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts +++ b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts @@ -17,66 +17,8 @@ * under the License. */ -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { npStart } from 'ui/new_platform'; -import { SavedObjectLoader } from '../../../../../plugins/saved_objects/public'; -import { createSavedSearchesLoader } from '../../../../../plugins/discover/public'; +import { npSetup } from 'ui/new_platform'; -/** - * This registry is used for the editing mode of Saved Searches, Visualizations, - * Dashboard and Time Lion saved objects. - */ -interface SavedObjectRegistryEntry { - id: string; - service: SavedObjectLoader; - title: string; -} - -export interface ISavedObjectsManagementRegistry { - register(service: SavedObjectRegistryEntry): void; - all(): SavedObjectRegistryEntry[]; - get(id: string): SavedObjectRegistryEntry | undefined; -} - -const registry: SavedObjectRegistryEntry[] = []; - -export const savedObjectManagementRegistry: ISavedObjectsManagementRegistry = { - register: (service: SavedObjectRegistryEntry) => { - registry.push(service); - }, - all: () => { - return registry; - }, - get: (id: string) => { - return _.find(registry, { id }); - }, -}; - -const services = { - savedObjectsClient: npStart.core.savedObjects.client, - indexPatterns: npStart.plugins.data.indexPatterns, - search: npStart.plugins.data.search, - chrome: npStart.core.chrome, - overlays: npStart.core.overlays, -}; - -savedObjectManagementRegistry.register({ - id: 'savedVisualizations', - service: npStart.plugins.visualizations.savedVisualizationsLoader, - title: 'visualizations', -}); - -savedObjectManagementRegistry.register({ - id: 'savedDashboards', - service: npStart.plugins.dashboard.getSavedDashboardLoader(), - title: i18n.translate('kbn.dashboard.savedDashboardsTitle', { - defaultMessage: 'dashboards', - }), -}); +const registry = npSetup.plugins.savedObjectsManagement?.serviceRegistry; -savedObjectManagementRegistry.register({ - id: 'savedSearches', - service: createSavedSearchesLoader(services), - title: 'searches', -}); +export const savedObjectManagementRegistry = registry!; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index.js index 54717ad003ade..adc1741f57263 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index.js @@ -17,5 +17,4 @@ * under the License. */ -import './objects'; import './index_patterns'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.html b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.html deleted file mode 100644 index 090fb7b636685..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.html +++ /dev/null @@ -1,5 +0,0 @@ - - -
-
-
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js deleted file mode 100644 index c5901ca6ee6bf..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js +++ /dev/null @@ -1,104 +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 { savedObjectManagementRegistry } from '../../saved_object_registry'; -import objectIndexHTML from './_objects.html'; -import uiRoutes from 'ui/routes'; -import chrome from 'ui/chrome'; -import { uiModules } from 'ui/modules'; -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { ObjectsTable } from './components/objects_table'; -import { I18nContext } from 'ui/i18n'; -import { get } from 'lodash'; -import { npStart } from 'ui/new_platform'; -import { getIndexBreadcrumbs } from './breadcrumbs'; - -const REACT_OBJECTS_TABLE_DOM_ELEMENT_ID = 'reactSavedObjectsTable'; - -function updateObjectsTable($scope, $injector) { - const indexPatterns = npStart.plugins.data.indexPatterns; - const $http = $injector.get('$http'); - const kbnUrl = $injector.get('kbnUrl'); - const config = $injector.get('config'); - - const savedObjectsClient = npStart.core.savedObjects.client; - const services = savedObjectManagementRegistry.all().map(obj => obj.service); - const uiCapabilites = npStart.core.application.capabilities; - - $scope.$$postDigest(() => { - const node = document.getElementById(REACT_OBJECTS_TABLE_DOM_ELEMENT_ID); - if (!node) { - return; - } - - render( - - { - if (object.meta.editUrl) { - kbnUrl.change(object.meta.editUrl); - $scope.$apply(); - } - }} - canGoInApp={object => { - const { inAppUrl } = object.meta; - return inAppUrl && get(uiCapabilites, inAppUrl.uiCapabilitiesPath); - }} - /> - , - node - ); - }); -} - -function destroyObjectsTable() { - const node = document.getElementById(REACT_OBJECTS_TABLE_DOM_ELEMENT_ID); - node && unmountComponentAtNode(node); -} - -uiRoutes - .when('/management/kibana/objects', { - template: objectIndexHTML, - k7Breadcrumbs: getIndexBreadcrumbs, - requireUICapability: 'management.kibana.objects', - }) - .when('/management/kibana/objects/:service', { - redirectTo: '/management/kibana/objects', - }); - -uiModules.get('apps/management').directive('kbnManagementObjects', function() { - return { - restrict: 'E', - controllerAs: 'managementObjectsController', - controller: function($scope, $injector) { - updateObjectsTable($scope, $injector); - $scope.$on('$destroy', destroyObjectsTable); - }, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.html b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.html deleted file mode 100644 index 8bce0aabcd64a..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.html +++ /dev/null @@ -1,5 +0,0 @@ - - -
-
-
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js deleted file mode 100644 index a847055b40015..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js +++ /dev/null @@ -1,85 +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 React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import 'angular'; -import 'angular-elastic/elastic'; -import uiRoutes from 'ui/routes'; -import { uiModules } from 'ui/modules'; -import { I18nContext } from 'ui/i18n'; -import { npStart } from 'ui/new_platform'; -import objectViewHTML from './_view.html'; -import { getViewBreadcrumbs } from './breadcrumbs'; -import { savedObjectManagementRegistry } from '../../saved_object_registry'; -import { SavedObjectEdition } from './saved_object_view'; - -const REACT_OBJECTS_VIEW_DOM_ELEMENT_ID = 'reactSavedObjectsView'; - -uiRoutes.when('/management/kibana/objects/:service/:id', { - template: objectViewHTML, - k7Breadcrumbs: getViewBreadcrumbs, - requireUICapability: 'management.kibana.objects', -}); - -function createReactView($scope, $routeParams) { - const { service: serviceName, id: objectId, notFound } = $routeParams; - - const { savedObjects, overlays, notifications, application } = npStart.core; - - $scope.$$postDigest(() => { - const node = document.getElementById(REACT_OBJECTS_VIEW_DOM_ELEMENT_ID); - if (!node) { - return; - } - - render( - - - , - node - ); - }); -} - -function destroyReactView() { - const node = document.getElementById(REACT_OBJECTS_VIEW_DOM_ELEMENT_ID); - node && unmountComponentAtNode(node); -} - -uiModules - .get('apps/management', ['monospaced.elastic']) - .directive('kbnManagementObjectsView', function() { - return { - restrict: 'E', - controller: function($scope, $routeParams) { - createReactView($scope, $routeParams); - $scope.$on('$destroy', destroyReactView); - }, - }; - }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/breadcrumbs.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/breadcrumbs.js deleted file mode 100644 index e9082bfeb680d..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/breadcrumbs.js +++ /dev/null @@ -1,50 +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 { MANAGEMENT_BREADCRUMB } from 'ui/management'; -import { i18n } from '@kbn/i18n'; - -import { savedObjectManagementRegistry } from '../../saved_object_registry'; - -export function getIndexBreadcrumbs() { - return [ - MANAGEMENT_BREADCRUMB, - { - text: i18n.translate('kbn.management.savedObjects.indexBreadcrumb', { - defaultMessage: 'Saved objects', - }), - href: '#/management/kibana/objects', - }, - ]; -} - -export function getViewBreadcrumbs($routeParams) { - const serviceObj = savedObjectManagementRegistry.get($routeParams.service); - const { service } = serviceObj; - - return [ - ...getIndexBreadcrumbs(), - { - text: i18n.translate('kbn.management.savedObjects.editBreadcrumb', { - defaultMessage: 'Edit {savedObjectType}', - values: { savedObjectType: service.type }, - }), - }, - ]; -} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/index.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/index.js deleted file mode 100644 index 601dea544361c..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/index.js +++ /dev/null @@ -1,20 +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. - */ - -export { ObjectsTable } from './objects_table'; diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.js b/src/legacy/server/saved_objects/saved_objects_mixin.js index bcf766231dc9c..3e71e1989ae7a 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.js @@ -78,13 +78,8 @@ export function savedObjectsMixin(kbnServer, server) { const provider = kbnServer.newPlatform.__internals.savedObjectsClientProvider; - const importAndExportableTypes = typeRegistry - .getImportableAndExportableTypes() - .map(type => type.name); - const service = { types: visibleTypes, - importAndExportableTypes, SavedObjectsClient, SavedObjectsRepository, getSavedObjectsRepository: createRepository, diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index ea1c27550867e..2d43cae79ac98 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -62,6 +62,7 @@ const createStartContract = (): Start => { }, }), get: jest.fn().mockReturnValue(Promise.resolve({})), + clearCache: jest.fn(), } as unknown) as IndexPatternsContract, }; }; diff --git a/src/plugins/discover/public/mocks.ts b/src/plugins/discover/public/mocks.ts index bb05e3d412001..218c59b5db07b 100644 --- a/src/plugins/discover/public/mocks.ts +++ b/src/plugins/discover/public/mocks.ts @@ -37,6 +37,9 @@ const createStartContract = (): Start => { docViews: { DocViewer: jest.fn(() => null), }, + savedSearches: { + createLoader: jest.fn(), + }, }; return startContract; }; diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index d2797586bfdfb..aa54823e6ec4d 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -21,12 +21,14 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { auto } from 'angular'; import { CoreSetup, Plugin } from 'kibana/public'; +import { SavedObjectLoader, SavedObjectKibanaServices } from '../../saved_objects/public'; import { DocViewInput, DocViewInputFn, DocViewRenderProps } from './doc_views/doc_views_types'; import { DocViewsRegistry } from './doc_views/doc_views_registry'; import { DocViewTable } from './components/table/table'; import { JsonCodeBlock } from './components/json_code_block/json_code_block'; import { DocViewer } from './components/doc_viewer/doc_viewer'; import { setDocViewsRegistry } from './services'; +import { createSavedSearchesLoader } from './saved_searches'; import './index.scss'; @@ -62,6 +64,13 @@ export interface DiscoverStart { */ DocViewer: React.ComponentType; }; + savedSearches: { + /** + * Create a {@link SavedObjectLoader | loader} to handle the saved searches type. + * @param services + */ + createLoader(services: SavedObjectKibanaServices): SavedObjectLoader; + }; } /** @@ -105,6 +114,9 @@ export class DiscoverPlugin implements Plugin { docViews: { DocViewer, }, + savedSearches: { + createLoader: createSavedSearchesLoader, + }, }; } } diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index 6099a2cc32afc..82789d3c3f55f 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -18,12 +18,21 @@ */ import { ManagementSetup, ManagementStart } from '../types'; +import { ManagementSection } from '../management_section'; + +const createManagementSectionMock = (): jest.Mocked> => { + return { + registerApp: jest.fn(), + getApp: jest.fn(), + getAppsEnabled: jest.fn().mockReturnValue([]), + }; +}; const createSetupContract = (): DeeplyMockedKeys => ({ sections: { register: jest.fn(), - getSection: jest.fn(), - getAllSections: jest.fn(), + getSection: jest.fn().mockReturnValue(createManagementSectionMock()), + getAllSections: jest.fn().mockReturnValue([]), }, }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/index.js b/src/plugins/saved_objects_management/common/index.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/index.js rename to src/plugins/saved_objects_management/common/index.ts index ac1e7bac06c87..67c3ae6d934ab 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/index.js +++ b/src/plugins/saved_objects_management/common/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Header } from './header'; +export { SavedObjectRelation, SavedObjectWithMetadata, SavedObjectMetadata } from './types'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/types.ts b/src/plugins/saved_objects_management/common/types.ts similarity index 74% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/types.ts rename to src/plugins/saved_objects_management/common/types.ts index 6a89142bc9798..be52d8e6486e2 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/types.ts +++ b/src/plugins/saved_objects_management/common/types.ts @@ -17,8 +17,12 @@ * under the License. */ -import { SavedObject, SavedObjectReference } from 'src/core/public'; +import { SavedObject } from 'src/core/types'; +/** + * The metadata injected into a {@link SavedObject | saved object} when returning + * {@link SavedObjectWithMetadata | enhanced objects} from the plugin API endpoints. + */ export interface SavedObjectMetadata { icon?: string; title?: string; @@ -26,31 +30,19 @@ export interface SavedObjectMetadata { inAppUrl?: { path: string; uiCapabilitiesPath: string }; } +/** + * A {@link SavedObject | saved object} enhanced with meta properties used by the client-side plugin. + */ export type SavedObjectWithMetadata = SavedObject & { meta: SavedObjectMetadata; }; +/** + * Represents a relation between two {@link SavedObject | saved object} + */ export interface SavedObjectRelation { id: string; type: string; relationship: 'child' | 'parent'; meta: SavedObjectMetadata; } - -export interface ObjectField { - type: FieldType; - name: string; - value: any; -} - -export type FieldType = 'text' | 'number' | 'boolean' | 'array' | 'json'; - -export interface FieldState { - value?: any; - invalid?: boolean; -} - -export interface SubmittedFormData { - attributes: any; - references: SavedObjectReference[]; -} diff --git a/src/plugins/saved_objects_management/kibana.json b/src/plugins/saved_objects_management/kibana.json index e1f14b0e3c59d..22135ce4558ae 100644 --- a/src/plugins/saved_objects_management/kibana.json +++ b/src/plugins/saved_objects_management/kibana.json @@ -3,5 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["home"] + "requiredPlugins": ["home", "management", "data"], + "optionalPlugins": ["dashboard", "visualizations", "discover"] } diff --git a/src/plugins/saved_objects_management/public/index.ts b/src/plugins/saved_objects_management/public/index.ts index 7fb2f137d7d84..b20b320bc6645 100644 --- a/src/plugins/saved_objects_management/public/index.ts +++ b/src/plugins/saved_objects_management/public/index.ts @@ -22,10 +22,14 @@ import { SavedObjectsManagementPlugin } from './plugin'; export { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart } from './plugin'; export { - ISavedObjectsManagementActionRegistry, + SavedObjectsManagementActionServiceSetup, + SavedObjectsManagementActionServiceStart, SavedObjectsManagementAction, SavedObjectsManagementRecord, + ISavedObjectsManagementServiceRegistry, + SavedObjectsManagementServiceRegistryEntry, } from './services'; +export { SavedObjectRelation, SavedObjectWithMetadata, SavedObjectMetadata } from './types'; export function plugin(initializerContext: PluginInitializerContext) { return new SavedObjectsManagementPlugin(); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.test.ts b/src/plugins/saved_objects_management/public/lib/case_conversion.test.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.test.ts rename to src/plugins/saved_objects_management/public/lib/case_conversion.test.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.ts b/src/plugins/saved_objects_management/public/lib/case_conversion.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.ts rename to src/plugins/saved_objects_management/public/lib/case_conversion.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.test.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.test.ts rename to src/plugins/saved_objects_management/public/lib/create_field_list.test.ts index 345716f91ea88..e7d6754ac4d05 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.test.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts @@ -17,8 +17,8 @@ * under the License. */ -import { SimpleSavedObject, SavedObjectReference } from '../../../../../../../../core/public'; -import { savedObjectsServiceMock } from '../../../../../../../../core/public/mocks'; +import { SimpleSavedObject, SavedObjectReference } from '../../../../core/public'; +import { savedObjectsServiceMock } from '../../../../core/public/mocks'; import { createFieldList } from './create_field_list'; const savedObjectClientMock = savedObjectsServiceMock.createStartContract().client; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.ts rename to src/plugins/saved_objects_management/public/lib/create_field_list.ts index 88a1184d5d70f..5d87c11a87198 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/create_field_list.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts @@ -18,10 +18,10 @@ */ import { forOwn, indexBy, isNumber, isBoolean, isPlainObject, isString } from 'lodash'; -import { SimpleSavedObject } from '../../../../../../../../core/public'; -import { castEsToKbnFieldTypeName } from '../../../../../../../../plugins/data/public'; -import { ObjectField } from '../types'; -import { SavedObjectLoader } from '../../../../../../../../plugins/saved_objects/public'; +import { SimpleSavedObject } from '../../../../core/public'; +import { castEsToKbnFieldTypeName } from '../../../data/public'; +import { ObjectField } from '../management_section/types'; +import { SavedObjectLoader } from '../../../saved_objects/public'; const maxRecursiveIterations = 20; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/extract_export_details.test.ts b/src/plugins/saved_objects_management/public/lib/extract_export_details.test.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/extract_export_details.test.ts rename to src/plugins/saved_objects_management/public/lib/extract_export_details.test.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/extract_export_details.ts b/src/plugins/saved_objects_management/public/lib/extract_export_details.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/extract_export_details.ts rename to src/plugins/saved_objects_management/public/lib/extract_export_details.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_by_type_and_search.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_by_type_and_search.ts rename to src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts index d3e527b9f96b7..e0f005fab2a3b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_by_type_and_search.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts @@ -17,16 +17,15 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; +import { HttpStart } from 'src/core/public'; export async function fetchExportByTypeAndSearch( + http: HttpStart, types: string[], search: string | undefined, includeReferencesDeep: boolean = false ): Promise { - return await kfetch({ - method: 'POST', - pathname: '/api/saved_objects/_export', + return http.post('/api/saved_objects/_export', { body: JSON.stringify({ type: types, search, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_objects.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_objects.ts rename to src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts index 744f8ef38af47..745d3758371a3 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/fetch_export_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts @@ -17,15 +17,14 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; +import { HttpStart } from 'src/core/public'; export async function fetchExportObjects( + http: HttpStart, objects: any[], includeReferencesDeep: boolean = false ): Promise { - return await kfetch({ - method: 'POST', - pathname: '/api/saved_objects/_export', + return http.post('/api/saved_objects/_export', { body: JSON.stringify({ objects, includeReferencesDeep, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.ts b/src/plugins/saved_objects_management/public/lib/find_objects.ts similarity index 57% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.ts rename to src/plugins/saved_objects_management/public/lib/find_objects.ts index 24e08f0524f62..5a77d3ae2f663 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/find_objects.ts @@ -17,16 +17,27 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; -import { SavedObjectsFindOptions } from 'src/core/public'; +import { HttpStart, SavedObjectsFindOptions } from 'src/core/public'; import { keysToCamelCaseShallow } from './case_conversion'; +import { SavedObjectWithMetadata } from '../types'; -export async function findObjects(findOptions: SavedObjectsFindOptions) { - const response = await kfetch({ - method: 'GET', - pathname: '/api/kibana/management/saved_objects/_find', - query: findOptions as Record, - }); +interface SavedObjectsFindResponse { + total: number; + page: number; + perPage: number; + savedObjects: SavedObjectWithMetadata[]; +} + +export async function findObjects( + http: HttpStart, + findOptions: SavedObjectsFindOptions +): Promise { + const response = await http.get>( + '/api/kibana/management/saved_objects/_find', + { + query: findOptions as Record, + } + ); - return keysToCamelCaseShallow(response); + return keysToCamelCaseShallow(response) as SavedObjectsFindResponse; } diff --git a/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts b/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts new file mode 100644 index 0000000000000..7d952ebf2ca14 --- /dev/null +++ b/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts @@ -0,0 +1,31 @@ +/* + * 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 { HttpStart } from 'src/core/public'; + +interface GetAllowedTypesResponse { + types: string[]; +} + +export async function getAllowedTypes(http: HttpStart) { + const response = await http.get( + '/api/kibana/management/saved_objects/_allowed_types' + ); + return response.types; +} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_default_title.ts b/src/plugins/saved_objects_management/public/lib/get_default_title.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_default_title.ts rename to src/plugins/saved_objects_management/public/lib/get_default_title.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.test.ts b/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts similarity index 67% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.test.ts rename to src/plugins/saved_objects_management/public/lib/get_relationships.test.ts index b45b51b4de293..d79447378dde5 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.test.ts +++ b/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts @@ -17,44 +17,43 @@ * under the License. */ +import { httpServiceMock } from '../../../../core/public/mocks'; import { getRelationships } from './get_relationships'; describe('getRelationships', () => { - it('should make an http request', async () => { - const $http = jest.fn() as any; - const basePath = 'test'; + let httpMock: ReturnType; - await getRelationships('dashboard', '1', ['search', 'index-pattern'], $http, basePath); - expect($http.mock.calls.length).toBe(1); + beforeEach(() => { + httpMock = httpServiceMock.createSetupContract(); + }); + + it('should make an http request', async () => { + await getRelationships(httpMock, 'dashboard', '1', ['search', 'index-pattern']); + expect(httpMock.get).toHaveBeenCalledTimes(1); }); it('should handle successful responses', async () => { - const $http = jest.fn().mockImplementation(() => ({ data: [1, 2] })) as any; - const basePath = 'test'; - - const response = await getRelationships( - 'dashboard', - '1', - ['search', 'index-pattern'], - $http, - basePath - ); + httpMock.get.mockResolvedValue([1, 2]); + + const response = await getRelationships(httpMock, 'dashboard', '1', [ + 'search', + 'index-pattern', + ]); expect(response).toEqual([1, 2]); }); it('should handle errors', async () => { - const $http = jest.fn().mockImplementation(() => { + httpMock.get.mockImplementation(() => { const err = new Error(); (err as any).data = { error: 'Test error', statusCode: 500, }; throw err; - }) as any; - const basePath = 'test'; + }); await expect( - getRelationships('dashboard', '1', ['search', 'index-pattern'], $http, basePath) + getRelationships(httpMock, 'dashboard', '1', ['search', 'index-pattern']) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Test error"`); }); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.ts b/src/plugins/saved_objects_management/public/lib/get_relationships.ts similarity index 67% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.ts rename to src/plugins/saved_objects_management/public/lib/get_relationships.ts index 07bdf2db68fa2..bf2e651aa6593 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_relationships.ts +++ b/src/plugins/saved_objects_management/public/lib/get_relationships.ts @@ -17,36 +17,30 @@ * under the License. */ -import { IHttpService } from 'angular'; +import { HttpStart } from 'src/core/public'; import { get } from 'lodash'; import { SavedObjectRelation } from '../types'; export async function getRelationships( + http: HttpStart, type: string, id: string, - savedObjectTypes: string[], - $http: IHttpService, - basePath: string + savedObjectTypes: string[] ): Promise { - const url = `${basePath}/api/kibana/management/saved_objects/relationships/${encodeURIComponent( + const url = `/api/kibana/management/saved_objects/relationships/${encodeURIComponent( type )}/${encodeURIComponent(id)}`; - const options = { - method: 'GET', - url, - params: { - savedObjectTypes, - }, - }; - try { - const response = await $http(options); - return response?.data; - } catch (resp) { - const respBody = get(resp, 'data', {}) as any; - const err = new Error(respBody.message || respBody.error || `${resp.status} Response`); + return await http.get(url, { + query: { + savedObjectTypes, + }, + }); + } catch (respError) { + const respBody = get(respError, 'data', {}) as any; + const err = new Error(respBody.message || respBody.error || `${respError.status} Response`); - (err as any).statusCode = respBody.statusCode || resp.status; + (err as any).statusCode = respBody.statusCode || respError.status; (err as any).body = respBody; throw err; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_saved_object_counts.ts b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts similarity index 72% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_saved_object_counts.ts rename to src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts index d4dda1190bc43..dcf59142e73e3 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_saved_object_counts.ts +++ b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts @@ -17,18 +17,15 @@ * under the License. */ -import { IHttpService } from 'angular'; -import chrome from 'ui/chrome'; +import { HttpStart } from 'src/core/public'; -const apiBase = chrome.addBasePath('/api/kibana/management/saved_objects/scroll'); export async function getSavedObjectCounts( - $http: IHttpService, + http: HttpStart, typesToInclude: string[], - searchString: string + searchString?: string ): Promise> { - const results = await $http.post>(`${apiBase}/counts`, { - typesToInclude, - searchString, - }); - return results.data; + return await http.post>( + `/api/kibana/management/saved_objects/scroll/counts`, + { body: JSON.stringify({ typesToInclude, searchString }) } + ); } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_saved_object_label.ts b/src/plugins/saved_objects_management/public/lib/get_saved_object_label.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/get_saved_object_label.ts rename to src/plugins/saved_objects_management/public/lib/get_saved_object_label.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_file.ts b/src/plugins/saved_objects_management/public/lib/import_file.ts similarity index 75% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_file.ts rename to src/plugins/saved_objects_management/public/lib/import_file.ts index 9bd5fbeed3a4c..96263452253ba 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -17,14 +17,18 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; +import { HttpStart, SavedObjectsImportError } from 'src/core/public'; -export async function importFile(file: Blob, overwriteAll: boolean = false) { +interface ImportResponse { + success: boolean; + successCount: number; + errors?: SavedObjectsImportError[]; +} + +export async function importFile(http: HttpStart, file: File, overwriteAll: boolean = false) { const formData = new FormData(); formData.append('file', file); - return await kfetch({ - method: 'POST', - pathname: '/api/saved_objects/_import', + return await http.post('/api/saved_objects/_import', { body: formData, headers: { // Important to be undefined, it forces proper headers to be set for FormData diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_legacy_file.test.ts b/src/plugins/saved_objects_management/public/lib/import_legacy_file.test.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_legacy_file.test.ts rename to src/plugins/saved_objects_management/public/lib/import_legacy_file.test.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_legacy_file.ts b/src/plugins/saved_objects_management/public/lib/import_legacy_file.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/import_legacy_file.ts rename to src/plugins/saved_objects_management/public/lib/import_legacy_file.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/in_app_url.test.ts b/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts similarity index 98% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/in_app_url.test.ts rename to src/plugins/saved_objects_management/public/lib/in_app_url.test.ts index c0d6716391a1f..09e08e6ec333b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/in_app_url.test.ts +++ b/src/plugins/saved_objects_management/public/lib/in_app_url.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Capabilities } from '../../../../../../../../core/public'; +import { Capabilities } from '../../../../core/public'; import { canViewInApp } from './in_app_url'; const createCapabilities = (sections: Record): Capabilities => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/in_app_url.ts b/src/plugins/saved_objects_management/public/lib/in_app_url.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/in_app_url.ts rename to src/plugins/saved_objects_management/public/lib/in_app_url.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/index.ts b/src/plugins/saved_objects_management/public/lib/index.ts similarity index 94% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/index.ts rename to src/plugins/saved_objects_management/public/lib/index.ts index ecdfa6549a54e..7021744095651 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/index.ts +++ b/src/plugins/saved_objects_management/public/lib/index.ts @@ -43,3 +43,5 @@ export { export { getDefaultTitle } from './get_default_title'; export { findObjects } from './find_objects'; export { extractExportDetails, SavedObjectsExportResultDetails } from './extract_export_details'; +export { createFieldList } from './create_field_list'; +export { getAllowedTypes } from './get_allowed_types'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/log_legacy_import.ts b/src/plugins/saved_objects_management/public/lib/log_legacy_import.ts similarity index 81% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/log_legacy_import.ts rename to src/plugins/saved_objects_management/public/lib/log_legacy_import.ts index 9bbafe3e69c98..9ec3c85b91c22 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/log_legacy_import.ts +++ b/src/plugins/saved_objects_management/public/lib/log_legacy_import.ts @@ -17,11 +17,8 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; +import { HttpStart } from 'src/core/public'; -export async function logLegacyImport() { - return await kfetch({ - method: 'POST', - pathname: '/api/saved_objects/_log_legacy_import', - }); +export async function logLegacyImport(http: HttpStart) { + return http.post('/api/saved_objects/_log_legacy_import'); } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/numeric.ts b/src/plugins/saved_objects_management/public/lib/numeric.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/numeric.ts rename to src/plugins/saved_objects_management/public/lib/numeric.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.test.ts b/src/plugins/saved_objects_management/public/lib/parse_query.test.ts similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.test.ts rename to src/plugins/saved_objects_management/public/lib/parse_query.test.ts index 77b34eccd9c6f..f62234eaf4e94 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.test.ts +++ b/src/plugins/saved_objects_management/public/lib/parse_query.test.ts @@ -25,6 +25,6 @@ describe('getQueryText', () => { getTermClauses: () => [{ value: 'foo' }, { value: 'bar' }], getFieldClauses: () => [{ value: 'lala' }, { value: 'lolo' }], }; - expect(parseQuery({ ast })).toEqual({ queryText: 'foo bar', visibleTypes: 'lala' }); + expect(parseQuery({ ast } as any)).toEqual({ queryText: 'foo bar', visibleTypes: 'lala' }); }); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.ts b/src/plugins/saved_objects_management/public/lib/parse_query.ts similarity index 77% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.ts rename to src/plugins/saved_objects_management/public/lib/parse_query.ts index 9b33deedafd95..f5b7b69ea049c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/parse_query.ts +++ b/src/plugins/saved_objects_management/public/lib/parse_query.ts @@ -17,9 +17,16 @@ * under the License. */ -export function parseQuery(query: any) { - let queryText; - let visibleTypes; +import { Query } from '@elastic/eui'; + +interface ParsedQuery { + queryText?: string; + visibleTypes?: string[]; +} + +export function parseQuery(query: Query): ParsedQuery { + let queryText: string | undefined; + let visibleTypes: string[] | undefined; if (query) { if (query.ast.getTermClauses().length) { @@ -29,7 +36,7 @@ export function parseQuery(query: any) { .join(' '); } if (query.ast.getFieldClauses('type')) { - visibleTypes = query.ast.getFieldClauses('type')[0].value; + visibleTypes = query.ast.getFieldClauses('type')[0].value as string[]; } } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/process_import_response.test.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/process_import_response.test.ts rename to src/plugins/saved_objects_management/public/lib/process_import_response.test.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/process_import_response.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/process_import_response.ts rename to src/plugins/saved_objects_management/public/lib/process_import_response.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts similarity index 90% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.test.ts rename to src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts index b94b0a9d1291f..86eebad7ae787 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts @@ -17,14 +17,10 @@ * under the License. */ -jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); - import { SavedObjectsImportUnknownError } from 'src/core/public'; -import { kfetch } from 'ui/kfetch'; +import { httpServiceMock } from '../../../../core/public/mocks'; import { resolveImportErrors } from './resolve_import_errors'; -const kfetchMock = kfetch as jest.Mock; - function getFormData(form: Map) { const formData: Record = {}; for (const [key, val] of form.entries()) { @@ -39,13 +35,20 @@ function getFormData(form: Map) { describe('resolveImportErrors', () => { const getConflictResolutions = jest.fn(); + let httpMock: ReturnType; beforeEach(() => { + httpMock = httpServiceMock.createSetupContract(); jest.resetAllMocks(); }); + const extractBodyFromCall = (index: number): Map => { + return (httpMock.post.mock.calls[index] as any)[1].body; + }; + test('works with empty import failures', async () => { const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -62,6 +65,7 @@ Object { test(`doesn't retry if only unknown failures are passed in`, async () => { const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -98,7 +102,7 @@ Object { }); test('resolves conflicts', async () => { - kfetchMock.mockResolvedValueOnce({ + httpMock.post.mockResolvedValueOnce({ success: true, successCount: 1, }); @@ -107,6 +111,7 @@ Object { 'a:2': false, }); const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -139,7 +144,8 @@ Object { "status": "success", } `); - const formData = getFormData(kfetchMock.mock.calls[0][0].body); + + const formData = getFormData(extractBodyFromCall(0)); expect(formData).toMatchInlineSnapshot(` Object { "file": "undefined", @@ -156,12 +162,13 @@ Object { }); test('resolves missing references', async () => { - kfetchMock.mockResolvedValueOnce({ + httpMock.post.mockResolvedValueOnce({ success: true, successCount: 2, }); getConflictResolutions.mockResolvedValueOnce({}); const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -203,7 +210,7 @@ Object { "status": "success", } `); - const formData = getFormData(kfetchMock.mock.calls[0][0].body); + const formData = getFormData(extractBodyFromCall(0)); expect(formData).toMatchInlineSnapshot(` Object { "file": "undefined", @@ -232,6 +239,7 @@ Object { test(`doesn't resolve missing references if newIndexPatternId isn't defined`, async () => { getConflictResolutions.mockResolvedValueOnce({}); const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -276,7 +284,7 @@ Object { }); test('handles missing references then conflicts on the same errored objects', async () => { - kfetchMock.mockResolvedValueOnce({ + httpMock.post.mockResolvedValueOnce({ success: false, successCount: 0, errors: [ @@ -289,7 +297,7 @@ Object { }, ], }); - kfetchMock.mockResolvedValueOnce({ + httpMock.post.mockResolvedValueOnce({ success: true, successCount: 1, }); @@ -298,6 +306,7 @@ Object { 'a:1': true, }); const result = await resolveImportErrors({ + http: httpMock, getConflictResolutions, state: { importCount: 0, @@ -334,7 +343,7 @@ Object { "status": "success", } `); - const formData1 = getFormData(kfetchMock.mock.calls[0][0].body); + const formData1 = getFormData(extractBodyFromCall(0)); expect(formData1).toMatchInlineSnapshot(` Object { "file": "undefined", @@ -354,7 +363,7 @@ Object { ], } `); - const formData2 = getFormData(kfetchMock.mock.calls[1][0].body); + const formData2 = getFormData(extractBodyFromCall(1)); expect(formData2).toMatchInlineSnapshot(` Object { "file": "undefined", diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts similarity index 95% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.ts rename to src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts index dcc282402147d..0aea7114bad1c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_import_errors.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts @@ -17,7 +17,7 @@ * under the License. */ -import { kfetch } from 'ui/kfetch'; +import { HttpStart } from 'src/core/public'; import { FailedImport } from './process_import_response'; interface RetryObject { @@ -27,13 +27,11 @@ interface RetryObject { replaceReferences?: any[]; } -async function callResolveImportErrorsApi(file: File, retries: any) { +async function callResolveImportErrorsApi(http: HttpStart, file: File, retries: any) { const formData = new FormData(); formData.append('file', file); formData.append('retries', JSON.stringify(retries)); - return await kfetch({ - method: 'POST', - pathname: '/api/saved_objects/_resolve_import_errors', + return http.post('/api/saved_objects/_resolve_import_errors', { headers: { // Important to be undefined, it forces proper headers to be set for FormData 'Content-Type': undefined, @@ -100,9 +98,11 @@ function mapImportFailureToRetryObject({ } export async function resolveImportErrors({ + http, getConflictResolutions, state, }: { + http: HttpStart; getConflictResolutions: (objects: any[]) => Promise>; state: { importCount: number; failedImports?: FailedImport[] } & Record; }) { @@ -170,7 +170,7 @@ export async function resolveImportErrors({ } // Call API - const response = await callResolveImportErrorsApi(file, retries); + const response = await callResolveImportErrorsApi(http, file, retries); successImportCount += response.successCount; importFailures = []; for (const { error, ...obj } of response.errors || []) { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts similarity index 98% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts rename to src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts index dc6d2643145ff..23c2b75169555 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts @@ -23,11 +23,8 @@ import { saveObjects, saveObject, } from './resolve_saved_objects'; -import { - SavedObject, - SavedObjectLoader, -} from '../../../../../../../../plugins/saved_objects/public'; -import { IndexPatternsContract } from '../../../../../../../../plugins/data/public'; +import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public'; +import { IndexPatternsContract } from '../../../data/public'; class SavedObjectNotFound extends Error { constructor(options: Record) { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts similarity index 94% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts rename to src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts index d9473367f7502..15e03ed39d88c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts @@ -20,15 +20,8 @@ import { i18n } from '@kbn/i18n'; import { cloneDeep } from 'lodash'; import { OverlayStart, SavedObjectReference } from 'src/core/public'; -import { - SavedObject, - SavedObjectLoader, -} from '../../../../../../../../plugins/saved_objects/public'; -import { - IndexPatternsContract, - IIndexPattern, - createSearchSource, -} from '../../../../../../../../plugins/data/public'; +import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public'; +import { IndexPatternsContract, IIndexPattern, createSearchSource } from '../../../data/public'; type SavedObjectsRawDoc = Record; @@ -55,7 +48,7 @@ function addJsonFieldToIndexPattern( target[fieldName] = JSON.parse(sourceString); } catch (error) { throw new Error( - i18n.translate('kbn.management.objects.parsingFieldErrorMessage', { + i18n.translate('savedObjectsManagement.parsingFieldErrorMessage', { defaultMessage: 'Error encountered parsing {fieldName} for index pattern {indexName}: {errorMessage}', values: { @@ -103,18 +96,21 @@ async function importIndexPattern( if (!newId) { // We can override and we want to prompt for confirmation const isConfirmed = await openConfirm( - i18n.translate('kbn.management.indexPattern.confirmOverwriteLabel', { + i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteLabel', { values: { title }, defaultMessage: "Are you sure you want to overwrite '{title}'?", }), { - title: i18n.translate('kbn.management.indexPattern.confirmOverwriteTitle', { + title: i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteTitle', { defaultMessage: 'Overwrite {type}?', values: { type }, }), - confirmButtonText: i18n.translate('kbn.management.indexPattern.confirmOverwriteButton', { - defaultMessage: 'Overwrite', - }), + confirmButtonText: i18n.translate( + 'savedObjectsManagement.indexPattern.confirmOverwriteButton', + { + defaultMessage: 'Overwrite', + } + ), } ); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/index.js b/src/plugins/saved_objects_management/public/management_section/index.ts similarity index 93% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/index.js rename to src/plugins/saved_objects_management/public/management_section/index.ts index 522b1ce83a6b6..1bccb2102f3b4 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/index.js +++ b/src/plugins/saved_objects_management/public/management_section/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Relationships } from './relationships'; +export { mountManagementSection } from './mount_section'; diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx new file mode 100644 index 0000000000000..6f03f97079bb6 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -0,0 +1,211 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useEffect } from 'react'; +import ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route, useParams, useLocation } from 'react-router-dom'; +import { parse } from 'query-string'; +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { I18nProvider } from '@kbn/i18n/react'; +import { CoreSetup, CoreStart, ChromeBreadcrumb, Capabilities } from 'src/core/public'; +import { ManagementAppMountParams } from '../../../management/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; +import { + ISavedObjectsManagementServiceRegistry, + SavedObjectsManagementActionServiceStart, +} from '../services'; +import { SavedObjectsTable } from './objects_table'; +import { SavedObjectEdition } from './object_view'; +import { getAllowedTypes } from './../lib'; + +interface MountParams { + core: CoreSetup; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + mountParams: ManagementAppMountParams; +} + +let allowedObjectTypes: string[] | undefined; + +export const mountManagementSection = async ({ + core, + mountParams, + serviceRegistry, +}: MountParams) => { + const [coreStart, { data }, pluginStart] = await core.getStartServices(); + const { element, basePath, setBreadcrumbs } = mountParams; + if (allowedObjectTypes === undefined) { + allowedObjectTypes = await getAllowedTypes(coreStart.http); + } + + const capabilities = coreStart.application.capabilities; + + ReactDOM.render( + + + + + + + + + + + + + + + + , + element + ); + + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +}; + +const RedirectToHomeIfUnauthorized: React.FunctionComponent<{ + capabilities: Capabilities; +}> = ({ children, capabilities }) => { + const allowed = capabilities?.management?.kibana?.objects ?? false; + if (!allowed) { + window.location.hash = '/home'; + return null; + } + return children! as React.ReactElement; +}; + +const SavedObjectsEditionPage = ({ + coreStart, + serviceRegistry, + setBreadcrumbs, +}: { + coreStart: CoreStart; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; +}) => { + const { service: serviceName, id } = useParams<{ service: string; id: string }>(); + const capabilities = coreStart.application.capabilities; + + const { search } = useLocation(); + const query = parse(search); + const service = serviceRegistry.get(serviceName); + + useEffect(() => { + setBreadcrumbs([ + { + text: i18n.translate('savedObjectsManagement.breadcrumb.index', { + defaultMessage: 'Saved objects', + }), + href: '#/management/kibana/objects', + }, + { + text: i18n.translate('savedObjectsManagement.breadcrumb.edit', { + defaultMessage: 'Edit {savedObjectType}', + values: { savedObjectType: service?.service.type ?? 'object' }, + }), + }, + ]); + }, [setBreadcrumbs, service]); + + return ( + + ); +}; + +const SavedObjectsTablePage = ({ + coreStart, + dataStart, + allowedTypes, + serviceRegistry, + actionRegistry, + setBreadcrumbs, +}: { + coreStart: CoreStart; + dataStart: DataPublicPluginStart; + allowedTypes: string[]; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + actionRegistry: SavedObjectsManagementActionServiceStart; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; +}) => { + const capabilities = coreStart.application.capabilities; + const itemsPerPage = coreStart.uiSettings.get('savedObjects:perPage', 50); + + useEffect(() => { + setBreadcrumbs([ + { + text: i18n.translate('savedObjectsManagement.breadcrumb.index', { + defaultMessage: 'Saved objects', + }), + href: '#/management/kibana/objects', + }, + ]); + }, [setBreadcrumbs]); + + return ( + { + const { editUrl } = savedObject.meta; + if (editUrl) { + // previously, kbnUrl.change(object.meta.editUrl); was used. + // using direct access to location.hash seems the only option for now, + // as using react-router-dom will prefix the url with the router's basename + // which should be ignored there. + window.location.hash = editUrl; + } + }} + canGoInApp={savedObject => { + const { inAppUrl } = savedObject.meta; + return inAppUrl ? get(capabilities, inAppUrl.uiCapabilitiesPath) : false; + }} + /> + ); +}; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap similarity index 96% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/header.test.tsx.snap rename to src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap index 7e1f7ea12b014..d56776c2be9d7 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/header.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap @@ -23,7 +23,7 @@ exports[`Intro component renders correctly 1`] = ` > } @@ -37,7 +37,7 @@ exports[`Intro component renders correctly 1`] = ` > Proceed with caution! @@ -53,7 +53,7 @@ exports[`Intro component renders correctly 1`] = `
Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be. diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/not_found_errors.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/not_found_errors.test.tsx.snap rename to src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap index ac565a000813e..d5372fd5b18d9 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/__snapshots__/not_found_errors.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap @@ -10,7 +10,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] = title={ } @@ -39,7 +39,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] = > There is a problem with this saved object @@ -55,7 +55,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] =
The index pattern associated with this object no longer exists. @@ -64,7 +64,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] =
If you know what this error means, go ahead and fix it — otherwise click the delete button above. @@ -87,7 +87,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type title={ } @@ -116,7 +116,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type > There is a problem with this saved object @@ -132,7 +132,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type
A field associated with this object no longer exists in the index pattern. @@ -141,7 +141,7 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type
If you know what this error means, go ahead and fix it — otherwise click the delete button above. @@ -164,7 +164,7 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = ` title={ } @@ -193,7 +193,7 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = ` > There is a problem with this saved object @@ -209,7 +209,7 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = `
The saved search associated with this object no longer exists. @@ -218,7 +218,7 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = `
If you know what this error means, go ahead and fix it — otherwise click the delete button above. @@ -241,7 +241,7 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = ` title={ } @@ -270,7 +270,7 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = ` > There is a problem with this saved object @@ -287,7 +287,7 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = `
If you know what this error means, go ahead and fix it — otherwise click the delete button above. diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/field.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/field.test.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/field.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx similarity index 97% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/field.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx index 1ed0b57e400b8..1b69eb4240d68 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/field.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx @@ -104,9 +104,9 @@ export class Field extends PureComponent { id={this.fieldId} label={ !!currentValue ? ( - + ) : ( - + ) } checked={!!currentValue} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/form.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/form.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx index 7270d41eef529..04be7ee3ce207 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/form.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx @@ -29,15 +29,11 @@ import { import { cloneDeep, set } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - SimpleSavedObject, - SavedObjectsClientContract, -} from '../../../../../../../../../core/public'; - -import { SavedObjectLoader } from '../../../../../../../../../plugins/saved_objects/public'; +import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public'; +import { SavedObjectLoader } from '../../../../../saved_objects/public'; import { Field } from './field'; import { ObjectField, FieldState, SubmittedFormData } from '../../types'; -import { createFieldList } from '../../lib/create_field_list'; +import { createFieldList } from '../../../lib'; interface FormProps { object: SimpleSavedObject; @@ -96,7 +92,7 @@ export class Form extends Component { { data-test-subj="savedObjectEditSave" > @@ -117,14 +113,14 @@ export class Form extends Component { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/header.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/header.test.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/header.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/header.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx index 641493e0cbaa8..305d953c4990b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/header.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx @@ -52,7 +52,7 @@ export const Header = ({ {canEdit ? (

@@ -60,7 +60,7 @@ export const Header = ({ ) : (

@@ -79,7 +79,7 @@ export const Header = ({ data-test-subj="savedObjectEditViewInApp" > @@ -96,7 +96,7 @@ export const Header = ({ data-test-subj="savedObjectEditDelete" > diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/index.ts b/src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/index.ts rename to src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/intro.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/intro.test.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/intro.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/intro.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx index 098ad71345d49..920a5fcbcb02e 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/intro.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx @@ -26,7 +26,7 @@ export const Intro = () => { } @@ -35,7 +35,7 @@ export const Intro = () => { >
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/not_found_errors.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/not_found_errors.test.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/not_found_errors.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx similarity index 87% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/not_found_errors.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx index c3d18855f6c9a..1a63f7eaf4819 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/object_view/not_found_errors.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx @@ -31,21 +31,21 @@ export const NotFoundErrors = ({ type }: NotFoundErrors) => { case 'search': return ( ); case 'index-pattern': return ( ); case 'index-pattern-field': return ( ); @@ -58,7 +58,7 @@ export const NotFoundErrors = ({ type }: NotFoundErrors) => { } @@ -68,7 +68,7 @@ export const NotFoundErrors = ({ type }: NotFoundErrors) => {
{getMessage()}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/index.js b/src/plugins/saved_objects_management/public/management_section/object_view/index.ts similarity index 93% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/index.js rename to src/plugins/saved_objects_management/public/management_section/object_view/index.ts index cdeebdbf7b63a..a823923536d31 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/index.js +++ b/src/plugins/saved_objects_management/public/management_section/object_view/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Flyout } from './flyout'; +export { SavedObjectEdition } from './saved_object_view'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/saved_object_view.tsx rename to src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 4984fe3e6d6b8..f714970a5cac3 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -26,16 +26,16 @@ import { OverlayStart, NotificationsStart, SimpleSavedObject, -} from '../../../../../../../core/public'; -import { ISavedObjectsManagementRegistry } from '../../saved_object_registry'; -import { Header, NotFoundErrors, Intro, Form } from './components/object_view'; -import { canViewInApp } from './lib/in_app_url'; -import { SubmittedFormData } from './types'; +} from '../../../../../core/public'; +import { ISavedObjectsManagementServiceRegistry } from '../../services'; +import { Header, NotFoundErrors, Intro, Form } from './components'; +import { canViewInApp } from '../../lib'; +import { SubmittedFormData } from '../types'; interface SavedObjectEditionProps { id: string; serviceName: string; - serviceRegistry: ISavedObjectsManagementRegistry; + serviceRegistry: ISavedObjectsManagementServiceRegistry; capabilities: Capabilities; overlays: OverlayStart; notifications: NotificationsStart; @@ -135,17 +135,17 @@ export class SavedObjectEdition extends Component< const { type, object } = this.state; const confirmed = await overlays.openConfirm( - i18n.translate('kbn.management.objects.confirmModalOptions.modalDescription', { + i18n.translate('savedObjectsManagement.deleteConfirm.modalDescription', { defaultMessage: 'This action permanently removes the object from Kibana.', }), { confirmButtonText: i18n.translate( - 'kbn.management.objects.confirmModalOptions.deleteButtonLabel', + 'savedObjectsManagement.deleteConfirm.modalDeleteButtonLabel', { defaultMessage: 'Delete', } ), - title: i18n.translate('kbn.management.objects.confirmModalOptions.modalTitle', { + title: i18n.translate('savedObjectsManagement.deleteConfirm.modalTitle', { defaultMessage: `Delete '{title}'?`, values: { title: object?.attributes?.title || 'saved Kibana object', diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap similarity index 73% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap rename to src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index 2c0a5d8f6b8f1..fe64df6ff51d1 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -1,19 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ObjectsTable delete should show a confirm modal 1`] = ` +exports[`SavedObjectsTable delete should show a confirm modal 1`] = ` } confirmButtonText={ } @@ -23,7 +23,7 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = ` title={ } @@ -31,7 +31,7 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = `

@@ -58,12 +58,10 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = ` Array [ Object { "id": "1", - "title": "Title 1", "type": "index-pattern", }, Object { "id": "3", - "title": "Title 2", "type": "dashboard", }, ] @@ -76,7 +74,7 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = `
`; -exports[`ObjectsTable export should allow the user to choose when exporting all 1`] = ` +exports[`SavedObjectsTable export should allow the user to choose when exporting all 1`] = ` @@ -84,7 +82,7 @@ exports[`ObjectsTable export should allow the user to choose when exporting all } @@ -149,7 +147,7 @@ exports[`ObjectsTable export should allow the user to choose when exporting all label={ } @@ -173,7 +171,7 @@ exports[`ObjectsTable export should allow the user to choose when exporting all > @@ -187,7 +185,7 @@ exports[`ObjectsTable export should allow the user to choose when exporting all > @@ -199,23 +197,87 @@ exports[`ObjectsTable export should allow the user to choose when exporting all `; -exports[`ObjectsTable import should show the flyout 1`] = ` +exports[`SavedObjectsTable import should show the flyout 1`] = ` `; -exports[`ObjectsTable relationships should show the flyout 1`] = ` +exports[`SavedObjectsTable relationships should show the flyout 1`] = ` `; -exports[`ObjectsTable should render normally 1`] = ` +exports[`SavedObjectsTable should render normally 1`] = ` @@ -251,7 +313,23 @@ exports[`ObjectsTable should render normally 1`] = ` size="xs" /> @@ -36,7 +36,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = ` title={ } @@ -44,7 +44,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = `

, @@ -131,7 +131,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = ` > @@ -148,7 +148,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = ` > @@ -164,6 +164,30 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` Array [ Object { "getConflictResolutions": [Function], + "http": Object { + "addLoadingCountSource": [MockFunction], + "anonymousPaths": Object { + "isAnonymous": [MockFunction], + "register": [MockFunction], + }, + "basePath": BasePath { + "basePath": "", + "get": [Function], + "prepend": [Function], + "remove": [Function], + "serverBasePath": "", + }, + "delete": [MockFunction], + "fetch": [MockFunction], + "get": [MockFunction], + "getLoadingCount$": [MockFunction], + "head": [MockFunction], + "intercept": [MockFunction], + "options": [MockFunction], + "patch": [MockFunction], + "post": [MockFunction], + "put": [MockFunction], + }, "state": Object { "conflictedIndexPatterns": undefined, "conflictedSavedObjectsLinkedToSavedSearches": undefined, @@ -243,7 +267,7 @@ exports[`Flyout conflicts should handle errors 1`] = ` title={ } @@ -251,7 +275,7 @@ exports[`Flyout conflicts should handle errors 1`] = `

} @@ -280,7 +304,7 @@ exports[`Flyout errors should display unsupported type errors properly 1`] = `

@@ -331,7 +355,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` title={ } @@ -339,7 +363,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `

@@ -356,7 +380,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` title={ } @@ -364,7 +388,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `

, @@ -462,7 +486,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` > @@ -479,7 +503,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` > @@ -498,7 +522,7 @@ Array [ title={ } @@ -506,7 +530,7 @@ Array [

@@ -518,7 +542,7 @@ Array [ title={ } @@ -526,7 +550,7 @@ Array [

, @@ -548,7 +572,7 @@ Array [ title={ } @@ -578,7 +602,7 @@ exports[`Flyout should render import step 1`] = `

@@ -595,7 +619,7 @@ exports[`Flyout should render import step 1`] = ` label={ } @@ -607,7 +631,7 @@ exports[`Flyout should render import step 1`] = ` initialPromptText={ } @@ -628,7 +652,7 @@ exports[`Flyout should render import step 1`] = ` label={ } @@ -651,7 +675,7 @@ exports[`Flyout should render import step 1`] = ` > @@ -668,7 +692,7 @@ exports[`Flyout should render import step 1`] = ` > diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/__snapshots__/header.test.js.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap similarity index 88% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/__snapshots__/header.test.js.snap rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap index 51bd51a5e2e58..642a5030e4ec0 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/__snapshots__/header.test.js.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap @@ -13,7 +13,7 @@ exports[`Header should render normally 1`] = `

@@ -38,7 +38,7 @@ exports[`Header should render normally 1`] = ` > @@ -73,7 +73,7 @@ exports[`Header should render normally 1`] = ` > @@ -93,7 +93,7 @@ exports[`Header should render normally 1`] = ` > diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap similarity index 99% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap index 728944f3ccbfe..a8bb691cd54e9 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap @@ -202,7 +202,7 @@ exports[`Relationships should render errors 1`] = ` title={ } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap similarity index 93% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap index a4dcfb9c38184..d09dd6f8b868b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap @@ -36,7 +36,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` > , @@ -51,7 +51,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` > @@ -72,7 +72,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` label={ } @@ -83,7 +83,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` label={ } @@ -106,7 +106,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` > @@ -171,6 +171,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` items={ Array [ Object { + "attributes": Object {}, "id": "1", "meta": Object { "editUrl": "#/management/kibana/index_patterns/1", @@ -181,6 +182,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` }, "title": "MyIndexPattern*", }, + "references": Array [], "type": "index-pattern", }, ] @@ -249,7 +251,7 @@ exports[`Table should render normally 1`] = ` > , @@ -264,7 +266,7 @@ exports[`Table should render normally 1`] = ` > @@ -285,7 +287,7 @@ exports[`Table should render normally 1`] = ` label={ } @@ -296,7 +298,7 @@ exports[`Table should render normally 1`] = ` label={ } @@ -319,7 +321,7 @@ exports[`Table should render normally 1`] = ` > @@ -384,6 +386,7 @@ exports[`Table should render normally 1`] = ` items={ Array [ Object { + "attributes": Object {}, "id": "1", "meta": Object { "editUrl": "#/management/kibana/index_patterns/1", @@ -394,6 +397,7 @@ exports[`Table should render normally 1`] = ` }, "title": "MyIndexPattern*", }, + "references": Array [], "type": "index-pattern", }, ] diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.mocks.ts b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.mocks.ts new file mode 100644 index 0000000000000..b5361d212954f --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.mocks.ts @@ -0,0 +1,44 @@ +/* + * 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 const importFileMock = jest.fn(); +jest.doMock('../../../lib/import_file', () => ({ + importFile: importFileMock, +})); + +export const resolveImportErrorsMock = jest.fn(); +jest.doMock('../../../lib/resolve_import_errors', () => ({ + resolveImportErrors: resolveImportErrorsMock, +})); + +export const importLegacyFileMock = jest.fn(); +jest.doMock('../../../lib/import_legacy_file', () => ({ + importLegacyFile: importLegacyFileMock, +})); + +export const resolveSavedObjectsMock = jest.fn(); +export const resolveSavedSearchesMock = jest.fn(); +export const resolveIndexPatternConflictsMock = jest.fn(); +export const saveObjectsMock = jest.fn(); +jest.doMock('../../../lib/resolve_saved_objects', () => ({ + resolveSavedObjects: resolveSavedObjectsMock, + resolveSavedSearches: resolveSavedSearchesMock, + resolveIndexPatternConflicts: resolveIndexPatternConflictsMock, + saveObjects: saveObjectsMock, +})); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx similarity index 75% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index 0d16e0ae35dd6..5d713ff044f24 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -17,68 +17,62 @@ * under the License. */ +import { + importFileMock, + importLegacyFileMock, + resolveImportErrorsMock, + resolveIndexPatternConflictsMock, + resolveSavedObjectsMock, + resolveSavedSearchesMock, + saveObjectsMock, +} from './flyout.test.mocks'; + import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../../../../../../plugins/index_pattern_management/public/mocks'; -import { Flyout } from '../flyout'; - -jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); - -jest.mock('../../../../../lib/import_file', () => ({ - importFile: jest.fn(), -})); - -jest.mock('../../../../../lib/resolve_import_errors', () => ({ - resolveImportErrors: jest.fn(), -})); - -jest.mock('ui/chrome', () => ({ - addBasePath: () => {}, - getInjected: () => ['index-pattern', 'visualization', 'dashboard', 'search'], -})); - -jest.mock('../../../../../lib/import_legacy_file', () => ({ - importLegacyFile: jest.fn(), -})); - -jest.mock('../../../../../lib/resolve_saved_objects', () => ({ - resolveSavedObjects: jest.fn(), - resolveSavedSearches: jest.fn(), - resolveIndexPatternConflicts: jest.fn(), - saveObjects: jest.fn(), -})); - -jest.mock('../../../../../../../../../../../../plugins/index_pattern_management/public', () => ({ - setup: mockManagementPlugin.createSetupContract(), - start: mockManagementPlugin.createStartContract(), -})); - -jest.mock('ui/notify', () => ({})); - -const defaultProps = { - close: jest.fn(), - done: jest.fn(), - services: [], - newIndexPatternUrl: '', - getConflictResolutions: jest.fn(), - confirmModalPromise: jest.fn(), - indexPatterns: { - getFields: jest.fn().mockImplementation(() => [{ id: '1' }, { id: '2' }]), - }, -}; - -const mockFile = { +import { coreMock } from '../../../../../../core/public/mocks'; +import { serviceRegistryMock } from '../../../services/service_registry.mock'; +import { Flyout, FlyoutProps, FlyoutState } from './flyout'; +import { ShallowWrapper } from 'enzyme'; + +const mockFile = ({ name: 'foo.ndjson', path: '/home/foo.ndjson', -}; -const legacyMockFile = { +} as unknown) as File; +const legacyMockFile = ({ name: 'foo.json', path: '/home/foo.json', -}; +} as unknown) as File; describe('Flyout', () => { + let defaultProps: FlyoutProps; + + const shallowRender = (props: FlyoutProps) => { + return (shallowWithI18nProvider() as unknown) as ShallowWrapper< + FlyoutProps, + FlyoutState, + Flyout + >; + }; + + beforeEach(() => { + const { http, overlays } = coreMock.createStart(); + + defaultProps = { + close: jest.fn(), + done: jest.fn(), + newIndexPatternUrl: '', + indexPatterns: { + getFields: jest.fn().mockImplementation(() => [{ id: '1' }, { id: '2' }]), + } as any, + overlays, + http, + allowedTypes: ['search', 'index-pattern', 'visualization'], + serviceRegistry: serviceRegistryMock.create(), + }; + }); + it('should render import step', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -89,7 +83,7 @@ describe('Flyout', () => { }); it('should toggle the overwrite all control', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -102,7 +96,7 @@ describe('Flyout', () => { }); it('should allow picking a file', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -115,7 +109,7 @@ describe('Flyout', () => { }); it('should allow removing a file', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await Promise.resolve(); @@ -130,22 +124,21 @@ describe('Flyout', () => { }); it('should handle invalid files', async () => { - const { importLegacyFile } = require('../../../../../lib/import_legacy_file'); - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); // Ensure the state changes are reflected component.update(); - importLegacyFile.mockImplementation(() => { + importLegacyFileMock.mockImplementation(() => { throw new Error('foobar'); }); await component.instance().legacyImport(); expect(component.state('error')).toBe('The file could not be processed.'); - importLegacyFile.mockImplementation(() => ({ + importLegacyFileMock.mockImplementation(() => ({ invalid: true, })); @@ -156,11 +149,8 @@ describe('Flyout', () => { }); describe('conflicts', () => { - const { importFile } = require('../../../../../lib/import_file'); - const { resolveImportErrors } = require('../../../../../lib/resolve_import_errors'); - beforeEach(() => { - importFile.mockImplementation(() => ({ + importFileMock.mockImplementation(() => ({ success: false, successCount: 0, errors: [ @@ -180,7 +170,7 @@ describe('Flyout', () => { }, ], })); - resolveImportErrors.mockImplementation(() => ({ + resolveImportErrorsMock.mockImplementation(() => ({ status: 'success', importCount: 1, failedImports: [], @@ -188,7 +178,7 @@ describe('Flyout', () => { }); it('should figure out unmatchedReferences', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -198,7 +188,7 @@ describe('Flyout', () => { component.setState({ file: mockFile, isLegacyFile: false }); await component.instance().import(); - expect(importFile).toHaveBeenCalledWith(mockFile, true); + expect(importFileMock).toHaveBeenCalledWith(defaultProps.http, mockFile, true); expect(component.state()).toMatchObject({ conflictedIndexPatterns: undefined, conflictedSavedObjectsLinkedToSavedSearches: undefined, @@ -223,7 +213,7 @@ describe('Flyout', () => { }); it('should allow conflict resolution', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -239,7 +229,7 @@ describe('Flyout', () => { // Ensure we can change the resolution component.instance().onIndexChanged('MyIndexPattern*', { target: { value: '2' } }); - expect(component.state('unmatchedReferences')[0].newIndexPatternId).toBe('2'); + expect(component.state('unmatchedReferences')![0].newIndexPatternId).toBe('2'); // Let's resolve now await component @@ -247,18 +237,18 @@ describe('Flyout', () => { .simulate('click'); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); - expect(resolveImportErrors).toMatchSnapshot(); + expect(resolveImportErrorsMock).toMatchSnapshot(); }); it('should handle errors', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); // Ensure the state changes are reflected component.update(); - resolveImportErrors.mockImplementation(() => ({ + resolveImportErrorsMock.mockImplementation(() => ({ status: 'success', importCount: 0, failedImports: [ @@ -303,18 +293,15 @@ describe('Flyout', () => { }); describe('errors', () => { - const { importFile } = require('../../../../../lib/import_file'); - const { resolveImportErrors } = require('../../../../../lib/resolve_import_errors'); - it('should display unsupported type errors properly', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await Promise.resolve(); // Ensure the state changes are reflected component.update(); - importFile.mockImplementation(() => ({ + importFileMock.mockImplementation(() => ({ success: false, successCount: 0, errors: [ @@ -328,7 +315,7 @@ describe('Flyout', () => { }, ], })); - resolveImportErrors.mockImplementation(() => ({ + resolveImportErrorsMock.mockImplementation(() => ({ status: 'success', importCount: 0, failedImports: [ @@ -372,14 +359,6 @@ describe('Flyout', () => { }); describe('legacy conflicts', () => { - const { importLegacyFile } = require('../../../../../lib/import_legacy_file'); - const { - resolveSavedObjects, - resolveSavedSearches, - resolveIndexPatternConflicts, - saveObjects, - } = require('../../../../../lib/resolve_saved_objects'); - const mockData = [ { _id: '1', @@ -406,7 +385,7 @@ describe('Flyout', () => { }, obj: { searchSource: { - getOwnField: field => { + getOwnField: (field: string) => { if (field === 'index') { return 'MyIndexPattern*'; } @@ -426,8 +405,8 @@ describe('Flyout', () => { const mockConflictedSearchDocs = [3]; beforeEach(() => { - importLegacyFile.mockImplementation(() => mockData); - resolveSavedObjects.mockImplementation(() => ({ + importLegacyFileMock.mockImplementation(() => mockData); + resolveSavedObjectsMock.mockImplementation(() => ({ conflictedIndexPatterns: mockConflictedIndexPatterns, conflictedSavedObjectsLinkedToSavedSearches: mockConflictedSavedObjectsLinkedToSavedSearches, conflictedSearchDocs: mockConflictedSearchDocs, @@ -437,7 +416,7 @@ describe('Flyout', () => { }); it('should figure out unmatchedReferences', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -447,14 +426,14 @@ describe('Flyout', () => { component.setState({ file: legacyMockFile, isLegacyFile: true }); await component.instance().legacyImport(); - expect(importLegacyFile).toHaveBeenCalledWith(legacyMockFile); + expect(importLegacyFileMock).toHaveBeenCalledWith(legacyMockFile); // Remove the last element from data since it should be filtered out - expect(resolveSavedObjects).toHaveBeenCalledWith( + expect(resolveSavedObjectsMock).toHaveBeenCalledWith( mockData.slice(0, 2).map(doc => ({ ...doc, _migrationVersion: {} })), true, - defaultProps.services, + defaultProps.serviceRegistry.all().map(s => s.service), defaultProps.indexPatterns, - defaultProps.confirmModalPromise + defaultProps.overlays.openConfirm ); expect(component.state()).toMatchObject({ @@ -492,7 +471,7 @@ describe('Flyout', () => { }); it('should allow conflict resolution', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -508,7 +487,7 @@ describe('Flyout', () => { // Ensure we can change the resolution component.instance().onIndexChanged('MyIndexPattern*', { target: { value: '2' } }); - expect(component.state('unmatchedReferences')[0].newIndexPatternId).toBe('2'); + expect(component.state('unmatchedReferences')![0].newIndexPatternId).toBe('2'); // Let's resolve now await component @@ -516,33 +495,33 @@ describe('Flyout', () => { .simulate('click'); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); - expect(resolveIndexPatternConflicts).toHaveBeenCalledWith( + expect(resolveIndexPatternConflictsMock).toHaveBeenCalledWith( component.instance().resolutions, mockConflictedIndexPatterns, true, defaultProps.indexPatterns ); - expect(saveObjects).toHaveBeenCalledWith( + expect(saveObjectsMock).toHaveBeenCalledWith( mockConflictedSavedObjectsLinkedToSavedSearches, true ); - expect(resolveSavedSearches).toHaveBeenCalledWith( + expect(resolveSavedSearchesMock).toHaveBeenCalledWith( mockConflictedSearchDocs, - defaultProps.services, + defaultProps.serviceRegistry.all().map(s => s.service), defaultProps.indexPatterns, true ); }); it('should handle errors', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(defaultProps); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); // Ensure the state changes are reflected component.update(); - resolveIndexPatternConflicts.mockImplementation(() => { + resolveIndexPatternConflictsMock.mockImplementation(() => { throw new Error('foobar'); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx similarity index 77% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index da2221bb54203..45788dcb601ae 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -18,7 +18,6 @@ */ import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; import { take, get as getField } from 'lodash'; import { EuiFlyout, @@ -32,6 +31,7 @@ import { EuiForm, EuiFormRow, EuiSwitch, + // @ts-ignore EuiFilePicker, EuiInMemoryTable, EuiSelect, @@ -47,34 +47,62 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - +import { OverlayStart, HttpStart } from 'src/core/public'; +import { IndexPatternsContract, IIndexPattern } from '../../../../../data/public'; import { importFile, importLegacyFile, resolveImportErrors, logLegacyImport, getDefaultTitle, -} from '../../../../lib'; -import { processImportResponse } from '../../../../lib/process_import_response'; + processImportResponse, + ProcessedImportResponse, +} from '../../../lib'; import { resolveSavedObjects, resolveSavedSearches, resolveIndexPatternConflicts, saveObjects, -} from '../../../../lib/resolve_saved_objects'; -import { POSSIBLE_TYPES } from '../../objects_table'; - -export class Flyout extends Component { - static propTypes = { - close: PropTypes.func.isRequired, - done: PropTypes.func.isRequired, - services: PropTypes.array.isRequired, - newIndexPatternUrl: PropTypes.string.isRequired, - indexPatterns: PropTypes.object.isRequired, - confirmModalPromise: PropTypes.func.isRequired, - }; +} from '../../../lib/resolve_saved_objects'; +import { ISavedObjectsManagementServiceRegistry } from '../../../services'; + +export interface FlyoutProps { + serviceRegistry: ISavedObjectsManagementServiceRegistry; + allowedTypes: string[]; + close: () => void; + done: () => void; + newIndexPatternUrl: string; + indexPatterns: IndexPatternsContract; + overlays: OverlayStart; + http: HttpStart; +} + +export interface FlyoutState { + conflictedIndexPatterns?: any[]; + conflictedSavedObjectsLinkedToSavedSearches?: any[]; + conflictedSearchDocs?: any[]; + unmatchedReferences?: ProcessedImportResponse['unmatchedReferences']; + failedImports?: ProcessedImportResponse['failedImports']; + conflictingRecord?: ConflictingRecord; + error?: string; + file?: File; + importCount: number; + indexPatterns?: IIndexPattern[]; + isOverwriteAllChecked: boolean; + loadingMessage?: string; + isLegacyFile: boolean; + status: string; +} + +interface ConflictingRecord { + id: string; + type: string; + title: string; + done: (success: boolean) => void; +} - constructor(props) { +export class Flyout extends Component { + constructor(props: FlyoutProps) { super(props); this.state = { @@ -100,7 +128,7 @@ export class Flyout extends Component { fetchIndexPatterns = async () => { const indexPatterns = await this.props.indexPatterns.getFields(['id', 'title']); - this.setState({ indexPatterns }); + this.setState({ indexPatterns } as any); }; changeOverwriteAll = () => { @@ -109,11 +137,12 @@ export class Flyout extends Component { })); }; - setImportFile = ([file]) => { - if (!file) { + setImportFile = (files: FileList | null) => { + if (!files || !files[0]) { this.setState({ file: undefined, isLegacyFile: false }); return; } + const file = files[0]; this.setState({ file, isLegacyFile: /\.json$/i.test(file.name) || file.type === 'application/json', @@ -126,30 +155,29 @@ export class Flyout extends Component { * Does the initial import of a file, resolveImportErrors then handles errors and retries */ import = async () => { + const { http } = this.props; const { file, isOverwriteAllChecked } = this.state; this.setState({ status: 'loading', error: undefined }); // Import the file - let response; try { - response = await importFile(file, isOverwriteAllChecked); + const response = await importFile(http, file!, isOverwriteAllChecked); + this.setState(processImportResponse(response), () => { + // Resolve import errors right away if there's no index patterns to match + // This will ask about overwriting each object, etc + if (this.state.unmatchedReferences?.length === 0) { + this.resolveImportErrors(); + } + }); } catch (e) { this.setState({ status: 'error', - error: i18n.translate('kbn.management.objects.objectsTable.flyout.importFileErrorMessage', { + error: i18n.translate('savedObjectsManagement.objectsTable.flyout.importFileErrorMessage', { defaultMessage: 'The file could not be processed.', }), }); return; } - - this.setState(processImportResponse(response), () => { - // Resolve import errors right away if there's no index patterns to match - // This will ask about overwriting each object, etc - if (this.state.unmatchedReferences.length === 0) { - this.resolveImportErrors(); - } - }); }; /** @@ -160,10 +188,10 @@ export class Flyout extends Component { * @param {array} objects List of objects to request the user if they wish to overwrite it * @return {Promise} An object with the key being "type:id" and value the resolution chosen by the user */ - getConflictResolutions = async objects => { - const resolutions = {}; + getConflictResolutions = async (objects: any[]) => { + const resolutions: Record = {}; for (const { type, id, title } of objects) { - const overwrite = await new Promise(resolve => { + const overwrite = await new Promise(resolve => { this.setState({ conflictingRecord: { id, @@ -193,6 +221,7 @@ export class Flyout extends Component { try { const updatedState = await resolveImportErrors({ + http: this.props.http, state: this.state, getConflictResolutions: this.getConflictResolutions, }); @@ -201,7 +230,7 @@ export class Flyout extends Component { this.setState({ status: 'error', error: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.resolveImportErrorsFileErrorMessage', + 'savedObjectsManagement.objectsTable.flyout.resolveImportErrorsFileErrorMessage', { defaultMessage: 'The file could not be processed.' } ), }); @@ -209,22 +238,22 @@ export class Flyout extends Component { }; legacyImport = async () => { - const { services, indexPatterns, confirmModalPromise } = this.props; + const { serviceRegistry, indexPatterns, overlays, http, allowedTypes } = this.props; const { file, isOverwriteAllChecked } = this.state; this.setState({ status: 'loading', error: undefined }); // Log warning on server, don't wait for response - logLegacyImport(); + logLegacyImport(http); let contents; try { - contents = await importLegacyFile(file); + contents = await importLegacyFile(file!); } catch (e) { this.setState({ status: 'error', error: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.importLegacyFileErrorMessage', + 'savedObjectsManagement.objectsTable.flyout.importLegacyFileErrorMessage', { defaultMessage: 'The file could not be processed.' } ), }); @@ -235,7 +264,7 @@ export class Flyout extends Component { this.setState({ status: 'error', error: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.invalidFormatOfImportedFileErrorMessage', + 'savedObjectsManagement.objectsTable.flyout.invalidFormatOfImportedFileErrorMessage', { defaultMessage: 'Saved objects file format is invalid and cannot be imported.' } ), }); @@ -243,7 +272,7 @@ export class Flyout extends Component { } contents = contents - .filter(content => POSSIBLE_TYPES.includes(content._type)) + .filter(content => allowedTypes.includes(content._type)) .map(doc => ({ ...doc, // The server assumes that documents with no migrationVersion are up to date. @@ -263,18 +292,18 @@ export class Flyout extends Component { } = await resolveSavedObjects( contents, isOverwriteAllChecked, - services, + serviceRegistry.all().map(e => e.service), indexPatterns, - confirmModalPromise + overlays.openConfirm ); - const byId = {}; + const byId: Record = {}; conflictedIndexPatterns .map(({ doc, obj }) => { return { doc, obj: obj._serialize() }; }) .forEach(({ doc, obj }) => - obj.references.forEach(ref => { + obj.references.forEach((ref: Record) => { byId[ref.id] = byId[ref.id] != null ? byId[ref.id].concat({ doc, obj }) : [{ doc, obj }]; }) ); @@ -291,7 +320,7 @@ export class Flyout extends Component { }); return accum; }, - [] + [] as any[] ); this.setState({ @@ -305,12 +334,12 @@ export class Flyout extends Component { }); }; - get hasUnmatchedReferences() { + public get hasUnmatchedReferences() { return this.state.unmatchedReferences && this.state.unmatchedReferences.length > 0; } - get resolutions() { - return this.state.unmatchedReferences.reduce( + public get resolutions() { + return this.state.unmatchedReferences!.reduce( (accum, { existingIndexPatternId, newIndexPatternId }) => { if (newIndexPatternId) { accum.push({ @@ -320,7 +349,7 @@ export class Flyout extends Component { } return accum; }, - [] + [] as Array<{ oldId: string; newId: string }> ); } @@ -333,7 +362,7 @@ export class Flyout extends Component { failedImports, } = this.state; - const { services, indexPatterns } = this.props; + const { serviceRegistry, indexPatterns } = this.props; this.setState({ error: undefined, @@ -350,48 +379,48 @@ export class Flyout extends Component { // Do not Promise.all these calls as the order matters this.setState({ loadingMessage: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.confirmLegacyImport.resolvingConflictsLoadingMessage', + 'savedObjectsManagement.objectsTable.flyout.confirmLegacyImport.resolvingConflictsLoadingMessage', { defaultMessage: 'Resolving conflicts…' } ), }); if (resolutions.length) { importCount += await resolveIndexPatternConflicts( resolutions, - conflictedIndexPatterns, + conflictedIndexPatterns!, isOverwriteAllChecked, - this.props.indexPatterns + indexPatterns ); } this.setState({ loadingMessage: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.confirmLegacyImport.savingConflictsLoadingMessage', + 'savedObjectsManagement.objectsTable.flyout.confirmLegacyImport.savingConflictsLoadingMessage', { defaultMessage: 'Saving conflicts…' } ), }); importCount += await saveObjects( - conflictedSavedObjectsLinkedToSavedSearches, + conflictedSavedObjectsLinkedToSavedSearches!, isOverwriteAllChecked ); this.setState({ loadingMessage: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.confirmLegacyImport.savedSearchAreLinkedProperlyLoadingMessage', + 'savedObjectsManagement.objectsTable.flyout.confirmLegacyImport.savedSearchAreLinkedProperlyLoadingMessage', { defaultMessage: 'Ensure saved searches are linked properly…' } ), }); importCount += await resolveSavedSearches( - conflictedSearchDocs, - services, + conflictedSearchDocs!, + serviceRegistry.all().map(e => e.service), indexPatterns, isOverwriteAllChecked ); this.setState({ loadingMessage: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.confirmLegacyImport.retryingFailedObjectsLoadingMessage', + 'savedObjectsManagement.objectsTable.flyout.confirmLegacyImport.retryingFailedObjectsLoadingMessage', { defaultMessage: 'Retrying failed objects…' } ), }); importCount += await saveObjects( - failedImports.map(({ obj }) => obj), + failedImports!.map(({ obj }) => obj) as any[], isOverwriteAllChecked ); } catch (e) { @@ -407,26 +436,26 @@ export class Flyout extends Component { this.setState({ status: 'success', importCount }); }; - onIndexChanged = (id, e) => { + onIndexChanged = (id: string, e: any) => { const value = e.target.value; this.setState(state => { - const conflictIndex = state.unmatchedReferences.findIndex( + const conflictIndex = state.unmatchedReferences?.findIndex( conflict => conflict.existingIndexPatternId === id ); - if (conflictIndex === -1) { + if (conflictIndex === undefined || conflictIndex === -1) { return state; } return { unmatchedReferences: [ - ...state.unmatchedReferences.slice(0, conflictIndex), + ...state.unmatchedReferences!.slice(0, conflictIndex), { - ...state.unmatchedReferences[conflictIndex], + ...state.unmatchedReferences![conflictIndex], newIndexPatternId: value, }, - ...state.unmatchedReferences.slice(conflictIndex + 1), + ...state.unmatchedReferences!.slice(conflictIndex + 1), ], - }; + } as any; }); }; @@ -441,11 +470,11 @@ export class Flyout extends Component { { field: 'existingIndexPatternId', name: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnIdName', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnIdName', { defaultMessage: 'ID' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnIdDescription', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnIdDescription', { defaultMessage: 'ID of the index pattern' } ), sortable: true, @@ -453,28 +482,28 @@ export class Flyout extends Component { { field: 'list', name: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnCountName', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnCountName', { defaultMessage: 'Count' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnCountDescription', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnCountDescription', { defaultMessage: 'How many affected objects' } ), - render: list => { + render: (list: any[]) => { return {list.length}; }, }, { field: 'list', name: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsName', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsName', { defaultMessage: 'Sample of affected objects' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsDescription', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsDescription', { defaultMessage: 'Sample of affected objects' } ), - render: list => { + render: (list: any[]) => { return (
    {take(list, 3).map((obj, key) => ( @@ -487,15 +516,18 @@ export class Flyout extends Component { { field: 'existingIndexPatternId', name: i18n.translate( - 'kbn.management.objects.objectsTable.flyout.renderConflicts.columnNewIndexPatternName', + 'savedObjectsManagement.objectsTable.flyout.renderConflicts.columnNewIndexPatternName', { defaultMessage: 'New index pattern' } ), - render: id => { - const options = this.state.indexPatterns.map(indexPattern => ({ - text: indexPattern.title, - value: indexPattern.id, - ['data-test-subj']: `indexPatternOption-${indexPattern.title}`, - })); + render: (id: string) => { + const options = this.state.indexPatterns!.map( + indexPattern => + ({ + text: indexPattern.title, + value: indexPattern.id, + 'data-test-subj': `indexPatternOption-${indexPattern.title}`, + } as { text: string; value: string; 'data-test-subj'?: string }) + ); options.unshift({ text: '-- Skip Import --', @@ -518,7 +550,11 @@ export class Flyout extends Component { }; return ( - + ); } @@ -534,7 +570,7 @@ export class Flyout extends Component { } @@ -581,7 +617,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsFailedWarning" title={ } @@ -590,7 +626,7 @@ export class Flyout extends Component { >

    { return i18n.translate( - 'kbn.management.objects.objectsTable.flyout.importFailedMissingReference', + 'savedObjectsManagement.objectsTable.flyout.importFailedMissingReference', { defaultMessage: '{type} [id={id}] could not locate {refType} [id={refId}]', values: { @@ -618,7 +654,7 @@ export class Flyout extends Component { }); } else if (error.type === 'unsupported_type') { return i18n.translate( - 'kbn.management.objects.objectsTable.flyout.importFailedUnsupportedType', + 'savedObjectsManagement.objectsTable.flyout.importFailedUnsupportedType', { defaultMessage: '{type} [id={id}] unsupported type', values: { @@ -628,7 +664,7 @@ export class Flyout extends Component { } ); } - return getField(error, 'body.message', error.message || ''); + return getField(error, 'body.message', (error as any).message ?? ''); }) .join(' ')}

    @@ -643,7 +679,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsSuccessNoneImported" title={ } @@ -657,7 +693,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsSuccess" title={ } @@ -666,7 +702,7 @@ export class Flyout extends Component { >

    @@ -684,7 +720,7 @@ export class Flyout extends Component { } @@ -692,7 +728,7 @@ export class Flyout extends Component { } @@ -704,7 +740,7 @@ export class Flyout extends Component { name="overwriteAll" label={ } @@ -727,7 +763,7 @@ export class Flyout extends Component { confirmButton = ( @@ -742,7 +778,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsConfirmBtn" > @@ -757,7 +793,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsImportBtn" > @@ -769,7 +805,7 @@ export class Flyout extends Component { @@ -791,7 +827,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsLegacyWarning" title={ } @@ -800,7 +836,7 @@ export class Flyout extends Component { >

    @@ -815,7 +851,7 @@ export class Flyout extends Component { data-test-subj="importSavedObjectsConflictsWarning" title={ } @@ -824,7 +860,7 @@ export class Flyout extends Component { >

    @@ -867,11 +903,11 @@ export class Flyout extends Component { } overwriteConfirmed() { - this.state.conflictingRecord.done(true); + this.state.conflictingRecord!.done(true); } overwriteSkipped() { - this.state.conflictingRecord.done(false); + this.state.conflictingRecord!.done(false); } render() { @@ -883,18 +919,18 @@ export class Flyout extends Component {

    diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/header.test.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.test.tsx similarity index 96% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/header.test.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/header.test.tsx index 1f501b5751224..891190d0bb24b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/__jest__/header.test.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.test.tsx @@ -19,8 +19,7 @@ import React from 'react'; import { shallow } from 'enzyme'; - -import { Header } from '../header'; +import { Header } from './header'; describe('Header', () => { it('should render normally', () => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/header.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx similarity index 83% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/header.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx index 0bec8a0cf2daf..7a9584f08d632 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/header/header.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx @@ -18,8 +18,6 @@ */ import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; - import { EuiSpacer, EuiTitle, @@ -31,14 +29,24 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -export const Header = ({ onExportAll, onImport, onRefresh, filteredCount }) => ( +export const Header = ({ + onExportAll, + onImport, + onRefresh, + filteredCount, +}: { + onExportAll: () => void; + onImport: () => void; + onRefresh: () => void; + filteredCount: number; +}) => (

    @@ -55,7 +63,7 @@ export const Header = ({ onExportAll, onImport, onRefresh, filteredCount }) => ( onClick={onExportAll} > ( onClick={onImport} > @@ -79,7 +87,7 @@ export const Header = ({ onExportAll, onImport, onRefresh, filteredCount }) => ( @@ -92,7 +100,7 @@ export const Header = ({ onExportAll, onImport, onRefresh, filteredCount }) => (

    ); - -Header.propTypes = { - onExportAll: PropTypes.func.isRequired, - onImport: PropTypes.func.isRequired, - onRefresh: PropTypes.func.isRequired, - filteredCount: PropTypes.number.isRequired, -}; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/index.ts b/src/plugins/saved_objects_management/public/management_section/objects_table/components/index.ts new file mode 100644 index 0000000000000..9c8736a9011eb --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { Header } from './header'; +export { Table } from './table'; +export { Flyout } from './flyout'; +export { Relationships } from './relationships'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/relationships.test.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx similarity index 88% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/relationships.test.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx index 479726e8785d8..347f2d977015c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/relationships.test.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx @@ -19,27 +19,23 @@ import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; +import { httpServiceMock } from '../../../../../../core/public/mocks'; +import { Relationships, RelationshipsProps } from './relationships'; -jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); - -jest.mock('ui/chrome', () => ({ - addBasePath: () => '', -})); - -jest.mock('../../../../../lib/fetch_export_by_type_and_search', () => ({ +jest.mock('../../../lib/fetch_export_by_type_and_search', () => ({ fetchExportByTypeAndSearch: jest.fn(), })); -jest.mock('../../../../../lib/fetch_export_objects', () => ({ +jest.mock('../../../lib/fetch_export_objects', () => ({ fetchExportObjects: jest.fn(), })); -import { Relationships } from '../relationships'; - describe('Relationships', () => { it('should render index patterns normally', async () => { - const props = { + const props: RelationshipsProps = { goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, getRelationships: jest.fn().mockImplementation(() => [ { type: 'search', @@ -73,6 +69,8 @@ describe('Relationships', () => { savedObject: { id: '1', type: 'index-pattern', + attributes: {}, + references: [], meta: { title: 'MyIndexPattern*', icon: 'indexPatternApp', @@ -101,8 +99,10 @@ describe('Relationships', () => { }); it('should render searches normally', async () => { - const props = { + const props: RelationshipsProps = { goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, getRelationships: jest.fn().mockImplementation(() => [ { type: 'index-pattern', @@ -136,6 +136,8 @@ describe('Relationships', () => { savedObject: { id: '1', type: 'search', + attributes: {}, + references: [], meta: { title: 'MySearch', icon: 'search', @@ -164,8 +166,10 @@ describe('Relationships', () => { }); it('should render visualizations normally', async () => { - const props = { + const props: RelationshipsProps = { goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, getRelationships: jest.fn().mockImplementation(() => [ { type: 'dashboard', @@ -199,6 +203,8 @@ describe('Relationships', () => { savedObject: { id: '1', type: 'visualization', + attributes: {}, + references: [], meta: { title: 'MyViz', icon: 'visualizeApp', @@ -227,8 +233,10 @@ describe('Relationships', () => { }); it('should render dashboards normally', async () => { - const props = { + const props: RelationshipsProps = { goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, getRelationships: jest.fn().mockImplementation(() => [ { type: 'visualization', @@ -262,6 +270,8 @@ describe('Relationships', () => { savedObject: { id: '1', type: 'dashboard', + attributes: {}, + references: [], meta: { title: 'MyDashboard', icon: 'dashboardApp', @@ -290,14 +300,18 @@ describe('Relationships', () => { }); it('should render errors', async () => { - const props = { + const props: RelationshipsProps = { goInspectObject: () => {}, + canGoInApp: () => true, + basePath: httpServiceMock.createSetupContract().basePath, getRelationships: jest.fn().mockImplementation(() => { throw new Error('foo'); }), savedObject: { id: '1', type: 'dashboard', + attributes: {}, + references: [], meta: { title: 'MyDashboard', icon: 'dashboardApp', diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/relationships.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx similarity index 75% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/relationships.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx index ce3415ad2f0e7..ddb262138d565 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/relationships.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx @@ -18,8 +18,6 @@ */ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - import { EuiTitle, EuiFlyout, @@ -34,25 +32,34 @@ import { EuiText, EuiSpacer, } from '@elastic/eui'; -import chrome from 'ui/chrome'; +import { FilterConfig } from '@elastic/eui/src/components/search_bar/filters/filters'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getDefaultTitle, getSavedObjectLabel } from '../../../../lib'; +import { IBasePath } from 'src/core/public'; +import { getDefaultTitle, getSavedObjectLabel } from '../../../lib'; +import { SavedObjectWithMetadata, SavedObjectRelation } from '../../../types'; + +export interface RelationshipsProps { + basePath: IBasePath; + getRelationships: (type: string, id: string) => Promise; + savedObject: SavedObjectWithMetadata; + close: () => void; + goInspectObject: (obj: SavedObjectWithMetadata) => void; + canGoInApp: (obj: SavedObjectWithMetadata) => boolean; +} -export class Relationships extends Component { - static propTypes = { - getRelationships: PropTypes.func.isRequired, - savedObject: PropTypes.object.isRequired, - close: PropTypes.func.isRequired, - goInspectObject: PropTypes.func.isRequired, - canGoInApp: PropTypes.func.isRequired, - }; +export interface RelationshipsState { + relationships: SavedObjectRelation[]; + isLoading: boolean; + error?: string; +} - constructor(props) { +export class Relationships extends Component { + constructor(props: RelationshipsProps) { super(props); this.state = { - relationships: undefined, + relationships: [], isLoading: false, error: undefined, }; @@ -62,7 +69,7 @@ export class Relationships extends Component { this.getRelationshipData(); } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: RelationshipsProps) { if (nextProps.savedObject.id !== this.props.savedObject.id) { this.getRelationshipData(); } @@ -92,7 +99,7 @@ export class Relationships extends Component { } @@ -104,7 +111,7 @@ export class Relationships extends Component { } renderRelationships() { - const { goInspectObject, savedObject } = this.props; + const { goInspectObject, savedObject, basePath } = this.props; const { relationships, isLoading, error } = this.state; if (error) { @@ -118,17 +125,17 @@ export class Relationships extends Component { const columns = [ { field: 'type', - name: i18n.translate('kbn.management.objects.objectsTable.relationships.columnTypeName', { + name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnTypeName', { defaultMessage: 'Type', }), width: '50px', align: 'center', description: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.columnTypeDescription', + 'savedObjectsManagement.objectsTable.relationships.columnTypeDescription', { defaultMessage: 'Type of the saved object' } ), sortable: false, - render: (type, object) => { + render: (type: string, object: SavedObjectWithMetadata) => { return ( { + render: (relationship: string) => { if (relationship === 'parent') { return ( @@ -166,7 +173,7 @@ export class Relationships extends Component { return ( @@ -176,17 +183,17 @@ export class Relationships extends Component { }, { field: 'meta.title', - name: i18n.translate('kbn.management.objects.objectsTable.relationships.columnTitleName', { + name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnTitleName', { defaultMessage: 'Title', }), description: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.columnTitleDescription', + 'savedObjectsManagement.objectsTable.relationships.columnTitleDescription', { defaultMessage: 'Title of the saved object' } ), dataType: 'string', sortable: false, - render: (title, object) => { - const { path } = object.meta.inAppUrl || {}; + render: (title: string, object: SavedObjectWithMetadata) => { + const { path = '' } = object.meta.inAppUrl || {}; const canGoInApp = this.props.canGoInApp(object); if (!canGoInApp) { return ( @@ -196,7 +203,7 @@ export class Relationships extends Component { ); } return ( - + {title || getDefaultTitle(object)} ); @@ -204,24 +211,24 @@ export class Relationships extends Component { }, { name: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.columnActionsName', + 'savedObjectsManagement.objectsTable.relationships.columnActionsName', { defaultMessage: 'Actions' } ), actions: [ { name: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.columnActions.inspectActionName', + 'savedObjectsManagement.objectsTable.relationships.columnActions.inspectActionName', { defaultMessage: 'Inspect' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.columnActions.inspectActionDescription', + 'savedObjectsManagement.objectsTable.relationships.columnActions.inspectActionDescription', { defaultMessage: 'Inspect this saved object' } ), type: 'icon', icon: 'inspect', 'data-test-subj': 'relationshipsTableAction-inspect', - onClick: object => goInspectObject(object), - available: object => !!object.meta.editUrl, + onClick: (object: SavedObjectWithMetadata) => goInspectObject(object), + available: (object: SavedObjectWithMetadata) => !!object.meta.editUrl, }, ], }, @@ -244,7 +251,7 @@ export class Relationships extends Component { type: 'field_value_selection', field: 'relationship', name: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.search.filters.relationship.name', + 'savedObjectsManagement.objectsTable.relationships.search.filters.relationship.name', { defaultMessage: 'Direct relationship' } ), multiSelect: 'or', @@ -253,7 +260,7 @@ export class Relationships extends Component { value: 'parent', name: 'parent', view: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.search.filters.relationship.parentAsValue.view', + 'savedObjectsManagement.objectsTable.relationships.search.filters.relationship.parentAsValue.view', { defaultMessage: 'Parent' } ), }, @@ -261,7 +268,7 @@ export class Relationships extends Component { value: 'child', name: 'child', view: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.search.filters.relationship.childAsValue.view', + 'savedObjectsManagement.objectsTable.relationships.search.filters.relationship.childAsValue.view', { defaultMessage: 'Child' } ), }, @@ -271,13 +278,13 @@ export class Relationships extends Component { type: 'field_value_selection', field: 'type', name: i18n.translate( - 'kbn.management.objects.objectsTable.relationships.search.filters.type.name', + 'savedObjectsManagement.objectsTable.relationships.search.filters.type.name', { defaultMessage: 'Type' } ), multiSelect: 'or', options: [...filterTypesMap.values()], }, - ], + ] as FilterConfig[], }; return ( @@ -285,7 +292,7 @@ export class Relationships extends Component {

    {i18n.translate( - 'kbn.management.objects.objectsTable.relationships.relationshipsTitle', + 'savedObjectsManagement.objectsTable.relationships.relationshipsTitle', { defaultMessage: 'Here are the saved objects related to {title}. ' + @@ -301,7 +308,7 @@ export class Relationships extends Component { ({ diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/table.test.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx similarity index 87% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/table.test.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx index 9b3e2314c9f84..356f227773610 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/table.test.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx @@ -19,27 +19,22 @@ import React from 'react'; import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers'; +// @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; -import { keyCodes } from '@elastic/eui/lib/services'; -import { npSetup as mockNpSetup } from '../../../../../../../../../../../ui/public/new_platform/__mocks__'; +import { keyCodes } from '@elastic/eui'; +import { httpServiceMock } from '../../../../../../core/public/mocks'; +import { actionServiceMock } from '../../../services/action_service.mock'; +import { Table, TableProps } from './table'; -jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); - -jest.mock('ui/chrome', () => ({ - addBasePath: () => '', -})); - -jest.mock('ui/new_platform', () => ({ - npSetup: mockNpSetup, -})); - -import { Table } from '../table'; - -const defaultProps = { +const defaultProps: TableProps = { + basePath: httpServiceMock.createSetupContract().basePath, + actionRegistry: actionServiceMock.createStart(), selectedSavedObjects: [ { id: '1', type: 'index-pattern', + attributes: {}, + references: [], meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', @@ -58,13 +53,15 @@ const defaultProps = { onDelete: () => {}, onExport: () => {}, goInspectObject: () => {}, - canGoInApp: () => {}, + canGoInApp: () => true, pageIndex: 1, pageSize: 2, items: [ { id: '1', type: 'index-pattern', + attributes: {}, + references: [], meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', @@ -120,7 +117,7 @@ describe('Table', () => { { type: 'visualization' }, { type: 'search' }, { type: 'index-pattern' }, - ]; + ] as any; const customizedProps = { ...defaultProps, selectedSavedObjects, canDelete: false }; const component = shallowWithI18nProvider(

); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx similarity index 71% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx index 132fa1e691c1c..5b574e4b3d331 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx @@ -17,12 +17,10 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { npSetup } from 'ui/new_platform'; +import { IBasePath } from 'src/core/public'; import React, { PureComponent, Fragment } from 'react'; -import PropTypes from 'prop-types'; - import { + // @ts-ignore EuiSearchBar, EuiBasicTable, EuiButton, @@ -35,54 +33,64 @@ import { EuiSwitch, EuiFormRow, EuiText, + EuiTableFieldDataColumnType, + EuiTableActionsColumnType, } from '@elastic/eui'; -import { getDefaultTitle, getSavedObjectLabel } from '../../../../lib'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { getDefaultTitle, getSavedObjectLabel } from '../../../lib'; +import { SavedObjectWithMetadata } from '../../../types'; +import { + SavedObjectsManagementActionServiceStart, + SavedObjectsManagementAction, +} from '../../../services'; -export class Table extends PureComponent { - static propTypes = { - selectedSavedObjects: PropTypes.array.isRequired, - selectionConfig: PropTypes.shape({ - selectable: PropTypes.func, - selectableMessage: PropTypes.func, - onSelectionChange: PropTypes.func.isRequired, - }).isRequired, - filterOptions: PropTypes.array.isRequired, - canDelete: PropTypes.bool.isRequired, - onDelete: PropTypes.func.isRequired, - onExport: PropTypes.func.isRequired, - goInspectObject: PropTypes.func.isRequired, - - pageIndex: PropTypes.number.isRequired, - pageSize: PropTypes.number.isRequired, - items: PropTypes.array.isRequired, - itemId: PropTypes.oneOfType([ - PropTypes.string, // the name of the item id property - PropTypes.func, // (item) => string - ]), - totalItemCount: PropTypes.number.isRequired, - onQueryChange: PropTypes.func.isRequired, - onTableChange: PropTypes.func.isRequired, - isSearching: PropTypes.bool.isRequired, - - onShowRelationships: PropTypes.func.isRequired, +export interface TableProps { + basePath: IBasePath; + actionRegistry: SavedObjectsManagementActionServiceStart; + selectedSavedObjects: SavedObjectWithMetadata[]; + selectionConfig: { + onSelectionChange: (selection: SavedObjectWithMetadata[]) => void; }; + filterOptions: any[]; + canDelete: boolean; + onDelete: () => void; + onExport: (includeReferencesDeep: boolean) => void; + goInspectObject: (obj: SavedObjectWithMetadata) => void; + pageIndex: number; + pageSize: number; + items: SavedObjectWithMetadata[]; + itemId: string | (() => string); + totalItemCount: number; + onQueryChange: (query: any) => void; + onTableChange: (table: any) => void; + isSearching: boolean; + onShowRelationships: (object: SavedObjectWithMetadata) => void; + canGoInApp: (obj: SavedObjectWithMetadata) => boolean; +} + +interface TableState { + isSearchTextValid: boolean; + parseErrorMessage: any; + isExportPopoverOpen: boolean; + isIncludeReferencesDeepChecked: boolean; + activeAction?: SavedObjectsManagementAction; +} - state = { +export class Table extends PureComponent { + state: TableState = { isSearchTextValid: true, parseErrorMessage: null, isExportPopoverOpen: false, isIncludeReferencesDeepChecked: true, - activeAction: null, + activeAction: undefined, }; - constructor(props) { + constructor(props: TableProps) { super(props); - this.extraActions = npSetup.plugins.savedObjectsManagement.actionRegistry.getAll(); } - onChange = ({ query, error }) => { + onChange = ({ query, error }: any) => { if (error) { this.setState({ isSearchTextValid: false, @@ -136,12 +144,14 @@ export class Table extends PureComponent { onTableChange, goInspectObject, onShowRelationships, + basePath, + actionRegistry, } = this.props; const pagination = { - pageIndex: pageIndex, - pageSize: pageSize, - totalItemCount: totalItemCount, + pageIndex, + pageSize, + totalItemCount, pageSizeOptions: [5, 10, 20, 50], }; @@ -149,7 +159,7 @@ export class Table extends PureComponent { { type: 'field_value_selection', field: 'type', - name: i18n.translate('kbn.management.objects.objectsTable.table.typeFilterName', { + name: i18n.translate('savedObjectsManagement.objectsTable.table.typeFilterName', { defaultMessage: 'Type', }), multiSelect: 'or', @@ -168,18 +178,18 @@ export class Table extends PureComponent { const columns = [ { field: 'type', - name: i18n.translate('kbn.management.objects.objectsTable.table.columnTypeName', { + name: i18n.translate('savedObjectsManagement.objectsTable.table.columnTypeName', { defaultMessage: 'Type', }), width: '50px', align: 'center', description: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnTypeDescription', + 'savedObjectsManagement.objectsTable.table.columnTypeDescription', { defaultMessage: 'Type of the saved object' } ), sortable: false, 'data-test-subj': 'savedObjectsTableRowType', - render: (type, object) => { + render: (type: string, object: SavedObjectWithMetadata) => { return ( ); }, - }, + } as EuiTableFieldDataColumnType>, { field: 'meta.title', - name: i18n.translate('kbn.management.objects.objectsTable.table.columnTitleName', { + name: i18n.translate('savedObjectsManagement.objectsTable.table.columnTitleName', { defaultMessage: 'Title', }), description: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnTitleDescription', + 'savedObjectsManagement.objectsTable.table.columnTitleDescription', { defaultMessage: 'Title of the saved object' } ), dataType: 'string', sortable: false, 'data-test-subj': 'savedObjectsTableRowTitle', - render: (title, object) => { - const { path } = object.meta.inAppUrl || {}; + render: (title: string, object: SavedObjectWithMetadata) => { + const { path = '' } = object.meta.inAppUrl || {}; const canGoInApp = this.props.canGoInApp(object); if (!canGoInApp) { return {title || getDefaultTitle(object)}; } return ( - {title || getDefaultTitle(object)} + {title || getDefaultTitle(object)} ); }, - }, + } as EuiTableFieldDataColumnType>, { - name: i18n.translate('kbn.management.objects.objectsTable.table.columnActionsName', { + name: i18n.translate('savedObjectsManagement.objectsTable.table.columnActionsName', { defaultMessage: 'Actions', }), actions: [ { name: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnActions.inspectActionName', + 'savedObjectsManagement.objectsTable.table.columnActions.inspectActionName', { defaultMessage: 'Inspect' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnActions.inspectActionDescription', + 'savedObjectsManagement.objectsTable.table.columnActions.inspectActionDescription', { defaultMessage: 'Inspect this saved object' } ), type: 'icon', @@ -237,11 +247,11 @@ export class Table extends PureComponent { }, { name: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnActions.viewRelationshipsActionName', + 'savedObjectsManagement.objectsTable.table.columnActions.viewRelationshipsActionName', { defaultMessage: 'Relationships' } ), description: i18n.translate( - 'kbn.management.objects.objectsTable.table.columnActions.viewRelationshipsActionDescription', + 'savedObjectsManagement.objectsTable.table.columnActions.viewRelationshipsActionDescription', { defaultMessage: 'View the relationships this saved object has to other saved objects', @@ -252,33 +262,35 @@ export class Table extends PureComponent { onClick: object => onShowRelationships(object), 'data-test-subj': 'savedObjectsTableAction-relationships', }, - ...this.extraActions.map(action => { + ...actionRegistry.getAll().map(action => { return { ...action.euiAction, 'data-test-subj': `savedObjectsTableAction-${action.id}`, - onClick: object => { + onClick: (object: SavedObjectWithMetadata) => { this.setState({ activeAction: action, }); action.registerOnFinishCallback(() => { this.setState({ - activeAction: null, + activeAction: undefined, }); }); - action.euiAction.onClick(object); + if (action.euiAction.onClick) { + action.euiAction.onClick(object as any); + } }, }; }), ], - }, + } as EuiTableActionsColumnType, ]; let queryParseError; if (!this.state.isSearchTextValid) { const parseErrorMsg = i18n.translate( - 'kbn.management.objects.objectsTable.searchBar.unableToParseQueryErrorMessage', + 'savedObjectsManagement.objectsTable.searchBar.unableToParseQueryErrorMessage', { defaultMessage: 'Unable to parse query' } ); queryParseError = ( @@ -294,20 +306,20 @@ export class Table extends PureComponent { isDisabled={selectedSavedObjects.length === 0} > ); - const activeActionContents = this.state.activeAction ? this.state.activeAction.render() : null; + const activeActionContents = this.state.activeAction?.render() ?? null; return ( {activeActionContents} , @@ -339,7 +351,7 @@ export class Table extends PureComponent { } @@ -348,7 +360,7 @@ export class Table extends PureComponent { name="includeReferencesDeep" label={ } @@ -359,7 +371,7 @@ export class Table extends PureComponent { @@ -374,7 +386,7 @@ export class Table extends PureComponent { loading={isSearching} itemId={itemId} items={items} - columns={columns} + columns={columns as any} pagination={pagination} selection={selection} onChange={onTableChange} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/index.js b/src/plugins/saved_objects_management/public/management_section/objects_table/index.ts similarity index 93% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/index.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/index.ts index e1195c6edfe31..8777b15389690 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/index.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Table } from './table'; +export { SavedObjectsTable } from './saved_objects_table'; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.mocks.ts b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.mocks.ts new file mode 100644 index 0000000000000..6b4659a6b5a13 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.mocks.ts @@ -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. + */ + +export const saveAsMock = jest.fn(); +jest.doMock('@elastic/filesaver', () => ({ + saveAs: saveAsMock, +})); + +jest.doMock('lodash', () => ({ + ...jest.requireActual('lodash'), + debounce: (func: Function) => { + function debounced(this: any, ...args: any[]) { + return func.apply(this, args); + } + return debounced; + }, +})); + +export const findObjectsMock = jest.fn(); +jest.doMock('../../lib/find_objects', () => ({ + findObjects: findObjectsMock, +})); + +export const fetchExportObjectsMock = jest.fn(); +jest.doMock('../../lib/fetch_export_objects', () => ({ + fetchExportObjects: fetchExportObjectsMock, +})); + +export const fetchExportByTypeAndSearchMock = jest.fn(); +jest.doMock('../../lib/fetch_export_by_type_and_search', () => ({ + fetchExportByTypeAndSearch: fetchExportByTypeAndSearchMock, +})); + +export const extractExportDetailsMock = jest.fn(); +jest.doMock('../../lib/extract_export_details', () => ({ + extractExportDetails: extractExportDetailsMock, +})); + +jest.doMock('./components/header', () => ({ + Header: () => 'Header', +})); + +export const getSavedObjectCountsMock = jest.fn(); +jest.doMock('../../lib/get_saved_object_counts', () => ({ + getSavedObjectCounts: getSavedObjectCountsMock, +})); + +export const getRelationshipsMock = jest.fn(); +jest.doMock('../../lib/get_relationships', () => ({ + getRelationships: getRelationshipsMock, +})); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx similarity index 58% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index 7b9c17640a0f3..342fdc4784b09 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -17,69 +17,39 @@ * under the License. */ +import { + extractExportDetailsMock, + fetchExportByTypeAndSearchMock, + fetchExportObjectsMock, + findObjectsMock, + getRelationshipsMock, + getSavedObjectCountsMock, + saveAsMock, +} from './saved_objects_table.test.mocks'; + import React from 'react'; -import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../../../../plugins/index_pattern_management/public/mocks'; import { Query } from '@elastic/eui'; - -import { ObjectsTable, POSSIBLE_TYPES } from '../objects_table'; -import { Flyout } from '../components/flyout/'; -import { Relationships } from '../components/relationships/'; -import { findObjects } from '../../../lib'; -import { extractExportDetails } from '../../../lib/extract_export_details'; - -jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); - -jest.mock('../../../../../../../../../../plugins/index_pattern_management/public', () => ({ - setup: mockManagementPlugin.createSetupContract(), - start: mockManagementPlugin.createStartContract(), -})); - -jest.mock('../../../lib/find_objects', () => ({ - findObjects: jest.fn(), -})); - -jest.mock('../components/header', () => ({ - Header: () => 'Header', -})); - -jest.mock('ui/chrome', () => ({ - addBasePath: () => '', - getInjected: () => ['index-pattern', 'visualization', 'dashboard', 'search'], -})); - -jest.mock('../../../lib/fetch_export_objects', () => ({ - fetchExportObjects: jest.fn(), -})); - -jest.mock('../../../lib/fetch_export_by_type_and_search', () => ({ - fetchExportByTypeAndSearch: jest.fn(), -})); - -jest.mock('../../../lib/extract_export_details', () => ({ - extractExportDetails: jest.fn(), -})); - -jest.mock('../../../lib/get_saved_object_counts', () => ({ - getSavedObjectCounts: jest.fn().mockImplementation(() => { - return { - 'index-pattern': 0, - visualization: 0, - dashboard: 0, - search: 0, - }; - }), -})); - -jest.mock('@elastic/filesaver', () => ({ - saveAs: jest.fn(), -})); - -jest.mock('../../../lib/get_relationships', () => ({ - getRelationships: jest.fn(), -})); - -jest.mock('ui/notify', () => ({})); +import { ShallowWrapper } from 'enzyme'; +import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; +import { + httpServiceMock, + overlayServiceMock, + notificationServiceMock, + savedObjectsServiceMock, + applicationServiceMock, +} from '../../../../../core/public/mocks'; +import { dataPluginMock } from '../../../../data/public/mocks'; +import { serviceRegistryMock } from '../../services/service_registry.mock'; +import { actionServiceMock } from '../../services/action_service.mock'; +import { + SavedObjectsTable, + SavedObjectsTableProps, + SavedObjectsTableState, +} from './saved_objects_table'; +import { Flyout, Relationships } from './components'; +import { SavedObjectWithMetadata } from '../../types'; + +const allowedTypes = ['index-pattern', 'visualization', 'dashboard', 'search']; const allSavedObjects = [ { @@ -112,122 +82,128 @@ const allSavedObjects = [ }, ]; -const $http = () => {}; -$http.post = jest.fn().mockImplementation(() => []); -const defaultProps = { - goInspectObject: () => {}, - confirmModalPromise: jest.fn(), - savedObjectsClient: { - find: jest.fn(), - bulkGet: jest.fn(), - }, - indexPatterns: { - clearCache: jest.fn(), - }, - $http, - basePath: '', - newIndexPatternUrl: '', - kbnIndex: '', - services: [], - uiCapabilities: { - savedObjectsManagement: { - read: true, - edit: false, - delete: false, - }, - }, - canDelete: true, -}; - -beforeEach(() => { - findObjects.mockImplementation(() => ({ - total: 4, - savedObjects: [ - { - id: '1', - type: 'index-pattern', - meta: { - title: `MyIndexPattern*`, - icon: 'indexPatternApp', - editUrl: '#/management/kibana/index_patterns/1', - inAppUrl: { - path: '/management/kibana/index_patterns/1', - uiCapabilitiesPath: 'management.kibana.index_patterns', +describe('SavedObjectsTable', () => { + let defaultProps: SavedObjectsTableProps; + let http: ReturnType; + let overlays: ReturnType; + let notifications: ReturnType; + let savedObjects: ReturnType; + + const shallowRender = (overrides: Partial = {}) => { + return (shallowWithI18nProvider( + + ) as unknown) as ShallowWrapper< + SavedObjectsTableProps, + SavedObjectsTableState, + SavedObjectsTable + >; + }; + + beforeEach(() => { + extractExportDetailsMock.mockReset(); + + http = httpServiceMock.createStartContract(); + overlays = overlayServiceMock.createStartContract(); + notifications = notificationServiceMock.createStartContract(); + savedObjects = savedObjectsServiceMock.createStartContract(); + + const applications = applicationServiceMock.createStartContract(); + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: false, + }, + }; + + http.post.mockResolvedValue([]); + + getSavedObjectCountsMock.mockReturnValue({ + 'index-pattern': 0, + visualization: 0, + dashboard: 0, + search: 0, + }); + + defaultProps = { + allowedTypes, + serviceRegistry: serviceRegistryMock.create(), + actionRegistry: actionServiceMock.createStart(), + savedObjectsClient: savedObjects.client, + indexPatterns: dataPluginMock.createStartContract().indexPatterns, + http, + overlays, + notifications, + applications, + perPageConfig: 15, + goInspectObject: () => {}, + canGoInApp: () => true, + }; + + findObjectsMock.mockImplementation(() => ({ + total: 4, + savedObjects: [ + { + id: '1', + type: 'index-pattern', + meta: { + title: `MyIndexPattern*`, + icon: 'indexPatternApp', + editUrl: '#/management/kibana/index_patterns/1', + inAppUrl: { + path: '/management/kibana/index_patterns/1', + uiCapabilitiesPath: 'management.kibana.index_patterns', + }, }, }, - }, - { - id: '2', - type: 'search', - meta: { - title: `MySearch`, - icon: 'search', - editUrl: '#/management/kibana/objects/savedSearches/2', - inAppUrl: { - path: '/discover/2', - uiCapabilitiesPath: 'discover.show', + { + id: '2', + type: 'search', + meta: { + title: `MySearch`, + icon: 'search', + editUrl: '#/management/kibana/objects/savedSearches/2', + inAppUrl: { + path: '/discover/2', + uiCapabilitiesPath: 'discover.show', + }, }, }, - }, - { - id: '3', - type: 'dashboard', - meta: { - title: `MyDashboard`, - icon: 'dashboardApp', - editUrl: '#/management/kibana/objects/savedDashboards/3', - inAppUrl: { - path: '/dashboard/3', - uiCapabilitiesPath: 'dashboard.show', + { + id: '3', + type: 'dashboard', + meta: { + title: `MyDashboard`, + icon: 'dashboardApp', + editUrl: '#/management/kibana/objects/savedDashboards/3', + inAppUrl: { + path: '/dashboard/3', + uiCapabilitiesPath: 'dashboard.show', + }, }, }, - }, - { - id: '4', - type: 'visualization', - meta: { - title: `MyViz`, - icon: 'visualizeApp', - editUrl: '#/management/kibana/objects/savedVisualizations/4', - inAppUrl: { - path: '/visualize/edit/4', - uiCapabilitiesPath: 'visualize.show', + { + id: '4', + type: 'visualization', + meta: { + title: `MyViz`, + icon: 'visualizeApp', + editUrl: '#/management/kibana/objects/savedVisualizations/4', + inAppUrl: { + path: '/visualize/edit/4', + uiCapabilitiesPath: 'visualize.show', + }, }, }, - }, - ], - })); -}); - -let addDangerMock; -let addSuccessMock; -let addWarningMock; - -describe('ObjectsTable', () => { - beforeEach(() => { - defaultProps.savedObjectsClient.find.mockClear(); - extractExportDetails.mockReset(); - // mock _.debounce to fire immediately with no internal timer - require('lodash').debounce = func => { - function debounced(...args) { - return func.apply(this, args); - } - return debounced; - }; - addDangerMock = jest.fn(); - addSuccessMock = jest.fn(); - addWarningMock = jest.fn(); - require('ui/notify').toastNotifications = { - addDanger: addDangerMock, - addSuccess: addSuccessMock, - addWarning: addWarningMock, - }; + ], + })); }); it('should render normally', async () => { - const component = shallowWithI18nProvider( - - ); + const component = shallowRender({ perPageConfig: 15 }); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -238,19 +214,17 @@ describe('ObjectsTable', () => { }); it('should add danger toast when find fails', async () => { - findObjects.mockImplementation(() => { + findObjectsMock.mockImplementation(() => { throw new Error('Simulated find error'); }); - const component = shallowWithI18nProvider( - - ); + const component = shallowRender({ perPageConfig: 15 }); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); // Ensure the state changes are reflected component.update(); - expect(addDangerMock).toHaveBeenCalled(); + expect(notifications.toasts.addDanger).toHaveBeenCalled(); }); describe('export', () => { @@ -258,7 +232,7 @@ describe('ObjectsTable', () => { const mockSelectedSavedObjects = [ { id: '1', type: 'index-pattern' }, { id: '3', type: 'dashboard' }, - ]; + ] as SavedObjectWithMetadata[]; const mockSavedObjects = mockSelectedSavedObjects.map(obj => ({ _id: obj.id, @@ -272,11 +246,7 @@ describe('ObjectsTable', () => { })), }; - const { fetchExportObjects } = require('../../../lib/fetch_export_objects'); - - const component = shallowWithI18nProvider( - - ); + const component = shallowRender({ savedObjectsClient: mockSavedObjectsClient }); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -288,8 +258,8 @@ describe('ObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjects).toHaveBeenCalledWith(mockSelectedSavedObjects, true); - expect(addSuccessMock).toHaveBeenCalledWith({ + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); }); @@ -298,7 +268,7 @@ describe('ObjectsTable', () => { const mockSelectedSavedObjects = [ { id: '1', type: 'index-pattern' }, { id: '3', type: 'dashboard' }, - ]; + ] as SavedObjectWithMetadata[]; const mockSavedObjects = mockSelectedSavedObjects.map(obj => ({ _id: obj.id, @@ -312,16 +282,13 @@ describe('ObjectsTable', () => { })), }; - const { fetchExportObjects } = require('../../../lib/fetch_export_objects'); - extractExportDetails.mockImplementation(() => ({ + extractExportDetailsMock.mockImplementation(() => ({ exportedCount: 2, missingRefCount: 1, missingReferences: [{ id: '7', type: 'visualisation' }], })); - const component = shallowWithI18nProvider( - - ); + const component = shallowRender({ savedObjectsClient: mockSavedObjectsClient }); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -333,8 +300,8 @@ describe('ObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjects).toHaveBeenCalledWith(mockSelectedSavedObjects, true); - expect(addWarningMock).toHaveBeenCalledWith({ + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(notifications.toasts.addWarning).toHaveBeenCalledWith({ title: 'Your file is downloading in the background. ' + 'Some related objects could not be found. ' + @@ -343,25 +310,21 @@ describe('ObjectsTable', () => { }); it('should allow the user to choose when exporting all', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); // Ensure the state changes are reflected component.update(); - component.find('Header').prop('onExportAll')(); + (component.find('Header') as any).prop('onExportAll')(); component.update(); expect(component.find('EuiModal')).toMatchSnapshot(); }); it('should export all', async () => { - const { - fetchExportByTypeAndSearch, - } = require('../../../lib/fetch_export_by_type_and_search'); - const { saveAs } = require('@elastic/filesaver'); - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -370,23 +333,24 @@ describe('ObjectsTable', () => { // Set up mocks const blob = new Blob([JSON.stringify(allSavedObjects)], { type: 'application/ndjson' }); - fetchExportByTypeAndSearch.mockImplementation(() => blob); + fetchExportByTypeAndSearchMock.mockImplementation(() => blob); await component.instance().onExportAll(); - expect(fetchExportByTypeAndSearch).toHaveBeenCalledWith(POSSIBLE_TYPES, undefined, true); - expect(saveAs).toHaveBeenCalledWith(blob, 'export.ndjson'); - expect(addSuccessMock).toHaveBeenCalledWith({ + expect(fetchExportByTypeAndSearchMock).toHaveBeenCalledWith( + http, + allowedTypes, + undefined, + true + ); + expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); + expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); }); it('should export all, accounting for the current search criteria', async () => { - const { - fetchExportByTypeAndSearch, - } = require('../../../lib/fetch_export_by_type_and_search'); - const { saveAs } = require('@elastic/filesaver'); - const component = shallowWithI18nProvider(); + const component = shallowRender(); component.instance().onQueryChange({ query: Query.parse('test'), @@ -399,13 +363,18 @@ describe('ObjectsTable', () => { // Set up mocks const blob = new Blob([JSON.stringify(allSavedObjects)], { type: 'application/ndjson' }); - fetchExportByTypeAndSearch.mockImplementation(() => blob); + fetchExportByTypeAndSearchMock.mockImplementation(() => blob); await component.instance().onExportAll(); - expect(fetchExportByTypeAndSearch).toHaveBeenCalledWith(POSSIBLE_TYPES, 'test*', true); - expect(saveAs).toHaveBeenCalledWith(blob, 'export.ndjson'); - expect(addSuccessMock).toHaveBeenCalledWith({ + expect(fetchExportByTypeAndSearchMock).toHaveBeenCalledWith( + http, + allowedTypes, + 'test*', + true + ); + expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); + expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); }); @@ -413,7 +382,7 @@ describe('ObjectsTable', () => { describe('import', () => { it('should show the flyout', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -427,7 +396,7 @@ describe('ObjectsTable', () => { }); it('should hide the flyout', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -443,9 +412,7 @@ describe('ObjectsTable', () => { describe('relationships', () => { it('should fetch relationships', async () => { - const { getRelationships } = require('../../../lib/get_relationships'); - - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -454,17 +421,11 @@ describe('ObjectsTable', () => { await component.instance().getRelationships('search', '1'); const savedObjectTypes = ['index-pattern', 'visualization', 'dashboard', 'search']; - expect(getRelationships).toHaveBeenCalledWith( - 'search', - '1', - savedObjectTypes, - defaultProps.$http, - defaultProps.basePath - ); + expect(getRelationshipsMock).toHaveBeenCalledWith(http, 'search', '1', savedObjectTypes); }); it('should show the flyout', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -483,7 +444,7 @@ describe('ObjectsTable', () => { uiCapabilitiesPath: 'discover.show', }, }, - }); + } as SavedObjectWithMetadata); component.update(); expect(component.find(Relationships)).toMatchSnapshot(); @@ -503,7 +464,7 @@ describe('ObjectsTable', () => { }); it('should hide the flyout', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -522,12 +483,12 @@ describe('ObjectsTable', () => { describe('delete', () => { it('should show a confirm modal', async () => { - const component = shallowWithI18nProvider(); + const component = shallowRender(); const mockSelectedSavedObjects = [ - { id: '1', type: 'index-pattern', title: 'Title 1' }, - { id: '3', type: 'dashboard', title: 'Title 2' }, - ]; + { id: '1', type: 'index-pattern' }, + { id: '3', type: 'dashboard' }, + ] as SavedObjectWithMetadata[]; // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -546,7 +507,7 @@ describe('ObjectsTable', () => { const mockSelectedSavedObjects = [ { id: '1', type: 'index-pattern' }, { id: '3', type: 'dashboard' }, - ]; + ] as SavedObjectWithMetadata[]; const mockSavedObjects = mockSelectedSavedObjects.map(obj => ({ id: obj.id, @@ -562,9 +523,7 @@ describe('ObjectsTable', () => { delete: jest.fn(), }; - const component = shallowWithI18nProvider( - - ); + const component = shallowRender({ savedObjectsClient: mockSavedObjectsClient }); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx similarity index 73% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js rename to src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index 188762f165b24..c76fea5a0fb29 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -17,17 +17,10 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { saveAs } from '@elastic/filesaver'; import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { debounce } from 'lodash'; -import { Header } from './components/header'; -import { Flyout } from './components/flyout'; -import { Relationships } from './components/relationships'; -import { Table } from './components/table'; -import { toastNotifications } from 'ui/notify'; - +// @ts-ignore +import { saveAs } from '@elastic/filesaver'; import { EuiSpacer, Query, @@ -54,7 +47,15 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - +import { + SavedObjectsClientContract, + SavedObjectsFindOptions, + HttpStart, + OverlayStart, + NotificationsStart, + ApplicationStart, +} from 'src/core/public'; +import { IndexPatternsContract } from '../../../../data/public'; import { parseQuery, getSavedObjectCounts, @@ -63,39 +64,72 @@ import { fetchExportObjects, fetchExportByTypeAndSearch, findObjects, + extractExportDetails, + SavedObjectsExportResultDetails, } from '../../lib'; -import { extractExportDetails } from '../../lib/extract_export_details'; - -export const POSSIBLE_TYPES = chrome.getInjected('importAndExportableTypes'); - -export class ObjectsTable extends Component { - static propTypes = { - savedObjectsClient: PropTypes.object.isRequired, - indexPatterns: PropTypes.object.isRequired, - $http: PropTypes.func.isRequired, - basePath: PropTypes.string.isRequired, - perPageConfig: PropTypes.number, - newIndexPatternUrl: PropTypes.string.isRequired, - confirmModalPromise: PropTypes.func.isRequired, - services: PropTypes.array.isRequired, - uiCapabilities: PropTypes.object.isRequired, - goInspectObject: PropTypes.func.isRequired, - canGoInApp: PropTypes.func.isRequired, - }; +import { SavedObjectWithMetadata } from '../../types'; +import { + ISavedObjectsManagementServiceRegistry, + SavedObjectsManagementActionServiceStart, +} from '../../services'; +import { Header, Table, Flyout, Relationships } from './components'; + +interface ExportAllOption { + id: string; + label: string; +} - constructor(props) { +export interface SavedObjectsTableProps { + allowedTypes: string[]; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + actionRegistry: SavedObjectsManagementActionServiceStart; + savedObjectsClient: SavedObjectsClientContract; + indexPatterns: IndexPatternsContract; + http: HttpStart; + overlays: OverlayStart; + notifications: NotificationsStart; + applications: ApplicationStart; + perPageConfig: number; + goInspectObject: (obj: SavedObjectWithMetadata) => void; + canGoInApp: (obj: SavedObjectWithMetadata) => boolean; +} + +export interface SavedObjectsTableState { + totalCount: number; + page: number; + perPage: number; + savedObjects: SavedObjectWithMetadata[]; + savedObjectCounts: Record; + activeQuery: Query; + selectedSavedObjects: SavedObjectWithMetadata[]; + isShowingImportFlyout: boolean; + isSearching: boolean; + filteredItemCount: number; + isShowingRelationships: boolean; + relationshipObject?: SavedObjectWithMetadata; + isShowingDeleteConfirmModal: boolean; + isShowingExportAllOptionsModal: boolean; + isDeleting: boolean; + exportAllOptions: ExportAllOption[]; + exportAllSelectedOptions: Record; + isIncludeReferencesDeepChecked: boolean; +} + +export class SavedObjectsTable extends Component { + private _isMounted = false; + + constructor(props: SavedObjectsTableProps) { super(props); - this.savedObjectTypes = POSSIBLE_TYPES; this.state = { totalCount: 0, page: 0, perPage: props.perPageConfig || 50, savedObjects: [], - savedObjectCounts: this.savedObjectTypes.reduce((typeToCountMap, type) => { + savedObjectCounts: props.allowedTypes.reduce((typeToCountMap, type) => { typeToCountMap[type] = 0; return typeToCountMap; - }, {}), + }, {} as Record), activeQuery: Query.parse(''), selectedSavedObjects: [], isShowingImportFlyout: false, @@ -124,21 +158,20 @@ export class ObjectsTable extends Component { } fetchCounts = async () => { + const { allowedTypes } = this.props; const { queryText, visibleTypes } = parseQuery(this.state.activeQuery); - const filteredTypes = this.savedObjectTypes.filter( - type => !visibleTypes || visibleTypes.includes(type) - ); + const filteredTypes = allowedTypes.filter(type => !visibleTypes || visibleTypes.includes(type)); // These are the saved objects visible in the table. const filteredSavedObjectCounts = await getSavedObjectCounts( - this.props.$http, + this.props.http, filteredTypes, queryText ); - const exportAllOptions = []; - const exportAllSelectedOptions = {}; + const exportAllOptions: ExportAllOption[] = []; + const exportAllSelectedOptions: Record = {}; Object.keys(filteredSavedObjectCounts).forEach(id => { // Add this type as a bulk-export option. @@ -147,17 +180,13 @@ export class ObjectsTable extends Component { label: `${id} (${filteredSavedObjectCounts[id] || 0})`, }); - // Select it by defayult. + // Select it by default. exportAllSelectedOptions[id] = true; }); // Fetch all the saved objects that exist so we can accurately populate the counts within // the table filter dropdown. - const savedObjectCounts = await getSavedObjectCounts( - this.props.$http, - this.savedObjectTypes, - queryText - ); + const savedObjectCounts = await getSavedObjectCounts(this.props.http, allowedTypes, queryText); this.setState(state => ({ ...state, @@ -178,66 +207,64 @@ export class ObjectsTable extends Component { debouncedFetch = debounce(async () => { const { activeQuery: query, page, perPage } = this.state; + const { notifications, http, allowedTypes } = this.props; const { queryText, visibleTypes } = parseQuery(query); // "searchFields" is missing from the "findOptions" but gets injected via the API. // The API extracts the fields from each uiExports.savedObjectsManagement "defaultSearchField" attribute - const findOptions = { + const findOptions: SavedObjectsFindOptions = { search: queryText ? `${queryText}*` : undefined, perPage, page: page + 1, fields: ['id'], - type: this.savedObjectTypes.filter(type => !visibleTypes || visibleTypes.includes(type)), + type: allowedTypes.filter(type => !visibleTypes || visibleTypes.includes(type)), }; if (findOptions.type.length > 1) { findOptions.sortField = 'type'; } - let resp; try { - resp = await findObjects(findOptions); + const resp = await findObjects(http, findOptions); + if (!this._isMounted) { + return; + } + + this.setState(({ activeQuery }) => { + // ignore results for old requests + if (activeQuery.text !== query.text) { + return null; + } + + return { + savedObjects: resp.savedObjects, + filteredItemCount: resp.total, + isSearching: false, + }; + }); } catch (error) { if (this._isMounted) { this.setState({ isSearching: false, }); } - toastNotifications.addDanger({ + notifications.toasts.addDanger({ title: i18n.translate( - 'kbn.management.objects.objectsTable.unableFindSavedObjectsNotificationMessage', + 'savedObjectsManagement.objectsTable.unableFindSavedObjectsNotificationMessage', { defaultMessage: 'Unable find saved objects' } ), text: `${error}`, }); - return; - } - - if (!this._isMounted) { - return; } - - this.setState(({ activeQuery }) => { - // ignore results for old requests - if (activeQuery.text !== query.text) { - return {}; - } - - return { - savedObjects: resp.savedObjects, - filteredItemCount: resp.total, - isSearching: false, - }; - }); }, 300); refreshData = async () => { await Promise.all([this.fetchSavedObjects(), this.fetchCounts()]); }; - onSelectionChanged = selection => { + onSelectionChanged = (selection: SavedObjectWithMetadata[]) => { this.setState({ selectedSavedObjects: selection }); }; - onQueryChange = ({ query }) => { + onQueryChange = ({ query }: { query: Query }) => { // TODO: Use isSameQuery to compare new query with state.activeQuery to avoid re-fetching the // same data we already have. this.setState( @@ -253,7 +280,7 @@ export class ObjectsTable extends Component { ); }; - onTableChange = async table => { + onTableChange = async (table: any) => { const { index: page, size: perPage } = table.page || {}; this.setState( @@ -266,7 +293,7 @@ export class ObjectsTable extends Component { ); }; - onShowRelationships = object => { + onShowRelationships = (object: SavedObjectWithMetadata) => { this.setState({ isShowingRelationships: true, relationshipObject: object, @@ -280,16 +307,17 @@ export class ObjectsTable extends Component { }); }; - onExport = async includeReferencesDeep => { + onExport = async (includeReferencesDeep: boolean) => { const { selectedSavedObjects } = this.state; + const { notifications, http } = this.props; const objectsToExport = selectedSavedObjects.map(obj => ({ id: obj.id, type: obj.type })); let blob; try { - blob = await fetchExportObjects(objectsToExport, includeReferencesDeep); + blob = await fetchExportObjects(http, objectsToExport, includeReferencesDeep); } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('kbn.management.objects.objectsTable.export.dangerNotification', { + notifications.toasts.addDanger({ + title: i18n.translate('savedObjectsManagement.objectsTable.export.dangerNotification', { defaultMessage: 'Unable to generate export', }), }); @@ -304,24 +332,26 @@ export class ObjectsTable extends Component { onExportAll = async () => { const { exportAllSelectedOptions, isIncludeReferencesDeepChecked, activeQuery } = this.state; + const { notifications, http } = this.props; const { queryText } = parseQuery(activeQuery); const exportTypes = Object.entries(exportAllSelectedOptions).reduce((accum, [id, selected]) => { if (selected) { accum.push(id); } return accum; - }, []); + }, [] as string[]); let blob; try { blob = await fetchExportByTypeAndSearch( + http, exportTypes, queryText ? `${queryText}*` : undefined, isIncludeReferencesDeepChecked ); } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('kbn.management.objects.objectsTable.export.dangerNotification', { + notifications.toasts.addDanger({ + title: i18n.translate('savedObjectsManagement.objectsTable.export.dangerNotification', { defaultMessage: 'Unable to generate export', }), }); @@ -335,11 +365,12 @@ export class ObjectsTable extends Component { this.setState({ isShowingExportAllOptionsModal: false }); }; - showExportSuccessMessage = exportDetails => { + showExportSuccessMessage = (exportDetails: SavedObjectsExportResultDetails | undefined) => { + const { notifications } = this.props; if (exportDetails && exportDetails.missingReferences.length > 0) { - toastNotifications.addWarning({ + notifications.toasts.addWarning({ title: i18n.translate( - 'kbn.management.objects.objectsTable.export.successWithMissingRefsNotification', + 'savedObjectsManagement.objectsTable.export.successWithMissingRefsNotification', { defaultMessage: 'Your file is downloading in the background. ' + @@ -349,8 +380,8 @@ export class ObjectsTable extends Component { ), }); } else { - toastNotifications.addSuccess({ - title: i18n.translate('kbn.management.objects.objectsTable.export.successNotification', { + notifications.toasts.addSuccess({ + title: i18n.translate('savedObjectsManagement.objectsTable.export.successNotification', { defaultMessage: 'Your file is downloading in the background', }), }); @@ -412,30 +443,30 @@ export class ObjectsTable extends Component { }); }; - getRelationships = async (type, id) => { - return await getRelationships( - type, - id, - this.savedObjectTypes, - this.props.$http, - this.props.basePath - ); + getRelationships = async (type: string, id: string) => { + const { allowedTypes, http } = this.props; + return await getRelationships(http, type, id, allowedTypes); }; renderFlyout() { if (!this.state.isShowingImportFlyout) { return null; } + const { applications } = this.props; + const newIndexPatternUrl = applications.getUrlForApp('kibana', { + path: '#/management/kibana/index_pattern', + }); return ( ); } @@ -447,10 +478,10 @@ export class ObjectsTable extends Component { return ( @@ -482,7 +513,7 @@ export class ObjectsTable extends Component { } @@ -491,19 +522,19 @@ export class ObjectsTable extends Component { buttonColor="danger" cancelButtonText={ } confirmButtonText={ isDeleting ? ( ) : ( ) @@ -512,7 +543,7 @@ export class ObjectsTable extends Component { >

@@ -522,7 +553,7 @@ export class ObjectsTable extends Component { { field: 'type', name: i18n.translate( - 'kbn.management.objects.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName', + 'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName', { defaultMessage: 'Type' } ), width: '50px', @@ -535,14 +566,14 @@ export class ObjectsTable extends Component { { field: 'id', name: i18n.translate( - 'kbn.management.objects.objectsTable.deleteSavedObjectsConfirmModal.idColumnName', + 'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName', { defaultMessage: 'Id' } ), }, { field: 'meta.title', name: i18n.translate( - 'kbn.management.objects.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName', + 'savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName', { defaultMessage: 'Title' } ), }, @@ -586,7 +617,7 @@ export class ObjectsTable extends Component { } @@ -626,7 +657,7 @@ export class ObjectsTable extends Component { name="includeReferencesDeep" label={ } @@ -641,7 +672,7 @@ export class ObjectsTable extends Component { @@ -649,7 +680,7 @@ export class ObjectsTable extends Component { @@ -673,12 +704,13 @@ export class ObjectsTable extends Component { isSearching, savedObjectCounts, } = this.state; + const { http, allowedTypes, applications } = this.props; const selectionConfig = { onSelectionChange: this.onSelectionChanged, }; - const filterOptions = this.savedObjectTypes.map(type => ({ + const filterOptions = allowedTypes.map(type => ({ value: type, name: type, view: `${type} (${savedObjectCounts[type] || 0})`, @@ -698,14 +730,16 @@ export class ObjectsTable extends Component { />
=> { const mock = { - actionRegistry: actionRegistryMock.create(), + actions: actionServiceMock.createSetup(), + serviceRegistry: serviceRegistryMock.create(), }; return mock; }; const createStartContractMock = (): jest.Mocked => { - const mock = {}; + const mock = { + actions: actionServiceMock.createStart(), + }; return mock; }; export const savedObjectsManagementPluginMock = { - createActionRegistry: actionRegistryMock.create, + createServiceRegistry: serviceRegistryMock.create, createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, }; diff --git a/src/plugins/saved_objects_management/public/plugin.test.ts b/src/plugins/saved_objects_management/public/plugin.test.ts index 1cafbb235ad5b..09080f46a6869 100644 --- a/src/plugins/saved_objects_management/public/plugin.test.ts +++ b/src/plugins/saved_objects_management/public/plugin.test.ts @@ -20,6 +20,9 @@ import { coreMock } from '../../../core/public/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { homePluginMock } from '../../home/public/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { managementPluginMock } from '../../management/public/mocks'; +import { dataPluginMock } from '../../data/public/mocks'; import { SavedObjectsManagementPlugin } from './plugin'; describe('SavedObjectsManagementPlugin', () => { @@ -31,10 +34,13 @@ describe('SavedObjectsManagementPlugin', () => { describe('#setup', () => { it('registers the saved_objects feature to the home plugin', async () => { - const coreSetup = coreMock.createSetup(); + const coreSetup = coreMock.createSetup({ + pluginStartDeps: { data: dataPluginMock.createStartContract() }, + }); const homeSetup = homePluginMock.createSetupContract(); + const managementSetup = managementPluginMock.createSetupContract(); - await plugin.setup(coreSetup, { home: homeSetup }); + await plugin.setup(coreSetup, { home: homeSetup, management: managementSetup }); expect(homeSetup.featureCatalogue.register).toHaveBeenCalledTimes(1); expect(homeSetup.featureCatalogue.register).toHaveBeenCalledWith( diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index 3f2e9c166058e..c8dede3da9263 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -19,37 +19,59 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { ManagementSetup } from '../../management/public'; +import { DataPublicPluginStart } from '../../data/public'; +import { DashboardStart } from '../../dashboard/public'; +import { DiscoverStart } from '../../discover/public'; import { HomePublicPluginSetup, FeatureCatalogueCategory } from '../../home/public'; +import { VisualizationsStart } from '../../visualizations/public'; import { - SavedObjectsManagementActionRegistry, - ISavedObjectsManagementActionRegistry, + SavedObjectsManagementActionService, + SavedObjectsManagementActionServiceSetup, + SavedObjectsManagementActionServiceStart, + SavedObjectsManagementServiceRegistry, + ISavedObjectsManagementServiceRegistry, } from './services'; +import { registerServices } from './register_services'; export interface SavedObjectsManagementPluginSetup { - actionRegistry: ISavedObjectsManagementActionRegistry; + actions: SavedObjectsManagementActionServiceSetup; + serviceRegistry: ISavedObjectsManagementServiceRegistry; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface SavedObjectsManagementPluginStart {} +export interface SavedObjectsManagementPluginStart { + actions: SavedObjectsManagementActionServiceStart; +} export interface SetupDependencies { + management: ManagementSetup; home: HomePublicPluginSetup; } +export interface StartDependencies { + data: DataPublicPluginStart; + dashboard?: DashboardStart; + visualizations?: VisualizationsStart; + discover?: DiscoverStart; +} + export class SavedObjectsManagementPlugin implements Plugin< SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart, SetupDependencies, - {} + StartDependencies > { - private actionRegistry = new SavedObjectsManagementActionRegistry(); + private actionService = new SavedObjectsManagementActionService(); + private serviceRegistry = new SavedObjectsManagementServiceRegistry(); public setup( - core: CoreSetup<{}>, - { home }: SetupDependencies + core: CoreSetup, + { home, management }: SetupDependencies ): SavedObjectsManagementPluginSetup { + const actionSetup = this.actionService.setup(); + home.featureCatalogue.register({ id: 'saved_objects', title: i18n.translate('savedObjectsManagement.objects.savedObjectsTitle', { @@ -65,12 +87,39 @@ export class SavedObjectsManagementPlugin category: FeatureCatalogueCategory.ADMIN, }); + const kibanaSection = management.sections.getSection('kibana'); + if (!kibanaSection) { + throw new Error('`kibana` management section not found.'); + } + kibanaSection.registerApp({ + id: 'objects', + title: i18n.translate('savedObjectsManagement.managementSectionLabel', { + defaultMessage: 'Saved Objects', + }), + order: 10, + mount: async mountParams => { + const { mountManagementSection } = await import('./management_section'); + return mountManagementSection({ + core, + serviceRegistry: this.serviceRegistry, + mountParams, + }); + }, + }); + + // depends on `getStartServices`, should not be awaited + registerServices(this.serviceRegistry, core.getStartServices); + return { - actionRegistry: this.actionRegistry, + actions: actionSetup, + serviceRegistry: this.serviceRegistry, }; } public start(core: CoreStart) { - return {}; + const actionStart = this.actionService.start(); + return { + actions: actionStart, + }; } } diff --git a/src/plugins/saved_objects_management/public/register_services.ts b/src/plugins/saved_objects_management/public/register_services.ts new file mode 100644 index 0000000000000..a34b632b78f6c --- /dev/null +++ b/src/plugins/saved_objects_management/public/register_services.ts @@ -0,0 +1,59 @@ +/* + * 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 { StartServicesAccessor } from '../../../core/public'; +import { SavedObjectsManagementPluginStart, StartDependencies } from './plugin'; +import { ISavedObjectsManagementServiceRegistry } from './services'; + +export const registerServices = async ( + registry: ISavedObjectsManagementServiceRegistry, + getStartServices: StartServicesAccessor +) => { + const [coreStart, { dashboard, data, visualizations, discover }] = await getStartServices(); + + if (dashboard) { + registry.register({ + id: 'savedDashboards', + title: 'dashboards', + service: dashboard.getSavedDashboardLoader(), + }); + } + + if (visualizations) { + registry.register({ + id: 'savedVisualizations', + title: 'visualizations', + service: visualizations.savedVisualizationsLoader, + }); + } + + if (discover) { + registry.register({ + id: 'savedSearches', + title: 'searches', + service: discover.savedSearches.createLoader({ + savedObjectsClient: coreStart.savedObjects.client, + indexPatterns: data.indexPatterns, + search: data.search, + chrome: coreStart.chrome, + overlays: coreStart.overlays, + }), + }); + } +}; diff --git a/src/plugins/saved_objects_management/public/services/action_service.mock.ts b/src/plugins/saved_objects_management/public/services/action_service.mock.ts new file mode 100644 index 0000000000000..97c95a589b925 --- /dev/null +++ b/src/plugins/saved_objects_management/public/services/action_service.mock.ts @@ -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 { + SavedObjectsManagementActionService, + SavedObjectsManagementActionServiceSetup, + SavedObjectsManagementActionServiceStart, +} from './action_service'; + +const createSetupMock = (): jest.Mocked => { + const mock = { + register: jest.fn(), + }; + return mock; +}; + +const createStartMock = (): jest.Mocked => { + const mock = { + has: jest.fn(), + getAll: jest.fn(), + }; + + mock.has.mockReturnValue(true); + mock.getAll.mockReturnValue([]); + + return mock; +}; + +const createServiceMock = (): jest.Mocked> => { + const mock = { + setup: jest.fn().mockReturnValue(createSetupMock()), + start: jest.fn().mockReturnValue(createStartMock()), + }; + return mock; +}; + +export const actionServiceMock = { + create: createServiceMock, + createSetup: createSetupMock, + createStart: createStartMock, +}; diff --git a/src/plugins/saved_objects_management/public/services/action_registry.test.ts b/src/plugins/saved_objects_management/public/services/action_service.test.ts similarity index 69% rename from src/plugins/saved_objects_management/public/services/action_registry.test.ts rename to src/plugins/saved_objects_management/public/services/action_service.test.ts index eb3bda00f4196..107554589f83d 100644 --- a/src/plugins/saved_objects_management/public/services/action_registry.test.ts +++ b/src/plugins/saved_objects_management/public/services/action_service.test.ts @@ -17,8 +17,11 @@ * under the License. */ -import { SavedObjectsManagementActionRegistry } from './action_registry'; -import { SavedObjectsManagementAction } from './action_types'; +import { + SavedObjectsManagementActionService, + SavedObjectsManagementActionServiceSetup, +} from './action_service'; +import { SavedObjectsManagementAction } from './types'; class DummyAction extends SavedObjectsManagementAction { constructor(public id: string) { @@ -36,27 +39,30 @@ class DummyAction extends SavedObjectsManagementAction { } describe('SavedObjectsManagementActionRegistry', () => { - let registry: SavedObjectsManagementActionRegistry; + let service: SavedObjectsManagementActionService; + let setup: SavedObjectsManagementActionServiceSetup; const createAction = (id: string): SavedObjectsManagementAction => { return new DummyAction(id); }; beforeEach(() => { - registry = new SavedObjectsManagementActionRegistry(); + service = new SavedObjectsManagementActionService(); + setup = service.setup(); }); describe('#register', () => { it('allows actions to be registered and retrieved', () => { const action = createAction('foo'); - registry.register(action); - expect(registry.getAll()).toContain(action); + setup.register(action); + const start = service.start(); + expect(start.getAll()).toContain(action); }); it('does not allow actions with duplicate ids to be registered', () => { const action = createAction('my-action'); - registry.register(action); - expect(() => registry.register(action)).toThrowErrorMatchingInlineSnapshot( + setup.register(action); + expect(() => setup.register(action)).toThrowErrorMatchingInlineSnapshot( `"Saved Objects Management Action with id 'my-action' already exists"` ); }); @@ -65,12 +71,14 @@ describe('SavedObjectsManagementActionRegistry', () => { describe('#has', () => { it('returns true when an action with a matching ID exists', () => { const action = createAction('existing-action'); - registry.register(action); - expect(registry.has('existing-action')).toEqual(true); + setup.register(action); + const start = service.start(); + expect(start.has('existing-action')).toEqual(true); }); it(`returns false when an action doesn't exist`, () => { - expect(registry.has('missing-action')).toEqual(false); + const start = service.start(); + expect(start.has('missing-action')).toEqual(false); }); }); }); diff --git a/src/plugins/saved_objects_management/public/services/action_registry.ts b/src/plugins/saved_objects_management/public/services/action_service.ts similarity index 56% rename from src/plugins/saved_objects_management/public/services/action_registry.ts rename to src/plugins/saved_objects_management/public/services/action_service.ts index 8bf77231dd73f..2b0b4cf5431e5 100644 --- a/src/plugins/saved_objects_management/public/services/action_registry.ts +++ b/src/plugins/saved_objects_management/public/services/action_service.ts @@ -17,36 +17,44 @@ * under the License. */ -import { SavedObjectsManagementAction } from './action_types'; - -export type ISavedObjectsManagementActionRegistry = PublicMethodsOf< - SavedObjectsManagementActionRegistry ->; - -export class SavedObjectsManagementActionRegistry { - private readonly actions = new Map(); +import { SavedObjectsManagementAction } from './types'; +export interface SavedObjectsManagementActionServiceSetup { /** * register given action in the registry. */ - register(action: SavedObjectsManagementAction) { - if (this.actions.has(action.id)) { - throw new Error(`Saved Objects Management Action with id '${action.id}' already exists`); - } - this.actions.set(action.id, action); - } + register: (action: SavedObjectsManagementAction) => void; +} +export interface SavedObjectsManagementActionServiceStart { /** * return true if the registry contains given action, false otherwise. */ - has(actionId: string) { - return this.actions.has(actionId); - } - + has: (actionId: string) => boolean; /** * return all {@link SavedObjectsManagementAction | actions} currently registered. */ - getAll() { - return [...this.actions.values()]; + getAll: () => SavedObjectsManagementAction[]; +} + +export class SavedObjectsManagementActionService { + private readonly actions = new Map(); + + setup(): SavedObjectsManagementActionServiceSetup { + return { + register: action => { + if (this.actions.has(action.id)) { + throw new Error(`Saved Objects Management Action with id '${action.id}' already exists`); + } + this.actions.set(action.id, action); + }, + }; + } + + start(): SavedObjectsManagementActionServiceStart { + return { + has: actionId => this.actions.has(actionId), + getAll: () => [...this.actions.values()], + }; } } diff --git a/src/plugins/saved_objects_management/public/services/index.ts b/src/plugins/saved_objects_management/public/services/index.ts index d6353576b8e11..a59ad9012c402 100644 --- a/src/plugins/saved_objects_management/public/services/index.ts +++ b/src/plugins/saved_objects_management/public/services/index.ts @@ -18,7 +18,13 @@ */ export { - SavedObjectsManagementActionRegistry, - ISavedObjectsManagementActionRegistry, -} from './action_registry'; -export { SavedObjectsManagementAction, SavedObjectsManagementRecord } from './action_types'; + SavedObjectsManagementActionService, + SavedObjectsManagementActionServiceStart, + SavedObjectsManagementActionServiceSetup, +} from './action_service'; +export { + SavedObjectsManagementServiceRegistry, + ISavedObjectsManagementServiceRegistry, + SavedObjectsManagementServiceRegistryEntry, +} from './service_registry'; +export { SavedObjectsManagementAction, SavedObjectsManagementRecord } from './types'; diff --git a/src/plugins/saved_objects_management/public/services/action_registry.mock.ts b/src/plugins/saved_objects_management/public/services/service_registry.mock.ts similarity index 79% rename from src/plugins/saved_objects_management/public/services/action_registry.mock.ts rename to src/plugins/saved_objects_management/public/services/service_registry.mock.ts index a9093ad42d0ac..2e671c781928f 100644 --- a/src/plugins/saved_objects_management/public/services/action_registry.mock.ts +++ b/src/plugins/saved_objects_management/public/services/service_registry.mock.ts @@ -17,21 +17,20 @@ * under the License. */ -import { ISavedObjectsManagementActionRegistry } from './action_registry'; +import { ISavedObjectsManagementServiceRegistry } from './service_registry'; -const createRegistryMock = (): jest.Mocked => { +const createRegistryMock = (): jest.Mocked => { const mock = { register: jest.fn(), - has: jest.fn(), - getAll: jest.fn(), + all: jest.fn(), + get: jest.fn(), }; - mock.has.mockReturnValue(true); - mock.getAll.mockReturnValue([]); + mock.all.mockReturnValue([]); return mock; }; -export const actionRegistryMock = { +export const serviceRegistryMock = { create: createRegistryMock, }; diff --git a/src/plugins/saved_objects_management/public/services/service_registry.ts b/src/plugins/saved_objects_management/public/services/service_registry.ts new file mode 100644 index 0000000000000..2d6ec0b92047a --- /dev/null +++ b/src/plugins/saved_objects_management/public/services/service_registry.ts @@ -0,0 +1,49 @@ +/* + * 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 { SavedObjectLoader } from '../../../saved_objects/public'; + +export interface SavedObjectsManagementServiceRegistryEntry { + id: string; + service: SavedObjectLoader; + title: string; +} + +export type ISavedObjectsManagementServiceRegistry = PublicMethodsOf< + SavedObjectsManagementServiceRegistry +>; + +export class SavedObjectsManagementServiceRegistry { + private readonly registry = new Map(); + + public register(entry: SavedObjectsManagementServiceRegistryEntry) { + if (this.registry.has(entry.id)) { + throw new Error(''); + } + this.registry.set(entry.id, entry); + } + + public all(): SavedObjectsManagementServiceRegistryEntry[] { + return [...this.registry.values()]; + } + + public get(id: string): SavedObjectsManagementServiceRegistryEntry | undefined { + return this.registry.get(id); + } +} diff --git a/src/plugins/saved_objects_management/public/services/action_types.ts b/src/plugins/saved_objects_management/public/services/types.ts similarity index 100% rename from src/plugins/saved_objects_management/public/services/action_types.ts rename to src/plugins/saved_objects_management/public/services/types.ts diff --git a/src/plugins/saved_objects_management/public/types.ts b/src/plugins/saved_objects_management/public/types.ts new file mode 100644 index 0000000000000..e91b5d253b55f --- /dev/null +++ b/src/plugins/saved_objects_management/public/types.ts @@ -0,0 +1,20 @@ +/* + * 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 { SavedObjectMetadata, SavedObjectWithMetadata, SavedObjectRelation } from '../common'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts similarity index 61% rename from src/legacy/core_plugins/kibana/public/management/sections/objects/index.js rename to src/plugins/saved_objects_management/server/routes/get_allowed_types.ts index 3965c42ac088d..ab5bec6678946 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/index.js +++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts @@ -17,20 +17,24 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; -import { management } from 'ui/management'; -import './_view'; -import './_objects'; -import 'ace'; -import { uiModules } from 'ui/modules'; +import { IRouter } from 'src/core/server'; -// add the module deps to this module -uiModules.get('apps/management'); +export const registerGetAllowedTypesRoute = (router: IRouter) => { + router.get( + { + path: '/api/kibana/management/saved_objects/_allowed_types', + validate: false, + }, + async (context, req, res) => { + const allowedTypes = context.core.savedObjects.typeRegistry + .getImportableAndExportableTypes() + .map(type => type.name); -management.getSection('kibana').register('objects', { - display: i18n.translate('kbn.management.objects.savedObjectsSectionLabel', { - defaultMessage: 'Saved Objects', - }), - order: 10, - url: '#/management/kibana/objects', -}); + return res.ok({ + body: { + types: allowedTypes, + }, + }); + } + ); +}; diff --git a/src/plugins/saved_objects_management/server/routes/index.test.ts b/src/plugins/saved_objects_management/server/routes/index.test.ts index f183972953dce..237760444f04e 100644 --- a/src/plugins/saved_objects_management/server/routes/index.test.ts +++ b/src/plugins/saved_objects_management/server/routes/index.test.ts @@ -34,7 +34,7 @@ describe('registerRoutes', () => { }); expect(httpSetup.createRouter).toHaveBeenCalledTimes(1); - expect(router.get).toHaveBeenCalledTimes(2); + expect(router.get).toHaveBeenCalledTimes(3); expect(router.post).toHaveBeenCalledTimes(2); expect(router.get).toHaveBeenCalledWith( @@ -49,6 +49,12 @@ describe('registerRoutes', () => { }), expect.any(Function) ); + expect(router.get).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/kibana/management/saved_objects/_allowed_types', + }), + expect.any(Function) + ); expect(router.post).toHaveBeenCalledWith( expect.objectContaining({ path: '/api/kibana/management/saved_objects/scroll/counts', diff --git a/src/plugins/saved_objects_management/server/routes/index.ts b/src/plugins/saved_objects_management/server/routes/index.ts index 2c6adb71ed3ce..0929de56b215e 100644 --- a/src/plugins/saved_objects_management/server/routes/index.ts +++ b/src/plugins/saved_objects_management/server/routes/index.ts @@ -23,6 +23,7 @@ import { registerFindRoute } from './find'; import { registerScrollForCountRoute } from './scroll_count'; import { registerScrollForExportRoute } from './scroll_export'; import { registerRelationshipsRoute } from './relationships'; +import { registerGetAllowedTypesRoute } from './get_allowed_types'; interface RegisterRouteOptions { http: HttpServiceSetup; @@ -35,4 +36,5 @@ export function registerRoutes({ http, managementServicePromise }: RegisterRoute registerScrollForCountRoute(router); registerScrollForExportRoute(router); registerRelationshipsRoute(router, managementServicePromise); + registerGetAllowedTypesRoute(router); } diff --git a/src/plugins/saved_objects_management/server/types.ts b/src/plugins/saved_objects_management/server/types.ts index 5c4763d357e87..bd17d6a19ae70 100644 --- a/src/plugins/saved_objects_management/server/types.ts +++ b/src/plugins/saved_objects_management/server/types.ts @@ -17,38 +17,10 @@ * under the License. */ -import { SavedObject } from 'src/core/server'; - // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectsManagementPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SavedObjectsManagementPluginStart {} -/** - * The metadata injected into a {@link SavedObject | saved object} when returning - * {@link SavedObjectWithMetadata | enhanced objects} from the plugin API endpoints. - */ -export interface SavedObjectMetadata { - icon?: string; - title?: string; - editUrl?: string; - inAppUrl?: { path: string; uiCapabilitiesPath: string }; -} - -/** - * A {@link SavedObject | saved object} enhanced with meta properties used by the client-side plugin. - */ -export type SavedObjectWithMetadata = SavedObject & { - meta: SavedObjectMetadata; -}; - -/** - * Represents a relation between two {@link SavedObject | saved object} - */ -export interface SavedObjectRelation { - id: string; - type: string; - relationship: 'child' | 'parent'; - meta: SavedObjectMetadata; -} +export { SavedObjectMetadata, SavedObjectWithMetadata, SavedObjectRelation } from '../common'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx index 7d1cb08cb8b1c..40480905350af 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx @@ -6,7 +6,7 @@ import React, { useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useUrlParams } from '../../../hooks'; +import { useGetUrlParams } from '../../../hooks'; import { getAnomalyRecordsAction, getMLCapabilitiesAction, @@ -28,13 +28,12 @@ interface Props { } export const DurationChart: React.FC = ({ monitorId }: Props) => { - const [getUrlParams] = useUrlParams(); const { dateRangeStart, dateRangeEnd, absoluteDateRangeStart, absoluteDateRangeEnd, - } = getUrlParams(); + } = useGetUrlParams(); const { durationLines, loading } = useSelector(selectDurationLines); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx index 50f91be4ff09f..6428ddfd10f8c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx @@ -15,7 +15,7 @@ import { getPingHistogram } from '../../../state/actions'; import { selectPingHistogram } from '../../../state/selectors'; import { withResponsiveWrapper, ResponsiveWrapperProps } from '../../higher_order'; import { GetPingHistogramParams, HistogramResult } from '../../../../common/types'; -import { useUrlParams } from '../../../hooks'; +import { useGetUrlParams } from '../../../hooks'; type Props = ResponsiveWrapperProps & Pick & @@ -30,14 +30,13 @@ const PingHistogramContainer: React.FC = ({ loading, esKuery, }) => { - const [getUrlParams] = useUrlParams(); const { absoluteDateRangeStart, absoluteDateRangeEnd, dateRangeStart: dateStart, dateRangeEnd: dateEnd, statusFilter, - } = getUrlParams(); + } = useGetUrlParams(); useEffect(() => { loadData({ monitorId, dateStart, dateEnd, statusFilter, filters: esKuery }); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx index ac8ff13d1edce..39ead242527f8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx @@ -6,7 +6,7 @@ import React, { useEffect } from 'react'; import { connect } from 'react-redux'; -import { useUrlParams } from '../../../hooks'; +import { useGetUrlParams } from '../../../hooks'; import { AppState } from '../../../state'; import { getSnapshotCountAction } from '../../../state/actions'; import { SnapshotComponent } from '../../functional/snapshot'; @@ -54,8 +54,7 @@ export const Container: React.FC = ({ esKuery, loadSnapshotCount, }: Props) => { - const [getUrlParams] = useUrlParams(); - const { dateRangeStart, dateRangeEnd, statusFilter } = getUrlParams(); + const { dateRangeStart, dateRangeEnd, statusFilter } = useGetUrlParams(); useEffect(() => { loadSnapshotCount({ dateRangeStart, dateRangeEnd, filters: esKuery, statusFilter }); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx index b383a696095a3..55c92e70b6066 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx @@ -4,20 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; +import React, { useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { indexStatusAction } from '../../../state/actions'; import { indexStatusSelector } from '../../../state/selectors'; import { EmptyStateComponent } from '../../functional/empty_state/empty_state'; +import { UptimeRefreshContext } from '../../../contexts'; export const EmptyState: React.FC = ({ children }) => { const { data, loading, error } = useSelector(indexStatusSelector); + const { lastRefresh } = useContext(UptimeRefreshContext); const dispatch = useDispatch(); useEffect(() => { dispatch(indexStatusAction.get()); - }, [dispatch]); + }, [dispatch, lastRefresh]); return ( = ({ summary, loadMonitorDetails, monitorDetails }) => { const monitorId = summary?.monitor_id; - const [getUrlParams] = useUrlParams(); - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams(); + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); useEffect(() => { loadMonitorDetails({ diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx index dd6f7a89cf9a3..00cfd02d8dc32 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx @@ -11,7 +11,7 @@ import { AppState } from '../../../state'; import { monitorLocationsSelector, monitorStatusSelector } from '../../../state/selectors'; import { MonitorStatusBarComponent } from '../../functional/monitor_status_details/monitor_status_bar'; import { getMonitorStatusAction } from '../../../state/actions'; -import { useUrlParams } from '../../../hooks'; +import { useGetUrlParams } from '../../../hooks'; import { Ping } from '../../../../common/graphql/types'; import { MonitorLocations } from '../../../../common/runtime_types/monitor'; import { UptimeRefreshContext } from '../../../contexts'; @@ -39,8 +39,7 @@ const Container: React.FC = ({ }: Props) => { const { lastRefresh } = useContext(UptimeRefreshContext); - const [getUrlParams] = useUrlParams(); - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams(); + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); useEffect(() => { loadMonitorStatus({ dateStart, dateEnd, monitorId }); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx index 3ced251dfab8c..9d2e48830fbfe 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx @@ -7,7 +7,7 @@ import React, { useContext, useEffect } from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { useUrlParams } from '../../../hooks'; +import { useGetUrlParams } from '../../../hooks'; import { AppState } from '../../../state'; import { monitorLocationsSelector } from '../../../state/selectors'; import { getMonitorLocationsAction, MonitorLocationsPayload } from '../../../state/actions/monitor'; @@ -36,8 +36,7 @@ export const Container: React.FC = ({ }: Props) => { const { lastRefresh } = useContext(UptimeRefreshContext); - const [getUrlParams] = useUrlParams(); - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = getUrlParams(); + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); useEffect(() => { loadMonitorLocations({ dateStart, dateEnd, monitorId }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx index 036882b49359f..1963a9c852b11 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx @@ -8,7 +8,7 @@ import { EuiText, EuiSpacer } from '@elastic/eui'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { MonitorPageLink } from '../monitor_page_link'; -import { useUrlParams } from '../../../../hooks'; +import { useGetUrlParams } from '../../../../hooks'; import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params'; import { MonitorError } from '../../../../../common/runtime_types'; @@ -30,8 +30,7 @@ interface MostRecentErrorProps { } export const MostRecentError = ({ error, monitorId, timestamp }: MostRecentErrorProps) => { - const [getUrlParams] = useUrlParams(); - const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = getUrlParams(); + const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = useGetUrlParams(); params.selectedPingStatus = 'down'; const linkParameters = stringifyUrlParams(params, true); diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap index 24ef7eda0d129..d83e45fea1aec 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap @@ -6,6 +6,7 @@ exports[`ML Confirm Job Delete shallow renders without errors 1`] = ` buttonColor="danger" cancelButtonText="Cancel" confirmButtonText="Delete" + data-test-subj="uptimeMLJobDeleteConfirmModel" defaultFocusedButton="confirm" onCancel={[MockFunction]} onConfirm={[MockFunction]} @@ -35,6 +36,7 @@ exports[`ML Confirm Job Delete shallow renders without errors while loading 1`] buttonColor="danger" cancelButtonText="Cancel" confirmButtonText="Delete" + data-test-subj="uptimeMLJobDeleteConfirmModel" defaultFocusedButton="confirm" onCancel={[MockFunction]} onConfirm={[MockFunction]} diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap index 2457488c4facc..fb40a42e47f75 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap @@ -4,6 +4,7 @@ exports[`ShowLicenseInfo renders without errors 1`] = ` Array [
diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap index ead27425c26f3..a83a1d99d7bb0 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap @@ -3,6 +3,7 @@ exports[`ML Flyout component renders without errors 1`] = `