diff --git a/src/plugins/custom_integrations/public/types.ts b/src/plugins/custom_integrations/public/types.ts index 946115329e2b5..af2939f2d1c19 100755 --- a/src/plugins/custom_integrations/public/types.ts +++ b/src/plugins/custom_integrations/public/types.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import type * as React from 'react'; import type { PresentationUtilPluginStart } from '../../presentation_util/public'; - -import { CustomIntegration } from '../common'; +import type { CustomIntegration } from '../common'; export interface CustomIntegrationsSetup { getAppendCustomIntegrations: () => Promise; diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index 3d70d138d80ed..e3369c2d571a6 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -5,7 +5,7 @@ "ui": true, "requiredPlugins": ["bfetch", "expressions", "uiActions", "share", "inspector", "fieldFormats", "dataViews"], "serviceFolders": ["search", "query", "autocomplete", "ui"], - "optionalPlugins": ["usageCollection"], + "optionalPlugins": ["usageCollection", "taskManager", "security"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaUtils", "kibanaReact", "inspector"], "owner": { diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json index fa98b98ff8e1c..4f147a6f8bc96 100644 --- a/src/plugins/home/tsconfig.json +++ b/src/plugins/home/tsconfig.json @@ -10,7 +10,7 @@ "include": ["common/**/*", "public/**/*", "server/**/*", "config.ts"], "references": [ { "path": "../../core/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, + { "path": "../data_views/tsconfig.json" }, { "path": "../custom_integrations/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, { "path": "../share/tsconfig.json" }, diff --git a/src/plugins/presentation_util/kibana.json b/src/plugins/presentation_util/kibana.json index 6c8d38a5f8a1e..2da10c219dee5 100644 --- a/src/plugins/presentation_util/kibana.json +++ b/src/plugins/presentation_util/kibana.json @@ -10,6 +10,7 @@ "server": true, "ui": true, "extraPublicDirs": ["common/lib"], - "requiredPlugins": ["savedObjects", "kibanaReact", "embeddable", "expressions", "dataViews"], + "requiredPlugins": ["kibanaReact", "embeddable", "expressions", "dataViews"], + "requiredBundles": [], "optionalPlugins": [] } diff --git a/src/plugins/presentation_util/public/components/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/plugins/presentation_util/public/components/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap new file mode 100644 index 0000000000000..91f99fd8e87dd --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/__snapshots__/saved_object_save_modal.test.tsx.snap @@ -0,0 +1,403 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SavedObjectSaveModal should render matching snapshot 1`] = ` + +
+ + + + + + + + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + + + + + + + Save + + +
+
+`; + +exports[`SavedObjectSaveModal should render matching snapshot when custom isValid is set 1`] = ` + +
+ + + + + + + + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + + + + + + + Save + + +
+
+`; + +exports[`SavedObjectSaveModal should render matching snapshot when custom isValid is set 2`] = ` + +
+ + + + + + + + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + + + + + + + Save + + +
+
+`; + +exports[`SavedObjectSaveModal should render matching snapshot when given options 1`] = ` + +
+ + + + + + + + + + + } + labelType="label" + > + + + + } + labelType="label" + > + + +
+ Hello! Main options +
+
+ +
+ Hey there! Options on the right +
+
+
+
+
+ + + + + + Save + + +
+
+`; diff --git a/src/plugins/presentation_util/public/components/save_modal/_index.scss b/src/plugins/presentation_util/public/components/save_modal/_index.scss new file mode 100644 index 0000000000000..6c773c7f777be --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/_index.scss @@ -0,0 +1 @@ +@import './saved_object_save_modal'; diff --git a/src/plugins/presentation_util/public/components/save_modal/index.ts b/src/plugins/presentation_util/public/components/save_modal/index.ts new file mode 100644 index 0000000000000..8c23b797a05db --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { OnSaveProps, SaveModalState } from './saved_object_save_modal'; +export { SavedObjectSaveModal } from './saved_object_save_modal'; +export type { OriginSaveModalProps } from './saved_object_save_modal_origin'; +export { SavedObjectSaveModalOrigin } from './saved_object_save_modal_origin'; +export type { SaveResult } from './show_saved_object_save_modal'; +export { showSaveModal } from './show_saved_object_save_modal'; diff --git a/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.scss b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.scss new file mode 100644 index 0000000000000..cf33a84c0576a --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.scss @@ -0,0 +1,7 @@ +.kbnSavedObjectSaveModal { + width: $euiSizeXXL * 10; +} + +.kbnSavedObjectsSaveModal--wide { + width: 800px; +} diff --git a/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.test.tsx b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.test.tsx new file mode 100644 index 0000000000000..8e939ec58a792 --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.test.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { shallow } from 'enzyme'; +import React from 'react'; +import { SavedObjectSaveModal } from './saved_object_save_modal'; + +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +describe('SavedObjectSaveModal', () => { + it('should render matching snapshot', () => { + const wrapper = shallow( + void 0} + onClose={() => void 0} + title={'Saved Object title'} + showCopyOnSave={false} + objectType="visualization" + showDescription={true} + /> + ); + expect(wrapper).toMatchSnapshot(); + }); + + it('should render matching snapshot when given options', () => { + const wrapper = shallow( + void 0} + onClose={() => void 0} + title={'Saved Object title'} + showCopyOnSave={false} + objectType="visualization" + showDescription={true} + options={
Hello! Main options
} + rightOptions={
Hey there! Options on the right
} + /> + ); + expect(wrapper).toMatchSnapshot(); + }); + + it('should render matching snapshot when custom isValid is set', () => { + const falseWrapper = shallow( + void 0} + onClose={() => void 0} + title={'Saved Object title'} + showCopyOnSave={false} + objectType="visualization" + showDescription={true} + isValid={false} + /> + ); + expect(falseWrapper).toMatchSnapshot(); + + const trueWrapper = shallow( + void 0} + onClose={() => void 0} + title={'Saved Object title'} + showCopyOnSave={false} + objectType="visualization" + showDescription={true} + isValid={true} + /> + ); + expect(trueWrapper).toMatchSnapshot(); + }); + + it('allows specifying custom save button label', () => { + const wrapper = mountWithIntl( + void 0} + onClose={() => void 0} + title={'Saved Object title'} + showCopyOnSave={false} + objectType="visualization" + showDescription={true} + confirmButtonLabel="Save and done" + /> + ); + expect(wrapper.find('button[data-test-subj="confirmSaveSavedObjectButton"]').text()).toBe( + 'Save and done' + ); + }); +}); diff --git a/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.tsx b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.tsx new file mode 100644 index 0000000000000..eb60ce0e45e80 --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal.tsx @@ -0,0 +1,345 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + htmlIdGenerator, + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, + EuiSwitch, + EuiSwitchEvent, + EuiTextArea, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export interface OnSaveProps { + newTitle: string; + newCopyOnSave: boolean; + isTitleDuplicateConfirmed: boolean; + onTitleDuplicate: () => void; + newDescription: string; +} + +interface Props { + onSave: (props: OnSaveProps) => void; + onClose: () => void; + title: string; + showCopyOnSave: boolean; + onCopyOnSaveChange?: (copyOnChange: boolean) => void; + initialCopyOnSave?: boolean; + objectType: string; + confirmButtonLabel?: React.ReactNode; + options?: React.ReactNode | ((state: SaveModalState) => React.ReactNode); + rightOptions?: React.ReactNode | ((state: SaveModalState) => React.ReactNode); + description?: string; + showDescription: boolean; + isValid?: boolean; +} + +export interface SaveModalState { + title: string; + copyOnSave: boolean; + isTitleDuplicateConfirmed: boolean; + hasTitleDuplicate: boolean; + isLoading: boolean; + visualizationDescription: string; +} + +const generateId = htmlIdGenerator(); + +/** @deprecated */ +export class SavedObjectSaveModal extends React.Component { + private warning = React.createRef(); + public readonly state = { + title: this.props.title, + copyOnSave: Boolean(this.props.initialCopyOnSave), + isTitleDuplicateConfirmed: false, + hasTitleDuplicate: false, + isLoading: false, + visualizationDescription: this.props.description ? this.props.description : '', + }; + + public render() { + const { isTitleDuplicateConfirmed, hasTitleDuplicate, title } = this.state; + const duplicateWarningId = generateId(); + + const hasColumns = !!this.props.rightOptions; + + const formBodyContent = ( + <> + + } + > + + + + {this.renderViewDescription()} + + {typeof this.props.options === 'function' + ? this.props.options(this.state) + : this.props.options} + + ); + + const formBody = hasColumns ? ( + + {formBodyContent} + + {typeof this.props.rightOptions === 'function' + ? this.props.rightOptions(this.state) + : this.props.rightOptions} + + + ) : ( + formBodyContent + ); + + return ( + +
+ + + + + + + + {this.renderDuplicateTitleCallout(duplicateWarningId)} + + + {!this.props.showDescription && this.props.description && ( + + {this.props.description} + + )} + {formBody} + {this.renderCopyOnSave()} + + + + + + + + + {this.renderConfirmButton()} + +
+
+ ); + } + + private renderViewDescription = () => { + if (!this.props.showDescription) { + return; + } + + return ( + + } + > + + + ); + }; + + private onDescriptionChange = (event: React.ChangeEvent) => { + this.setState({ + visualizationDescription: event.target.value, + }); + }; + + private onTitleDuplicate = () => { + this.setState({ + isLoading: false, + isTitleDuplicateConfirmed: true, + hasTitleDuplicate: true, + }); + + if (this.warning.current) { + this.warning.current.focus(); + } + }; + + private saveSavedObject = async () => { + if (this.state.isLoading) { + // ignore extra clicks + return; + } + + this.setState({ + isLoading: true, + }); + + await this.props.onSave({ + newTitle: this.state.title, + newCopyOnSave: this.state.copyOnSave, + isTitleDuplicateConfirmed: this.state.isTitleDuplicateConfirmed, + onTitleDuplicate: this.onTitleDuplicate, + newDescription: this.state.visualizationDescription, + }); + }; + + private onTitleChange = (event: React.ChangeEvent) => { + this.setState({ + title: event.target.value, + isTitleDuplicateConfirmed: false, + hasTitleDuplicate: false, + }); + }; + + private onCopyOnSaveChange = (event: EuiSwitchEvent) => { + this.setState({ + copyOnSave: event.target.checked, + }); + + if (this.props.onCopyOnSaveChange) { + this.props.onCopyOnSaveChange(event.target.checked); + } + }; + + private onFormSubmit = (event: React.FormEvent) => { + event.preventDefault(); + this.saveSavedObject(); + }; + + private renderConfirmButton = () => { + const { isLoading, title } = this.state; + + let confirmLabel: string | React.ReactNode = i18n.translate( + 'presentationUtil.saveModal.saveButtonLabel', + { + defaultMessage: 'Save', + } + ); + + if (this.props.confirmButtonLabel) { + confirmLabel = this.props.confirmButtonLabel; + } + + const isValid = this.props.isValid !== undefined ? this.props.isValid : true; + + return ( + + {confirmLabel} + + ); + }; + + private renderDuplicateTitleCallout = (duplicateWarningId: string) => { + if (!this.state.hasTitleDuplicate) { + return; + } + + return ( + <> +
+ + } + color="warning" + data-test-subj="titleDupicateWarnMsg" + id={duplicateWarningId} + > +

+ +

+
+
+ + + ); + }; + + private renderCopyOnSave = () => { + if (!this.props.showCopyOnSave) { + return; + } + + return ( + <> + + + } + /> + + ); + }; +} diff --git a/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal_origin.tsx b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal_origin.tsx new file mode 100644 index 0000000000000..f6660c2c6ebf8 --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/saved_object_save_modal_origin.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { Fragment, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFormRow, EuiSwitch } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { OnSaveProps, SaveModalState, SavedObjectSaveModal } from '.'; + +interface SaveModalDocumentInfo { + id?: string; + title: string; + description?: string; +} + +export interface OriginSaveModalProps { + originatingApp?: string; + getAppNameFromId?: (appId: string) => string | undefined; + originatingAppName?: string; + returnToOriginSwitchLabel?: string; + documentInfo: SaveModalDocumentInfo; + objectType: string; + onClose: () => void; + options?: React.ReactNode | ((state: SaveModalState) => React.ReactNode); + onSave: (props: OnSaveProps & { returnToOrigin: boolean }) => void; +} + +export function SavedObjectSaveModalOrigin(props: OriginSaveModalProps) { + const [returnToOriginMode, setReturnToOriginMode] = useState(Boolean(props.originatingApp)); + const { documentInfo } = props; + + const returnLabel = i18n.translate('presentationUtil.saveModalOrigin.returnToOriginLabel', { + defaultMessage: 'Return', + }); + const addLabel = i18n.translate('presentationUtil.saveModalOrigin.addToOriginLabel', { + defaultMessage: 'Add', + }); + + const getReturnToOriginSwitch = (state: SaveModalState) => { + const sourceOptions = + typeof props.options === 'function' ? props.options(state) : props.options; + + if (!props.originatingApp) { + return sourceOptions; + } + const origin = props.getAppNameFromId + ? props.getAppNameFromId(props.originatingApp) || props.originatingApp + : props.originatingApp; + + if ( + !state.copyOnSave || + props.originatingApp === 'dashboards' // dashboard supports adding a copied panel on save... + ) { + const originVerb = !documentInfo.id || state.copyOnSave ? addLabel : returnLabel; + return ( + + {sourceOptions} + + { + setReturnToOriginMode(event.target.checked); + }} + label={ + props.returnToOriginSwitchLabel ?? ( + + ) + } + /> + + + ); + } else { + setReturnToOriginMode(false); + return sourceOptions; + } + }; + + const onModalSave = (onSaveProps: OnSaveProps) => { + props.onSave({ ...onSaveProps, returnToOrigin: returnToOriginMode }); + }; + + const confirmButtonLabel = returnToOriginMode + ? i18n.translate('presentationUtil.saveModalOrigin.saveAndReturnLabel', { + defaultMessage: 'Save and return', + }) + : null; + + return ( + + ); +} diff --git a/src/plugins/presentation_util/public/components/save_modal/show_saved_object_save_modal.tsx b/src/plugins/presentation_util/public/components/save_modal/show_saved_object_save_modal.tsx new file mode 100644 index 0000000000000..7588a0893f95b --- /dev/null +++ b/src/plugins/presentation_util/public/components/save_modal/show_saved_object_save_modal.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import type { I18nStart } from 'src/core/public'; + +/** + * Represents the result of trying to persist the saved object. + * Contains `error` prop if something unexpected happened (e.g. network error). + * Contains an `id` if persisting was successful. If `id` and + * `error` are undefined, persisting was not successful, but the + * modal can still recover (e.g. the name of the saved object was already taken). + */ +export type SaveResult = { id?: string } | { error: Error }; + +function isSuccess(result: SaveResult): result is { id?: string } { + return 'id' in result; +} + +interface MinimalSaveModalProps { + onSave: (...args: any[]) => Promise; + onClose: () => void; +} + +export function showSaveModal( + saveModal: React.ReactElement, + I18nContext: I18nStart['Context'], + Wrapper?: React.FC +) { + const container = document.createElement('div'); + const closeModal = () => { + ReactDOM.unmountComponentAtNode(container); + document.body.removeChild(container); + }; + + const onSave = saveModal.props.onSave; + + const onSaveConfirmed: MinimalSaveModalProps['onSave'] = async (...args) => { + const response = await onSave(...args); + // close modal if we either hit an error or the saved object got an id + if (Boolean(isSuccess(response) ? response.id : response.error)) { + closeModal(); + } + return response; + }; + document.body.appendChild(container); + const element = React.cloneElement(saveModal, { + onSave: onSaveConfirmed, + onClose: closeModal, + }); + + const wrappedElement = Wrapper ? ( + + {element} + + ) : ( + {element} + ); + + ReactDOM.render(wrappedElement, container); +} diff --git a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx index 6c36cf8b8e3a7..95a4f80a2f4bc 100644 --- a/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx +++ b/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx @@ -10,7 +10,7 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { OnSaveProps, SavedObjectSaveModal } from '../../../../plugins/saved_objects/public'; +import { OnSaveProps, SavedObjectSaveModal } from './save_modal'; import { pluginServices } from '../services'; import { SaveModalDashboardProps } from './types'; diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 4bc06d51e7e0b..3086029f58300 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -414,8 +414,10 @@ export class FleetPlugin try { // Fleet remains `available` during setup as to excessively delay Kibana's boot process. // This should be reevaluated as Fleet's setup process is optimized and stabilized. + // + // UPDATE: Back to `degraded` because CI is affected by the load generating during installing the artifacts (randomly timing out some tests) this.fleetStatus$.next({ - level: ServiceStatusLevels.available, + level: ServiceStatusLevels.degraded, summary: 'Fleet is setting up', }); diff --git a/x-pack/plugins/security/kibana.json b/x-pack/plugins/security/kibana.json index 2eeac40e22f14..3d0bd9cbcbedc 100644 --- a/x-pack/plugins/security/kibana.json +++ b/x-pack/plugins/security/kibana.json @@ -8,7 +8,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "security"], - "requiredPlugins": ["data", "features", "licensing", "taskManager"], + "requiredPlugins": ["dataViews", "features", "licensing", "taskManager"], "optionalPlugins": ["home", "management", "usageCollection", "spaces", "share"], "server": true, "ui": true, diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index 3c723bdfcc988..18a0ad37b88fc 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -44,7 +44,7 @@ export const rolesManagementApp = Object.freeze({ title, async mount({ element, theme$, setBreadcrumbs, history }) { const [ - [startServices, { data, features, spaces }], + [startServices, { dataViews, features, spaces }], { RolesGridPage }, { EditRolePage }, { RolesAPIClient }, @@ -108,7 +108,7 @@ export const rolesManagementApp = Object.freeze({ license={license} docLinks={docLinks} uiCapabilities={application.capabilities} - dataViews={data.dataViews} + dataViews={dataViews} history={history} spacesApiUi={spacesApiUi} /> diff --git a/x-pack/plugins/security/public/plugin.test.tsx b/x-pack/plugins/security/public/plugin.test.tsx index 2bc4932b12a0b..98d0ea0ab25a2 100644 --- a/x-pack/plugins/security/public/plugin.test.tsx +++ b/x-pack/plugins/security/public/plugin.test.tsx @@ -10,7 +10,7 @@ import { Observable } from 'rxjs'; import type { CoreSetup } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; -import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { DataViewsPublicPluginStart } from 'src/plugins/data_views/public'; import { managementPluginMock } from 'src/plugins/management/public/mocks'; import type { FeaturesPluginStart } from '../../features/public'; @@ -92,7 +92,7 @@ describe('Security Plugin', () => { expect( plugin.start(coreMock.createStart({ basePath: '/some-base-path' }), { - data: {} as DataPublicPluginStart, + dataViews: {} as DataViewsPublicPluginStart, features: {} as FeaturesPluginStart, }) ).toEqual({ @@ -133,7 +133,7 @@ describe('Security Plugin', () => { const coreStart = coreMock.createStart({ basePath: '/some-base-path' }); plugin.start(coreStart, { - data: {} as DataPublicPluginStart, + dataViews: {} as DataViewsPublicPluginStart, features: {} as FeaturesPluginStart, management: managementStartMock, }); @@ -162,7 +162,7 @@ describe('Security Plugin', () => { ); plugin.start(coreMock.createStart({ basePath: '/some-base-path' }), { - data: {} as DataPublicPluginStart, + dataViews: {} as DataViewsPublicPluginStart, features: {} as FeaturesPluginStart, }); diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index c2860ec059b8d..02618bbc7977a 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; -import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { DataViewsPublicPluginStart } from 'src/plugins/data_views/public'; import type { HomePublicPluginSetup } from 'src/plugins/home/public'; import type { ManagementSetup, ManagementStart } from 'src/plugins/management/public'; @@ -39,7 +39,7 @@ export interface PluginSetupDependencies { } export interface PluginStartDependencies { - data: DataPublicPluginStart; + dataViews: DataViewsPublicPluginStart; features: FeaturesPluginStart; management?: ManagementStart; spaces?: SpacesPluginStart; diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 5cc25bbb44055..e4566248efc46 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -12,7 +12,7 @@ { "path": "../licensing/tsconfig.json" }, { "path": "../spaces/tsconfig.json" }, { "path": "../task_manager/tsconfig.json" }, - { "path": "../../../src/plugins/data/tsconfig.json" }, + { "path": "../../../src/plugins/data_views/tsconfig.json" }, { "path": "../../../src/plugins/es_ui_shared/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts index 20f11fe3b8900..f0af4191e7868 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts @@ -69,11 +69,11 @@ export class SyntheticsService { // } // }); - this.setupIndexTemplates(); + return this.setupIndexTemplates(); } private setupIndexTemplates() { - installSyntheticsIndexTemplates(this.server).then( + return installSyntheticsIndexTemplates(this.server).then( (result) => { if (result.name === 'synthetics' && result.install_status === 'installed') { this.logger.info('Installed synthetics index templates'); diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts index df88f895f2f7d..9cc541ed4b285 100644 --- a/x-pack/plugins/uptime/server/plugin.ts +++ b/x-pack/plugins/uptime/server/plugin.ts @@ -4,15 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { +import type { PluginInitializerContext, CoreStart, CoreSetup, Plugin as PluginType, Logger, - SavedObjectsClient, SavedObjectsClientContract, -} from '../../../../src/core/server'; + ServiceStatus, +} from 'src/core/server'; +import { BehaviorSubject } from 'rxjs'; +import { SavedObjectsClient, ServiceStatusLevels } from '../../../../src/core/server'; import { uptimeRuleFieldMap } from '../common/rules/uptime_rule_field_map'; import { initServerWithKibana } from './kibana.index'; import { @@ -32,6 +34,10 @@ import { syntheticsServiceApiKey } from './lib/saved_objects/service_api_key'; export type UptimeRuleRegistry = ReturnType['ruleRegistry']; export class Plugin implements PluginType { + private readonly status$ = new BehaviorSubject({ + level: ServiceStatusLevels.unavailable, + summary: 'Initializing service', + }); private savedObjectsClient?: SavedObjectsClientContract; private initContext: PluginInitializerContext; private logger: Logger; @@ -46,6 +52,8 @@ export class Plugin implements PluginType { public setup(core: CoreSetup, plugins: UptimeCorePluginsSetup) { const config = this.initContext.config.get(); + core.status.set(this.status$.asObservable()); + savedObjectsAdapter.config = config; this.logger = this.initContext.logger.get(); @@ -118,13 +126,25 @@ export class Plugin implements PluginType { } if (this.server?.config?.service?.enabled) { - this.syntheticService?.init(); + this.syntheticService?.init().then(() => { + this.status$.next({ + level: ServiceStatusLevels.available, + summary: 'Ready', + }); + }); this.syntheticService?.scheduleSyncTask(plugins.taskManager); if (this.server && this.syntheticService) { this.server.syntheticsService = this.syntheticService; } + } else { + this.status$.next({ + level: ServiceStatusLevels.available, + summary: 'Ready', + }); } } - public stop() {} + public stop() { + this.status$.complete(); + } }