[8.x] [Embeddable Rebuild] [Controls] Clean up services + TODOs (#193180) (#193429)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Embeddable Rebuild] [Controls] Clean up services + TODOs
(#193180)](https://github.com/elastic/kibana/pull/193180)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Hannah
Mudge","email":"Heenawter@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-09-19T14:02:49Z","message":"[Embeddable
Rebuild] [Controls] Clean up services + TODOs (#193180)\n\nPart of
https://github.com/elastic/kibana/issues/192005\r\nCloses
https://github.com/elastic/kibana/issues/167438\r\n\r\n##
Summary\r\n\r\n\r\n## Summary\r\n\r\nThis PR represents the second major
cleanup task for the control group\r\nembeddable refactor. The tasks
included in this PR can be loosely\r\nsummarized as follows:\r\n1. It
removes the old, cumbersome services implementation and replaces\r\nit
with a much simpler system (which is the same one used in the
`links`\r\n+ `presentation_panel` plugins).\r\n- This it the main reason
for the decrease in lines - the old system\r\nrequired a **huge** amount
of boilerplate code, which is no longer\r\nnecessary with the new method
for storing services.\r\n2. It addresses and/or removes any remaining
TODO comments in the\r\n`controls` plugin\r\n- This includes renaming
`ControlStyle` and `DEFAULT_CONTROL_STYLE` to\r\n`ControlLabelPosition`
and `DEFAULT_CONTROL_LABEL_POSITION`\r\nrespectively, which represents a
bulk of the changes.\r\n3. It moves all compatibility checks for all
control actions to be async\r\nimported.\r\n4. It removes the ability to
register controls from the `controls`\r\nplugin setup
contract.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"db5557429979b9a0f3420a50a06c7fd69cbdf5b2","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Presentation","release_note:skip","impact:high","v9.0.0","backport:prev-minor"],"title":"[Embeddable
Rebuild] [Controls] Clean up services +
TODOs","number":193180,"url":"https://github.com/elastic/kibana/pull/193180","mergeCommit":{"message":"[Embeddable
Rebuild] [Controls] Clean up services + TODOs (#193180)\n\nPart of
https://github.com/elastic/kibana/issues/192005\r\nCloses
https://github.com/elastic/kibana/issues/167438\r\n\r\n##
Summary\r\n\r\n\r\n## Summary\r\n\r\nThis PR represents the second major
cleanup task for the control group\r\nembeddable refactor. The tasks
included in this PR can be loosely\r\nsummarized as follows:\r\n1. It
removes the old, cumbersome services implementation and replaces\r\nit
with a much simpler system (which is the same one used in the
`links`\r\n+ `presentation_panel` plugins).\r\n- This it the main reason
for the decrease in lines - the old system\r\nrequired a **huge** amount
of boilerplate code, which is no longer\r\nnecessary with the new method
for storing services.\r\n2. It addresses and/or removes any remaining
TODO comments in the\r\n`controls` plugin\r\n- This includes renaming
`ControlStyle` and `DEFAULT_CONTROL_STYLE` to\r\n`ControlLabelPosition`
and `DEFAULT_CONTROL_LABEL_POSITION`\r\nrespectively, which represents a
bulk of the changes.\r\n3. It moves all compatibility checks for all
control actions to be async\r\nimported.\r\n4. It removes the ability to
register controls from the `controls`\r\nplugin setup
contract.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"db5557429979b9a0f3420a50a06c7fd69cbdf5b2"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193180","number":193180,"mergeCommit":{"message":"[Embeddable
Rebuild] [Controls] Clean up services + TODOs (#193180)\n\nPart of
https://github.com/elastic/kibana/issues/192005\r\nCloses
https://github.com/elastic/kibana/issues/167438\r\n\r\n##
Summary\r\n\r\n\r\n## Summary\r\n\r\nThis PR represents the second major
cleanup task for the control group\r\nembeddable refactor. The tasks
included in this PR can be loosely\r\nsummarized as follows:\r\n1. It
removes the old, cumbersome services implementation and replaces\r\nit
with a much simpler system (which is the same one used in the
`links`\r\n+ `presentation_panel` plugins).\r\n- This it the main reason
for the decrease in lines - the old system\r\nrequired a **huge** amount
of boilerplate code, which is no longer\r\nnecessary with the new method
for storing services.\r\n2. It addresses and/or removes any remaining
TODO comments in the\r\n`controls` plugin\r\n- This includes renaming
`ControlStyle` and `DEFAULT_CONTROL_STYLE` to\r\n`ControlLabelPosition`
and `DEFAULT_CONTROL_LABEL_POSITION`\r\nrespectively, which represents a
bulk of the changes.\r\n3. It moves all compatibility checks for all
control actions to be async\r\nimported.\r\n4. It removes the ability to
register controls from the `controls`\r\nplugin setup
contract.\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"db5557429979b9a0f3420a50a06c7fd69cbdf5b2"}}]}]
BACKPORT-->

Co-authored-by: Hannah Mudge <Heenawter@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-09-20 02:26:08 +10:00 committed by GitHub
parent b0853caa12
commit b2e6263d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
91 changed files with 399 additions and 1408 deletions

View file

@ -7,11 +7,11 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { ControlStyle, ControlWidth } from './types';
import { ControlLabelPosition, ControlWidth } from './types';
export const DEFAULT_CONTROL_WIDTH: ControlWidth = 'medium';
export const DEFAULT_CONTROL_GROW: boolean = true;
export const DEFAULT_CONTROL_STYLE: ControlStyle = 'oneLine';
export const DEFAULT_CONTROL_LABEL_POSITION: ControlLabelPosition = 'oneLine';
export const TIME_SLIDER_CONTROL = 'timeSlider';
export const RANGE_SLIDER_CONTROL = 'rangeSliderControl';

View file

@ -8,7 +8,7 @@
*/
import { DataViewField } from '@kbn/data-views-plugin/common';
import { ControlStyle, DefaultControlState, ParentIgnoreSettings } from '../types';
import { ControlLabelPosition, DefaultControlState, ParentIgnoreSettings } from '../types';
export const CONTROL_GROUP_TYPE = 'control_group';
@ -31,7 +31,7 @@ export interface ControlGroupEditorConfig {
export interface ControlGroupRuntimeState<State extends DefaultControlState = DefaultControlState> {
chainingSystem: ControlGroupChainingSystem;
labelPosition: ControlStyle; // TODO: Rename this type to ControlLabelPosition
labelPosition: ControlLabelPosition;
autoApplySelections: boolean;
ignoreParentSettings?: ParentIgnoreSettings;
@ -50,7 +50,7 @@ export interface ControlGroupSerializedState
ignoreParentSettingsJSON: string;
// In runtime state, we refer to this property as `labelPosition`;
// to avoid migrations, we will continue to refer to this property as `controlStyle` in the serialized state
controlStyle: ControlStyle;
controlStyle: ControlLabelPosition;
// In runtime state, we refer to the inverse of this property as `autoApplySelections`
// to avoid migrations, we will continue to refer to this property as `showApplySelections` in the serialized state
showApplySelections?: boolean;

View file

@ -8,7 +8,7 @@
*/
export type {
ControlStyle,
ControlLabelPosition,
ControlWidth,
DefaultControlState,
DefaultDataControlState,
@ -18,7 +18,7 @@ export type {
export {
DEFAULT_CONTROL_GROW,
DEFAULT_CONTROL_STYLE,
DEFAULT_CONTROL_LABEL_POSITION,
DEFAULT_CONTROL_WIDTH,
OPTIONS_LIST_CONTROL,
RANGE_SLIDER_CONTROL,

View file

@ -15,8 +15,6 @@ import { OptionsListSortingType } from './suggestions_sorting';
import { DefaultDataControlState } from '../types';
import { OptionsListSearchTechnique } from './suggestions_searching';
export const OPTIONS_LIST_CONTROL = 'optionsListControl'; // TODO: Replace with OPTIONS_LIST_CONTROL_TYPE
/**
* ----------------------------------------------------------------
* Options list state types

View file

@ -8,7 +8,7 @@
*/
export type ControlWidth = 'small' | 'medium' | 'large';
export type ControlStyle = 'twoLine' | 'oneLine';
export type ControlLabelPosition = 'twoLine' | 'oneLine';
export type TimeSlice = [number, number];

View file

@ -8,8 +8,5 @@
*/
// Start the services with stubs
import { pluginServices } from './public/services';
import { registry } from './public/services/plugin_services.stub';
registry.start({});
pluginServices.setRegistry(registry);
import { setStubKibanaServices } from './public/services/mocks';
setStubKibanaServices();

View file

@ -9,8 +9,6 @@
"browser": true,
"requiredPlugins": [
"presentationUtil",
"kibanaReact",
"expressions",
"embeddable",
"dataViews",
"data",
@ -18,6 +16,6 @@
"uiActions"
],
"extraPublicDirs": ["common"],
"requiredBundles": ["kibanaUtils"]
"requiredBundles": []
}
}

View file

@ -11,42 +11,10 @@ import React, { SyntheticEvent } from 'react';
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import {
apiIsPresentationContainer,
type PresentationContainer,
} from '@kbn/presentation-containers';
import {
apiCanAccessViewMode,
apiHasParentApi,
apiHasType,
apiHasUniqueId,
apiIsOfType,
type EmbeddableApiContext,
type HasParentApi,
type HasType,
type HasUniqueId,
} from '@kbn/presentation-publishing';
import { type Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import type { EmbeddableApiContext, HasUniqueId } from '@kbn/presentation-publishing';
import { IncompatibleActionError, type Action } from '@kbn/ui-actions-plugin/public';
import { ACTION_CLEAR_CONTROL } from '.';
import { CONTROL_GROUP_TYPE } from '..';
import { isClearableControl, type CanClearSelections } from '../types';
export type ClearControlActionApi = HasType &
HasUniqueId &
CanClearSelections &
HasParentApi<PresentationContainer & HasType>;
const isApiCompatible = (api: unknown | null): api is ClearControlActionApi =>
Boolean(
apiHasType(api) &&
apiHasUniqueId(api) &&
isClearableControl(api) &&
apiHasParentApi(api) &&
apiCanAccessViewMode(api.parentApi) &&
apiIsOfType(api.parentApi, CONTROL_GROUP_TYPE) &&
apiIsPresentationContainer(api.parentApi)
);
export class ClearControlAction implements Action<EmbeddableApiContext> {
public readonly type = ACTION_CLEAR_CONTROL;
@ -56,12 +24,10 @@ export class ClearControlAction implements Action<EmbeddableApiContext> {
constructor() {}
public readonly MenuItem = ({ context }: { context: EmbeddableApiContext }) => {
if (!isApiCompatible(context.embeddable)) throw new IncompatibleActionError();
return (
<EuiToolTip content={this.getDisplayName(context)}>
<EuiButtonIcon
data-test-subj={`control-action-${context.embeddable.uuid}-erase`}
data-test-subj={`control-action-${(context.embeddable as HasUniqueId).uuid}-erase`}
aria-label={this.getDisplayName(context)}
iconType={this.getIconType(context)}
onClick={(event: SyntheticEvent<HTMLButtonElement>) => {
@ -75,23 +41,24 @@ export class ClearControlAction implements Action<EmbeddableApiContext> {
};
public getDisplayName({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
return i18n.translate('controls.controlGroup.floatingActions.clearTitle', {
defaultMessage: 'Clear',
});
}
public getIconType({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
return 'eraser';
}
public async isCompatible({ embeddable }: EmbeddableApiContext) {
return isApiCompatible(embeddable);
const { isCompatible } = await import('./clear_control_action_compatibility_check');
return isCompatible(embeddable);
}
public async execute({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
const { compatibilityCheck } = await import('./clear_control_action_compatibility_check');
if (!compatibilityCheck(embeddable)) throw new IncompatibleActionError();
embeddable.clearSelections();
}
}

View file

@ -0,0 +1,42 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PresentationContainer, apiIsPresentationContainer } from '@kbn/presentation-containers';
import {
HasParentApi,
HasType,
HasUniqueId,
apiCanAccessViewMode,
apiHasParentApi,
apiHasType,
apiHasUniqueId,
apiIsOfType,
} from '@kbn/presentation-publishing';
import { CONTROL_GROUP_TYPE } from '../../common';
import { isClearableControl, type CanClearSelections } from '../types';
type ClearControlActionApi = HasType &
HasUniqueId &
CanClearSelections &
HasParentApi<PresentationContainer & HasType>;
export const compatibilityCheck = (api: unknown | null): api is ClearControlActionApi =>
Boolean(
apiHasType(api) &&
apiHasUniqueId(api) &&
isClearableControl(api) &&
apiHasParentApi(api) &&
apiCanAccessViewMode(api.parentApi) &&
apiIsOfType(api.parentApi, CONTROL_GROUP_TYPE) &&
apiIsPresentationContainer(api.parentApi)
);
export function isCompatible(api: unknown) {
return compatibilityCheck(api);
}

View file

@ -9,22 +9,15 @@
import { BehaviorSubject } from 'rxjs';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { ViewMode } from '@kbn/presentation-publishing';
import { getOptionsListControlFactory } from '../react_controls/controls/data_controls/options_list_control/get_options_list_control_factory';
import { OptionsListControlApi } from '../react_controls/controls/data_controls/options_list_control/types';
import {
getMockedBuildApi,
getMockedControlGroupApi,
} from '../react_controls/controls/mocks/control_mocks';
import { pluginServices } from '../services';
import { DeleteControlAction } from './delete_control_action';
const mockDataViews = dataViewPluginMocks.createStartContract();
const mockCore = coreMock.createStart();
import { coreServices } from '../services/kibana_services';
const dashboardApi = {
viewMode: new BehaviorSubject<ViewMode>('view'),
@ -38,11 +31,7 @@ const controlGroupApi = getMockedControlGroupApi(dashboardApi, {
let controlApi: OptionsListControlApi;
beforeAll(async () => {
const controlFactory = getOptionsListControlFactory({
core: mockCore,
data: dataPluginMock.createStartContract(),
dataViews: mockDataViews,
});
const controlFactory = getOptionsListControlFactory();
const uuid = 'testControl';
const control = await controlFactory.buildControl(
@ -72,7 +61,7 @@ test('Execute throws an error when called with an embeddable not in a parent', a
describe('Execute should open a confirm modal', () => {
test('Canceling modal will keep control', async () => {
const spyOn = jest.fn().mockResolvedValue(false);
pluginServices.getServices().overlays.openConfirm = spyOn;
coreServices.overlays.openConfirm = spyOn;
const deleteControlAction = new DeleteControlAction();
await deleteControlAction.execute({ embeddable: controlApi });
@ -83,7 +72,7 @@ describe('Execute should open a confirm modal', () => {
test('Confirming modal will delete control', async () => {
const spyOn = jest.fn().mockResolvedValue(true);
pluginServices.getServices().overlays.openConfirm = spyOn;
coreServices.overlays.openConfirm = spyOn;
const deleteControlAction = new DeleteControlAction();
await deleteControlAction.execute({ embeddable: controlApi });

View file

@ -10,65 +10,25 @@
import React from 'react';
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { ViewMode } from '@kbn/embeddable-plugin/public';
import { i18n } from '@kbn/i18n';
import {
apiIsPresentationContainer,
type PresentationContainer,
} from '@kbn/presentation-containers';
import {
apiCanAccessViewMode,
apiHasParentApi,
apiHasType,
apiHasUniqueId,
apiIsOfType,
getInheritedViewMode,
type EmbeddableApiContext,
type HasParentApi,
type HasType,
type HasUniqueId,
type PublishesViewMode,
} from '@kbn/presentation-publishing';
import type { HasUniqueId, EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError, type Action } from '@kbn/ui-actions-plugin/public';
import { ACTION_DELETE_CONTROL } from '.';
import { CONTROL_GROUP_TYPE } from '..';
import { pluginServices } from '../services';
export type DeleteControlActionApi = HasType &
HasUniqueId &
HasParentApi<PresentationContainer & PublishesViewMode & HasType>;
const isApiCompatible = (api: unknown | null): api is DeleteControlActionApi =>
Boolean(
apiHasType(api) &&
apiHasUniqueId(api) &&
apiHasParentApi(api) &&
apiCanAccessViewMode(api.parentApi) &&
apiIsOfType(api.parentApi, CONTROL_GROUP_TYPE) &&
apiIsPresentationContainer(api.parentApi)
);
import { coreServices } from '../services/kibana_services';
export class DeleteControlAction implements Action<EmbeddableApiContext> {
public readonly type = ACTION_DELETE_CONTROL;
public readonly id = ACTION_DELETE_CONTROL;
public order = 100; // should always be last
private openConfirm;
constructor() {
({
overlays: { openConfirm: this.openConfirm },
} = pluginServices.getServices());
}
constructor() {}
public readonly MenuItem = ({ context }: { context: EmbeddableApiContext }) => {
if (!isApiCompatible(context.embeddable)) throw new IncompatibleActionError();
return (
<EuiToolTip content={this.getDisplayName(context)}>
<EuiButtonIcon
data-test-subj={`control-action-${context.embeddable.uuid}-delete`}
data-test-subj={`control-action-${(context.embeddable as HasUniqueId).uuid}-delete`}
aria-label={this.getDisplayName(context)}
iconType={this.getIconType(context)}
onClick={() => this.execute(context)}
@ -79,27 +39,26 @@ export class DeleteControlAction implements Action<EmbeddableApiContext> {
};
public getDisplayName({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
return i18n.translate('controls.controlGroup.floatingActions.removeTitle', {
defaultMessage: 'Delete',
});
}
public getIconType({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
return 'trash';
}
public async isCompatible({ embeddable }: EmbeddableApiContext) {
return (
isApiCompatible(embeddable) && getInheritedViewMode(embeddable.parentApi) === ViewMode.EDIT
);
const { isCompatible } = await import('./delete_control_action_compatibility_check');
return isCompatible(embeddable);
}
public async execute({ embeddable }: EmbeddableApiContext) {
if (!isApiCompatible(embeddable)) throw new IncompatibleActionError();
const { compatibilityCheck } = await import('./delete_control_action_compatibility_check');
if (!compatibilityCheck(embeddable)) throw new IncompatibleActionError();
this.openConfirm(
coreServices.overlays
.openConfirm(
i18n.translate('controls.controlGroup.management.delete.sub', {
defaultMessage: 'Controls are not recoverable once removed.',
}),
@ -115,7 +74,8 @@ export class DeleteControlAction implements Action<EmbeddableApiContext> {
}),
buttonColor: 'danger',
}
).then((confirmed) => {
)
.then((confirmed) => {
if (confirmed) {
embeddable.parentApi.removePanel(embeddable.uuid);
}

View file

@ -0,0 +1,42 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { ViewMode } from '@kbn/embeddable-plugin/public';
import { PresentationContainer, apiIsPresentationContainer } from '@kbn/presentation-containers';
import {
HasParentApi,
HasType,
HasUniqueId,
PublishesViewMode,
apiCanAccessViewMode,
apiHasParentApi,
apiHasType,
apiHasUniqueId,
apiIsOfType,
getInheritedViewMode,
} from '@kbn/presentation-publishing';
import { CONTROL_GROUP_TYPE } from '../../common';
type DeleteControlActionApi = HasType &
HasUniqueId &
HasParentApi<PresentationContainer & PublishesViewMode & HasType>;
export const compatibilityCheck = (api: unknown | null): api is DeleteControlActionApi =>
Boolean(
apiHasType(api) &&
apiHasUniqueId(api) &&
apiHasParentApi(api) &&
apiCanAccessViewMode(api.parentApi) &&
apiIsOfType(api.parentApi, CONTROL_GROUP_TYPE) &&
apiIsPresentationContainer(api.parentApi)
);
export function isCompatible(api: unknown) {
return compatibilityCheck(api) && getInheritedViewMode(api.parentApi) === ViewMode.EDIT;
}

View file

@ -9,9 +9,6 @@
import { BehaviorSubject } from 'rxjs';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import dateMath from '@kbn/datemath';
import type { TimeRange } from '@kbn/es-query';
import type { ViewMode } from '@kbn/presentation-publishing';
@ -23,12 +20,10 @@ import {
getMockedControlGroupApi,
} from '../react_controls/controls/mocks/control_mocks';
import { getTimesliderControlFactory } from '../react_controls/controls/timeslider_control/get_timeslider_control_factory';
import { dataService } from '../services/kibana_services';
import { EditControlAction } from './edit_control_action';
const mockDataViews = dataViewPluginMocks.createStartContract();
const mockCore = coreMock.createStart();
const dataStartServiceMock = dataPluginMock.createStartContract();
dataStartServiceMock.query.timefilter.timefilter.calculateBounds = (timeRange: TimeRange) => {
dataService.query.timefilter.timefilter.calculateBounds = (timeRange: TimeRange) => {
const now = new Date();
return {
min: dateMath.parse(timeRange.from, { forceNow: now }),
@ -48,11 +43,7 @@ const controlGroupApi = getMockedControlGroupApi(dashboardApi, {
let optionsListApi: OptionsListControlApi;
beforeAll(async () => {
const controlFactory = getOptionsListControlFactory({
core: mockCore,
data: dataStartServiceMock,
dataViews: mockDataViews,
});
const controlFactory = getOptionsListControlFactory();
const optionsListUuid = 'optionsListControl';
const optionsListControl = await controlFactory.buildControl(
@ -73,10 +64,7 @@ beforeAll(async () => {
describe('Incompatible embeddables', () => {
test('Action is incompatible with embeddables that are not editable', async () => {
const timeSliderFactory = getTimesliderControlFactory({
core: mockCore,
data: dataStartServiceMock,
});
const timeSliderFactory = getTimesliderControlFactory();
const timeSliderUuid = 'timeSliderControl';
const timeSliderControl = await timeSliderFactory.buildControl(
{},

View file

@ -21,7 +21,6 @@ export { ACTION_CLEAR_CONTROL, ACTION_DELETE_CONTROL, ACTION_EDIT_CONTROL } from
export type {
DataControlApi,
DataControlFactory,
DataControlServices,
} from './react_controls/controls/data_controls/types';
export {

View file

@ -10,63 +10,36 @@
import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { PANEL_HOVER_TRIGGER } from '@kbn/embeddable-plugin/public';
import { ClearControlAction } from './actions/clear_control_action';
import { DeleteControlAction } from './actions/delete_control_action';
import { EditControlAction } from './actions/edit_control_action';
import { registerControlGroupEmbeddable } from './react_controls/control_group/register_control_group_embeddable';
import { registerOptionsListControl } from './react_controls/controls/data_controls/options_list_control/register_options_list_control';
import { registerRangeSliderControl } from './react_controls/controls/data_controls/range_slider/register_range_slider_control';
import { registerTimeSliderControl } from './react_controls/controls/timeslider_control/register_timeslider_control';
import { controlsService } from './services/controls/controls_service';
import type {
ControlsPluginSetup,
ControlsPluginSetupDeps,
ControlsPluginStart,
ControlsPluginStartDeps,
} from './types';
import { setKibanaServices, untilPluginStartServicesReady } from './services/kibana_services';
import type { ControlsPluginSetupDeps, ControlsPluginStartDeps } from './types';
export class ControlsPlugin
implements
Plugin<
ControlsPluginSetup,
ControlsPluginStart,
ControlsPluginSetupDeps,
ControlsPluginStartDeps
>
implements Plugin<void, void, ControlsPluginSetupDeps, ControlsPluginStartDeps>
{
private async startControlsKibanaServices(
coreStart: CoreStart,
startPlugins: ControlsPluginStartDeps
) {
const { registry, pluginServices } = await import('./services/plugin_services');
pluginServices.setRegistry(registry.start({ coreStart, startPlugins }));
}
public setup(
_coreSetup: CoreSetup<ControlsPluginStartDeps, ControlsPluginStart>,
_coreSetup: CoreSetup<ControlsPluginStartDeps>,
_setupPlugins: ControlsPluginSetupDeps
): ControlsPluginSetup {
const { registerControlFactory } = controlsService;
) {
const { embeddable } = _setupPlugins;
registerControlGroupEmbeddable(_coreSetup, embeddable);
registerOptionsListControl(_coreSetup);
registerRangeSliderControl(_coreSetup);
registerTimeSliderControl(_coreSetup);
return {
registerControlFactory,
};
registerControlGroupEmbeddable(embeddable);
registerOptionsListControl();
registerRangeSliderControl();
registerTimeSliderControl();
}
public start(coreStart: CoreStart, startPlugins: ControlsPluginStartDeps): ControlsPluginStart {
this.startControlsKibanaServices(coreStart, startPlugins).then(async () => {
public start(coreStart: CoreStart, startPlugins: ControlsPluginStartDeps) {
const { uiActions } = startPlugins;
setKibanaServices(coreStart, startPlugins);
const [{ DeleteControlAction }, { EditControlAction }, { ClearControlAction }] =
await Promise.all([
import('./actions/delete_control_action'),
import('./actions/edit_control_action'),
import('./actions/clear_control_action'),
]);
untilPluginStartServicesReady().then(() => {
const deleteControlAction = new DeleteControlAction();
uiActions.registerAction(deleteControlAction);
uiActions.attachAction(PANEL_HOVER_TRIGGER, deleteControlAction.id);
@ -79,12 +52,6 @@ export class ControlsPlugin
uiActions.registerAction(clearControlAction);
uiActions.attachAction(PANEL_HOVER_TRIGGER, clearControlAction.id);
});
const { getControlFactory, getAllControlTypes } = controlsService;
return {
getControlFactory,
getAllControlTypes,
};
}
public stop() {}

View file

@ -13,8 +13,6 @@ import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { Markdown } from '@kbn/shared-ux-markdown';
/** TODO: This file is duplicated from the controls plugin to avoid exporting it */
interface ControlErrorProps {
error: Error | string;
}

View file

@ -30,7 +30,7 @@ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiToolTip } from '
import { css } from '@emotion/react';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import type { ControlStyle } from '../../../../common';
import type { ControlLabelPosition } from '../../../../common';
import type { DefaultControlApi } from '../../controls/types';
import { ControlGroupStrings } from '../control_group_strings';
import { ControlsInOrder } from '../init_controls_manager';
@ -49,7 +49,7 @@ interface Props {
setControlApi: (uuid: string, controlApi: DefaultControlApi) => void;
};
hasUnappliedSelections: boolean;
labelPosition: ControlStyle;
labelPosition: ControlLabelPosition;
}
export function ControlGroup({

View file

@ -15,8 +15,8 @@ import { render } from '@testing-library/react';
import { ControlGroupApi } from '../../..';
import {
ControlGroupChainingSystem,
ControlStyle,
DEFAULT_CONTROL_STYLE,
ControlLabelPosition,
DEFAULT_CONTROL_LABEL_POSITION,
ParentIgnoreSettings,
} from '../../../../common';
import { DefaultControlApi } from '../../controls/types';
@ -33,7 +33,7 @@ describe('render', () => {
onDeleteAll: () => {},
stateManager: {
chainingSystem: new BehaviorSubject<ControlGroupChainingSystem>('HIERARCHICAL'),
labelPosition: new BehaviorSubject<ControlStyle>(DEFAULT_CONTROL_STYLE),
labelPosition: new BehaviorSubject<ControlLabelPosition>(DEFAULT_CONTROL_LABEL_POSITION),
autoApplySelections: new BehaviorSubject<boolean>(true),
ignoreParentSettings: new BehaviorSubject<ParentIgnoreSettings | undefined>(undefined),
},

View file

@ -27,7 +27,7 @@ import {
} from '@elastic/eui';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import type { ControlStyle, ParentIgnoreSettings } from '../../../../common';
import type { ControlLabelPosition, ParentIgnoreSettings } from '../../../../common';
import { CONTROL_LAYOUT_OPTIONS } from '../../controls/data_controls/editor_constants';
import type { ControlStateManager } from '../../controls/types';
import { ControlGroupStrings } from '../control_group_strings';
@ -86,7 +86,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager
idSelected={selectedLabelPosition}
legend={ControlGroupStrings.management.labelPosition.getLabelPositionLegend()}
onChange={(newPosition: string) => {
stateManager.labelPosition.next(newPosition as ControlStyle);
stateManager.labelPosition.next(newPosition as ControlLabelPosition);
}}
/>
</EuiFormRow>

View file

@ -14,7 +14,7 @@ import { pluginServices as presentationUtilPluginServices } from '@kbn/presentat
import { registry as presentationUtilServicesRegistry } from '@kbn/presentation-util-plugin/public/services/plugin_services.story';
import { render, waitFor } from '@testing-library/react';
import type { ControlStyle, ControlWidth } from '../../../../common';
import type { ControlLabelPosition, ControlWidth } from '../../../../common';
import { ControlPanel } from './control_panel';
describe('render', () => {
@ -74,7 +74,7 @@ describe('render', () => {
mockApi = {
uuid: 'control1',
parentApi: {
labelPosition: new BehaviorSubject<ControlStyle>('oneLine'),
labelPosition: new BehaviorSubject<ControlLabelPosition>('oneLine'),
},
};
const controlPanel = render(<ControlPanel uuid="control1" Component={Component} />);
@ -92,7 +92,7 @@ describe('render', () => {
mockApi = {
uuid: 'control1',
parentApi: {
labelPosition: new BehaviorSubject<ControlStyle>('twoLine'),
labelPosition: new BehaviorSubject<ControlLabelPosition>('twoLine'),
},
};
const controlPanel = render(<ControlPanel uuid="control1" Component={Component} />);

View file

@ -11,9 +11,7 @@ import fastIsEqual from 'fast-deep-equal';
import React, { useEffect } from 'react';
import { BehaviorSubject } from 'rxjs';
import { CoreStart } from '@kbn/core/public';
import { DataView } from '@kbn/data-views-plugin/common';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { i18n } from '@kbn/i18n';
import {
@ -31,11 +29,11 @@ import type {
ControlGroupChainingSystem,
ControlGroupRuntimeState,
ControlGroupSerializedState,
ControlLabelPosition,
ControlPanelsState,
ControlStyle,
ParentIgnoreSettings,
} from '../../../common';
import { CONTROL_GROUP_TYPE, DEFAULT_CONTROL_STYLE } from '../../../common';
import { CONTROL_GROUP_TYPE, DEFAULT_CONTROL_LABEL_POSITION } from '../../../common';
import { openDataControlEditor } from '../controls/data_controls/open_data_control_editor';
import { ControlGroup } from './components/control_group';
import { chaining$, controlFetch$, controlGroupFetch$ } from './control_fetch';
@ -45,13 +43,11 @@ import { openEditControlGroupFlyout } from './open_edit_control_group_flyout';
import { initSelectionsManager } from './selections_manager';
import type { ControlGroupApi } from './types';
import { deserializeControlGroup } from './utils/serialization_utils';
import { coreServices, dataViewsService } from '../../services/kibana_services';
const DEFAULT_CHAINING_SYSTEM = 'HIERARCHICAL';
export const getControlGroupEmbeddableFactory = (services: {
core: CoreStart;
dataViews: DataViewsPublicPluginStart;
}) => {
export const getControlGroupEmbeddableFactory = () => {
const controlGroupEmbeddableFactory: ReactEmbeddableFactory<
ControlGroupSerializedState,
ControlGroupRuntimeState,
@ -75,7 +71,7 @@ export const getControlGroupEmbeddableFactory = (services: {
} = initialRuntimeState;
const autoApplySelections$ = new BehaviorSubject<boolean>(autoApplySelections);
const defaultDataViewId = await services.dataViews.getDefaultId();
const defaultDataViewId = await dataViewsService.getDefaultId();
const lastSavedControlsState$ = new BehaviorSubject<ControlPanelsState>(
lastSavedRuntimeState.initialChildControlState
);
@ -94,15 +90,12 @@ export const getControlGroupEmbeddableFactory = (services: {
const ignoreParentSettings$ = new BehaviorSubject<ParentIgnoreSettings | undefined>(
ignoreParentSettings
);
const labelPosition$ = new BehaviorSubject<ControlStyle>( // TODO: Rename `ControlStyle`
initialLabelPosition ?? DEFAULT_CONTROL_STYLE // TODO: Rename `DEFAULT_CONTROL_STYLE`
const labelPosition$ = new BehaviorSubject<ControlLabelPosition>(
initialLabelPosition ?? DEFAULT_CONTROL_LABEL_POSITION
);
const allowExpensiveQueries$ = new BehaviorSubject<boolean>(true);
const disabledActionIds$ = new BehaviorSubject<string[] | undefined>(undefined);
/** TODO: Handle loading; loading should be true if any child is loading */
const dataLoading$ = new BehaviorSubject<boolean | undefined>(false);
const unsavedChanges = initializeControlGroupUnsavedChanges(
selectionsManager.applySelections,
controlsManager.api.children$,
@ -122,7 +115,10 @@ export const getControlGroupEmbeddableFactory = (services: {
(next: ParentIgnoreSettings | undefined) => ignoreParentSettings$.next(next),
fastIsEqual,
],
labelPosition: [labelPosition$, (next: ControlStyle) => labelPosition$.next(next)],
labelPosition: [
labelPosition$,
(next: ControlLabelPosition) => labelPosition$.next(next),
],
},
controlsManager.snapshotControlsRuntimeState,
controlsManager.resetControlsUnsavedChanges,
@ -157,18 +153,13 @@ export const getControlGroupEmbeddableFactory = (services: {
initialChildControlState: controlsManager.snapshotControlsRuntimeState(),
};
},
dataLoading: dataLoading$,
onEdit: async () => {
openEditControlGroupFlyout(
api,
{
openEditControlGroupFlyout(api, {
chainingSystem: chainingSystem$,
labelPosition: labelPosition$,
autoApplySelections: autoApplySelections$,
ignoreParentSettings: ignoreParentSettings$,
},
{ core: services.core }
);
});
},
isEditingEnabled: () => true,
openAddDataControlFlyout: (settings) => {
@ -193,7 +184,6 @@ export const getControlGroupEmbeddableFactory = (services: {
settings?.onSave?.();
},
controlGroupApi: api,
services,
});
},
serializeState: () => {
@ -201,7 +191,7 @@ export const getControlGroupEmbeddableFactory = (services: {
return {
rawState: {
chainingSystem: chainingSystem$.getValue(),
controlStyle: labelPosition$.getValue(), // Rename "labelPosition" to "controlStyle"
controlStyle: labelPosition$.getValue(),
showApplySelections: !autoApplySelections$.getValue(),
ignoreParentSettingsJSON: JSON.stringify(ignoreParentSettings$.getValue()),
panelsJSON,
@ -265,10 +255,9 @@ export const getControlGroupEmbeddableFactory = (services: {
/** Fetch the allowExpensiveQuries setting for the children to use if necessary */
const fetchAllowExpensiveQueries = async () => {
try {
const { allowExpensiveQueries } = await services.core.http.get<{
const { allowExpensiveQueries } = await coreServices.http.get<{
allowExpensiveQueries: boolean;
// TODO: Rename this route as part of https://github.com/elastic/kibana/issues/174961
}>('/internal/controls/optionsList/getExpensiveQueriesSetting', {
}>('/internal/controls/getExpensiveQueriesSetting', {
version: '1',
});
if (!allowExpensiveQueries) {

View file

@ -8,7 +8,6 @@
*/
import { OverlayRef } from '@kbn/core-mount-utils-browser';
import { CoreStart } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { tracksOverlays } from '@kbn/presentation-containers';
import { apiHasParentApi } from '@kbn/presentation-publishing';
@ -19,13 +18,11 @@ import { BehaviorSubject } from 'rxjs';
import { ControlStateManager } from '../controls/types';
import { ControlGroupEditor } from './components/control_group_editor';
import { ControlGroupApi, ControlGroupEditorState } from './types';
import { coreServices } from '../../services/kibana_services';
export const openEditControlGroupFlyout = (
controlGroupApi: ControlGroupApi,
stateManager: ControlStateManager<ControlGroupEditorState>,
services: {
core: CoreStart;
}
stateManager: ControlStateManager<ControlGroupEditorState>
) => {
/**
* Duplicate all state into a new manager because we do not want to actually apply the changes
@ -50,7 +47,7 @@ export const openEditControlGroupFlyout = (
};
const onDeleteAll = (ref: OverlayRef) => {
services.core.overlays
coreServices.overlays
.openConfirm(
i18n.translate('controls.controlGroup.management.delete.sub', {
defaultMessage: 'Controls are not recoverable once removed.',
@ -77,7 +74,7 @@ export const openEditControlGroupFlyout = (
});
};
const overlay = services.core.overlays.openFlyout(
const overlay = coreServices.overlays.openFlyout(
toMountPoint(
<ControlGroupEditor
api={controlGroupApi}
@ -96,8 +93,8 @@ export const openEditControlGroupFlyout = (
onCancel={() => closeOverlay(overlay)}
/>,
{
theme: services.core.theme,
i18n: services.core.i18n,
theme: coreServices.theme,
i18n: coreServices.i18n,
}
),
{

View file

@ -7,23 +7,16 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { CoreSetup } from '@kbn/core/public';
import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
import type { ControlsPluginStartDeps } from '../../types';
import { CONTROL_GROUP_TYPE } from '../../../common';
import { untilPluginStartServicesReady } from '../../services/kibana_services';
export function registerControlGroupEmbeddable(
coreSetup: CoreSetup<ControlsPluginStartDeps>,
embeddableSetup: EmbeddableSetup
) {
export function registerControlGroupEmbeddable(embeddableSetup: EmbeddableSetup) {
embeddableSetup.registerReactEmbeddableFactory(CONTROL_GROUP_TYPE, async () => {
const [{ getControlGroupEmbeddableFactory }, [coreStart, depsStart]] = await Promise.all([
const [{ getControlGroupEmbeddableFactory }] = await Promise.all([
import('./get_control_group_factory'),
coreSetup.getStartServices(),
untilPluginStartServicesReady(),
]);
return getControlGroupEmbeddableFactory({
core: coreStart,
dataViews: depsStart.data.dataViews,
});
return getControlGroupEmbeddableFactory();
});
}

View file

@ -19,7 +19,6 @@ import {
import {
HasEditCapabilities,
HasParentApi,
PublishesDataLoading,
PublishesDisabledActionIds,
PublishesFilters,
PublishesTimeslice,
@ -35,8 +34,8 @@ import {
ControlGroupEditorConfig,
ControlGroupRuntimeState,
ControlGroupSerializedState,
ControlLabelPosition,
ControlPanelState,
ControlStyle,
DefaultControlState,
ParentIgnoreSettings,
} from '../../../common';
@ -54,7 +53,6 @@ export type ControlGroupApi = PresentationContainer &
PublishesDataViews &
HasSerializedChildState<ControlPanelState> &
HasEditCapabilities &
PublishesDataLoading &
Pick<PublishesUnsavedChanges<ControlGroupRuntimeState>, 'unsavedChanges'> &
PublishesTimeslice &
PublishesDisabledActionIds &
@ -62,7 +60,7 @@ export type ControlGroupApi = PresentationContainer &
allowExpensiveQueries$: PublishingSubject<boolean>;
autoApplySelections$: PublishingSubject<boolean>;
ignoreParentSettings$: PublishingSubject<ParentIgnoreSettings | undefined>;
labelPosition: PublishingSubject<ControlStyle>;
labelPosition: PublishingSubject<ControlLabelPosition>;
asyncResetUnsavedChanges: () => Promise<void>;
controlFetch$: (controlUuid: string) => Observable<ControlFetchContext>;

View file

@ -13,11 +13,12 @@ import {
OPTIONS_LIST_CONTROL,
RANGE_SLIDER_CONTROL,
TIME_SLIDER_CONTROL,
type ControlGroupRuntimeState,
type ControlPanelsState,
type DefaultDataControlState,
} from '../../../../common';
import { type ControlGroupRuntimeState, type ControlPanelsState } from '../../../../common';
import type { OptionsListControlState } from '../../../../common/options_list';
import { pluginServices } from '../../../services';
import { dataViewsService } from '../../../services/kibana_services';
import { getDataControlFieldRegistry } from '../../controls/data_controls/data_control_editor_utils';
import type { RangesliderControlState } from '../../controls/data_controls/range_slider/types';
@ -82,7 +83,7 @@ export const controlGroupStateBuilder = {
};
async function getCompatibleControlType(dataViewId: string, fieldName: string) {
const dataView = await pluginServices.getServices().dataViews.get(dataViewId);
const dataView = await dataViewsService.get(dataViewId);
const fieldRegistry = await getDataControlFieldRegistry(dataView);
const field = fieldRegistry[fieldName];
if (field.compatibleControlTypes.length === 0) {

View file

@ -7,11 +7,11 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { type ControlGroupRuntimeState, DEFAULT_CONTROL_STYLE } from '../../../../common';
import { DEFAULT_CONTROL_LABEL_POSITION, type ControlGroupRuntimeState } from '../../../../common';
export const getDefaultControlGroupRuntimeState = (): ControlGroupRuntimeState => ({
initialChildControlState: {},
labelPosition: DEFAULT_CONTROL_STYLE,
labelPosition: DEFAULT_CONTROL_LABEL_POSITION,
chainingSystem: 'HIERARCHICAL',
autoApplySelections: true,
ignoreParentSettings: {

View file

@ -12,7 +12,6 @@ import { BehaviorSubject } from 'rxjs';
import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { stubFieldSpecMap } from '@kbn/data-views-plugin/common/field.stub';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { TimeRange } from '@kbn/es-query';
import { I18nProvider } from '@kbn/i18n-react';
import { act, fireEvent, render, RenderResult, waitFor } from '@testing-library/react';
@ -22,6 +21,7 @@ import {
DEFAULT_CONTROL_WIDTH,
type DefaultDataControlState,
} from '../../../../common';
import { dataViewsService } from '../../../services/kibana_services';
import { getAllControlTypes, getControlFactory } from '../../control_factory_registry';
import type { ControlGroupApi } from '../../control_group/types';
import type { ControlFactory } from '../types';
@ -39,7 +39,6 @@ jest.mock('../../control_factory_registry', () => ({
getControlFactory: jest.fn(),
}));
const mockDataViews = dataViewPluginMocks.createStartContract();
const mockDataView = createStubDataView({
spec: {
id: 'logstash-*',
@ -58,7 +57,6 @@ const mockDataView = createStubDataView({
timeFieldName: '@timestamp',
},
});
mockDataViews.get = jest.fn().mockResolvedValue(mockDataView);
const dashboardApi = {
timeRange$: new BehaviorSubject<TimeRange | undefined>(undefined),
@ -82,7 +80,7 @@ describe('Data control editor', () => {
controlType?: string;
initialDefaultPanelTitle?: string;
}) => {
mockDataViews.get = jest.fn().mockResolvedValue(mockDataView);
dataViewsService.get = jest.fn().mockResolvedValue(mockDataView);
const controlEditor = render(
<I18nProvider>
@ -97,13 +95,12 @@ describe('Data control editor', () => {
controlId={controlId}
controlType={controlType}
initialDefaultPanelTitle={initialDefaultPanelTitle}
services={{ dataViews: mockDataViews }}
/>
</I18nProvider>
);
await waitFor(() => {
expect(mockDataViews.get).toHaveBeenCalledTimes(1);
expect(dataViewsService.get).toHaveBeenCalledTimes(1);
});
return controlEditor;

View file

@ -33,7 +33,6 @@ import {
EuiToolTip,
} from '@elastic/eui';
import { DataViewField } from '@kbn/data-views-plugin/common';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import {
LazyDataViewPicker,
LazyFieldPicker,
@ -46,6 +45,7 @@ import {
type ControlWidth,
type DefaultDataControlState,
} from '../../../../common';
import { dataViewsService } from '../../../services/kibana_services';
import { getAllControlTypes, getControlFactory } from '../../control_factory_registry';
import type { ControlGroupApi } from '../../control_group/types';
import { DataControlEditorStrings } from './data_control_constants';
@ -67,9 +67,6 @@ export interface ControlEditorProps<
controlGroupApi: ControlGroupApi; // controls must always have a parent API
onCancel: (newState: Partial<State>) => void;
onSave: (newState: Partial<State>, type: string) => void;
services: {
dataViews: DataViewsPublicPluginStart;
};
}
const FieldPicker = withSuspense(LazyFieldPicker, null);
@ -151,8 +148,6 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
onSave,
onCancel,
controlGroupApi,
/** TODO: These should not be props */
services: { dataViews: dataViewService },
}: ControlEditorProps<State>) => {
const [editorState, setEditorState] = useState<Partial<State>>(initialState);
const [defaultPanelTitle, setDefaultPanelTitle] = useState<string>(
@ -163,16 +158,14 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
const [controlOptionsValid, setControlOptionsValid] = useState<boolean>(true);
const editorConfig = useMemo(() => controlGroupApi.getEditorConfig(), [controlGroupApi]);
// TODO: Maybe remove `useAsync` - see https://github.com/elastic/kibana/pull/182842#discussion_r1624909709
const {
loading: dataViewListLoading,
value: dataViewListItems = [],
error: dataViewListError,
} = useAsync(async () => {
return dataViewService.getIdsWithTitle();
return dataViewsService.getIdsWithTitle();
});
// TODO: Maybe remove `useAsync` - see https://github.com/elastic/kibana/pull/182842#discussion_r1624909709
const {
loading: dataViewLoading,
value: { selectedDataView, fieldRegistry } = {
@ -185,7 +178,7 @@ export const DataControlEditor = <State extends DefaultDataControlState = Defaul
return;
}
const dataView = await dataViewService.get(editorState.dataViewId);
const dataView = await dataViewsService.get(editorState.dataViewId);
const registry = await getDataControlFieldRegistry(dataView);
return {
selectedDataView: dataView,

View file

@ -13,7 +13,6 @@ import type { DataView } from '@kbn/data-views-plugin/common';
import { getAllControlTypes, getControlFactory } from '../../control_factory_registry';
import { isDataControlFactory, type DataControlFieldRegistry } from './types';
/** TODO: This funciton is duplicated from the controls plugin to avoid exporting it */
export const getDataControlFieldRegistry = memoize(
async (dataView: DataView) => {
return await loadFieldRegistryFromDataView(dataView);
@ -21,7 +20,6 @@ export const getDataControlFieldRegistry = memoize(
(dataView: DataView) => [dataView.id, JSON.stringify(dataView.fields.getAll())].join('|')
);
/** TODO: This function is duplicated from the controls plugin to avoid exporting it */
const loadFieldRegistryFromDataView = async (
dataView: DataView
): Promise<DataControlFieldRegistry> => {

View file

@ -7,10 +7,9 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { coreMock } from '@kbn/core/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import type { DataView } from '@kbn/data-views-plugin/public';
import { first, skip } from 'rxjs';
import { dataViewsService } from '../../../services/kibana_services';
import { ControlGroupApi } from '../../control_group/types';
import { initializeDataControl } from './initialize_data_control';
@ -21,9 +20,8 @@ describe('initializeDataControl', () => {
};
const editorStateManager = {};
const controlGroupApi = {} as unknown as ControlGroupApi;
const mockDataViews = dataViewPluginMocks.createStartContract();
// @ts-ignore
mockDataViews.get = async (id: string): Promise<DataView> => {
dataViewsService.get = async (id: string): Promise<DataView> => {
if (id !== 'myDataViewId') {
throw new Error(`Simulated error: no data view found for id ${id}`);
}
@ -40,10 +38,6 @@ describe('initializeDataControl', () => {
},
} as unknown as DataView;
};
const services = {
core: coreMock.createStart(),
dataViews: mockDataViews,
};
describe('dataViewId subscription', () => {
describe('no blocking errors', () => {
@ -55,8 +49,7 @@ describe('initializeDataControl', () => {
'referenceNameSuffix',
dataControlState,
editorStateManager,
controlGroupApi,
services
controlGroupApi
);
dataControl.api.defaultPanelTitle!.pipe(skip(1), first()).subscribe(() => {
@ -90,8 +83,7 @@ describe('initializeDataControl', () => {
dataViewId: 'notGonnaFindMeDataViewId',
},
editorStateManager,
controlGroupApi,
services
controlGroupApi
);
dataControl.api.dataViews.pipe(skip(1), first()).subscribe(() => {
@ -129,8 +121,7 @@ describe('initializeDataControl', () => {
fieldName: 'notGonnaFindMeFieldName',
},
editorStateManager,
controlGroupApi,
services
controlGroupApi
);
dataControl.api.defaultPanelTitle!.pipe(skip(1), first()).subscribe(() => {

View file

@ -10,19 +10,18 @@
import { isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, debounceTime, first, skip, switchMap, tap } from 'rxjs';
import { CoreStart } from '@kbn/core-lifecycle-browser';
import {
DATA_VIEW_SAVED_OBJECT_TYPE,
DataView,
DataViewField,
} from '@kbn/data-views-plugin/common';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { Filter } from '@kbn/es-query';
import { SerializedPanelState } from '@kbn/presentation-containers';
import { StateComparators } from '@kbn/presentation-publishing';
import { i18n } from '@kbn/i18n';
import type { DefaultControlState, DefaultDataControlState } from '../../../../common';
import { dataViewsService } from '../../../services/kibana_services';
import type { ControlGroupApi } from '../../control_group/types';
import { initializeDefaultControlApi } from '../initialize_default_control_api';
import type { ControlApiInitialization, ControlStateManager } from '../types';
@ -40,11 +39,7 @@ export const initializeDataControl = <EditorState extends object = {}>(
* responsible for managing
*/
editorStateManager: ControlStateManager<EditorState>,
controlGroupApi: ControlGroupApi,
services: {
core: CoreStart;
dataViews: DataViewsPublicPluginStart;
}
controlGroupApi: ControlGroupApi
): {
api: ControlApiInitialization<DataControlApi>;
cleanup: () => void;
@ -88,7 +83,7 @@ export const initializeDataControl = <EditorState extends object = {}>(
switchMap(async (currentDataViewId) => {
let dataView: DataView | undefined;
try {
dataView = await services.dataViews.get(currentDataViewId);
dataView = await dataViewsService.get(currentDataViewId);
return { dataView };
} catch (error) {
return { error };
@ -156,7 +151,6 @@ export const initializeDataControl = <EditorState extends object = {}>(
// open the editor to get the new state
openDataControlEditor<DefaultDataControlState & EditorState>({
services,
onSave: ({ type: newType, state: newState }) => {
if (newType === controlType) {
// apply the changes from the new state via the state manager

View file

@ -10,14 +10,14 @@
import React from 'react';
import deepEqual from 'react-fast-compare';
import { CoreStart, OverlayRef } from '@kbn/core/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { OverlayRef } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { tracksOverlays } from '@kbn/presentation-containers';
import { apiHasParentApi } from '@kbn/presentation-publishing';
import { toMountPoint } from '@kbn/react-kibana-mount';
import type { DefaultDataControlState } from '../../../../common';
import { coreServices } from '../../../services/kibana_services';
import type { ControlGroupApi } from '../../control_group/types';
import { DataControlEditor } from './data_control_editor';
@ -30,7 +30,6 @@ export const openDataControlEditor = <
initialDefaultPanelTitle,
onSave,
controlGroupApi,
services,
}: {
initialState: Partial<State>;
controlType?: string;
@ -38,10 +37,6 @@ export const openDataControlEditor = <
initialDefaultPanelTitle?: string;
onSave: ({ type, state }: { type: string; state: Partial<State> }) => void;
controlGroupApi: ControlGroupApi;
services: {
core: CoreStart;
dataViews: DataViewsPublicPluginStart;
};
}): void => {
const closeOverlay = (overlayRef: OverlayRef) => {
if (apiHasParentApi(controlGroupApi) && tracksOverlays(controlGroupApi.parentApi)) {
@ -55,7 +50,7 @@ export const openDataControlEditor = <
closeOverlay(overlay);
return;
}
services.core.overlays
coreServices.overlays
.openConfirm(
i18n.translate('controls.controlGroup.management.discard.sub', {
defaultMessage: `Changes that you've made to this control will be discarded, are you sure you want to continue?`,
@ -80,7 +75,7 @@ export const openDataControlEditor = <
});
};
const overlay = services.core.overlays.openFlyout(
const overlay = coreServices.overlays.openFlyout(
toMountPoint(
<DataControlEditor<State>
controlGroupApi={controlGroupApi}
@ -95,11 +90,10 @@ export const openDataControlEditor = <
closeOverlay(overlay);
onSave({ type: selectedControlType, state });
}}
services={{ dataViews: services.dataViews }}
/>,
{
theme: services.core.theme,
i18n: services.core.i18n,
theme: coreServices.theme,
i18n: coreServices.i18n,
}
),
{

View file

@ -26,13 +26,11 @@ import { isValidSearch } from '../../../../../common/options_list/is_valid_searc
import { OptionsListSelection } from '../../../../../common/options_list/options_list_selections';
import { ControlFetchContext } from '../../../control_group/control_fetch';
import { ControlStateManager } from '../../types';
import { DataControlServices } from '../types';
import { OptionsListFetchCache } from './options_list_fetch_cache';
import { OptionsListComponentApi, OptionsListComponentState, OptionsListControlApi } from './types';
export function fetchAndValidate$({
api,
services,
stateManager,
}: {
api: Pick<OptionsListControlApi, 'dataViews' | 'field$' | 'setBlockingError' | 'parentApi'> &
@ -41,7 +39,6 @@ export function fetchAndValidate$({
loadingSuggestions$: BehaviorSubject<boolean>;
debouncedSearchString: Observable<string>;
};
services: DataControlServices;
stateManager: ControlStateManager<
Pick<OptionsListComponentState, 'requestSize' | 'runPastTimeout' | 'searchTechnique' | 'sort'>
> & {
@ -126,7 +123,7 @@ export function fetchAndValidate$({
const newAbortController = new AbortController();
abortController = newAbortController;
try {
return await requestCache.runFetchRequest(request, newAbortController.signal, services);
return await requestCache.runFetchRequest(request, newAbortController.signal);
} catch (error) {
return { error };
}

View file

@ -9,26 +9,22 @@
import React from 'react';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { DataView } from '@kbn/data-views-plugin/common';
import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { act, render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { coreServices, dataViewsService } from '../../../../services/kibana_services';
import { getMockedBuildApi, getMockedControlGroupApi } from '../../mocks/control_mocks';
import { getOptionsListControlFactory } from './get_options_list_control_factory';
describe('Options List Control Api', () => {
const uuid = 'myControl1';
const controlGroupApi = getMockedControlGroupApi();
const mockDataViews = dataViewPluginMocks.createStartContract();
const mockCore = coreMock.createStart();
const waitOneTick = () => act(() => new Promise((resolve) => setTimeout(resolve, 0)));
mockDataViews.get = jest.fn().mockImplementation(async (id: string): Promise<DataView> => {
dataViewsService.get = jest.fn().mockImplementation(async (id: string): Promise<DataView> => {
if (id !== 'myDataViewId') {
throw new Error(`Simulated error: no data view found for id ${id}`);
}
@ -60,11 +56,7 @@ describe('Options List Control Api', () => {
return stubDataView;
});
const factory = getOptionsListControlFactory({
core: mockCore,
data: dataPluginMock.createStartContract(),
dataViews: mockDataViews,
});
const factory = getOptionsListControlFactory();
describe('filters$', () => {
test('should not set filters$ when selectedOptions is not provided', async () => {
@ -177,7 +169,7 @@ describe('Options List Control Api', () => {
describe('make selection', () => {
beforeAll(() => {
mockCore.http.fetch = jest.fn().mockResolvedValue({
coreServices.http.fetch = jest.fn().mockResolvedValue({
suggestions: [
{ value: 'woof', docCount: 10 },
{ value: 'bark', docCount: 15 },

View file

@ -9,7 +9,7 @@
import fastIsEqual from 'fast-deep-equal';
import React, { useEffect } from 'react';
import { BehaviorSubject, combineLatest, debounceTime, filter, skip } from 'rxjs';
import { BehaviorSubject, combineLatest, debounceTime, filter, map, skip } from 'rxjs';
import { buildExistsFilter, buildPhraseFilter, buildPhrasesFilter, Filter } from '@kbn/es-query';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
@ -25,7 +25,7 @@ import type {
} from '../../../../../common/options_list';
import { getSelectionAsFieldType, isValidSearch } from '../../../../../common/options_list';
import { initializeDataControl } from '../initialize_data_control';
import type { DataControlFactory, DataControlServices } from '../types';
import type { DataControlFactory } from '../types';
import { OptionsListControl } from './components/options_list_control';
import { OptionsListEditorOptions } from './components/options_list_editor_options';
import {
@ -39,9 +39,10 @@ import { initializeOptionsListSelections } from './options_list_control_selectio
import { OptionsListStrings } from './options_list_strings';
import type { OptionsListControlApi } from './types';
export const getOptionsListControlFactory = (
services: DataControlServices
): DataControlFactory<OptionsListControlState, OptionsListControlApi> => {
export const getOptionsListControlFactory = (): DataControlFactory<
OptionsListControlState,
OptionsListControlApi
> => {
return {
type: OPTIONS_LIST_CONTROL,
order: 3, // should always be first, since this is the most popular control
@ -78,6 +79,7 @@ export const getOptionsListControlFactory = (
const searchStringValid$ = new BehaviorSubject<boolean>(true);
const requestSize$ = new BehaviorSubject<number>(MIN_OPTIONS_LIST_REQUEST_SIZE);
const dataLoading$ = new BehaviorSubject<boolean | undefined>(undefined);
const availableOptions$ = new BehaviorSubject<OptionsListSuggestions | undefined>(undefined);
const invalidSelections$ = new BehaviorSubject<Set<OptionsListSelection>>(new Set());
const totalCardinality$ = new BehaviorSubject<number>(0);
@ -90,8 +92,7 @@ export const getOptionsListControlFactory = (
'optionsListDataView',
initialState,
{ searchTechnique: searchTechnique$, singleSelect: singleSelect$ },
controlGroupApi,
services
controlGroupApi
);
const selections = initializeOptionsListSelections(
@ -115,12 +116,16 @@ export const getOptionsListControlFactory = (
/** Handle loading state; since suggestion fetching and validation are tied, only need one loading subject */
const loadingSuggestions$ = new BehaviorSubject<boolean>(false);
const dataLoadingSubscription = loadingSuggestions$
const dataLoadingSubscription = combineLatest([
loadingSuggestions$,
dataControl.api.dataLoading,
])
.pipe(
debounceTime(100) // debounce set loading so that it doesn't flash as the user types
debounceTime(100), // debounce set loading so that it doesn't flash as the user types
map((values) => values.some((value) => value))
)
.subscribe((isLoading) => {
dataControl.api.setDataLoading(isLoading);
dataLoading$.next(isLoading);
});
/** Debounce the search string changes to reduce the number of fetch requests */
@ -161,7 +166,6 @@ export const getOptionsListControlFactory = (
/** Fetch the suggestions and perform validation */
const loadMoreSubject = new BehaviorSubject<null>(null);
const fetchSubscription = fetchAndValidate$({
services,
api: {
...dataControl.api,
loadMoreSubject,
@ -235,6 +239,7 @@ export const getOptionsListControlFactory = (
const api = buildApi(
{
...dataControl.api,
dataLoading: dataLoading$,
getTypeDisplayName: OptionsListStrings.control.getDisplayName,
serializeState: () => {
const { rawState: dataControlState, references } = dataControl.serialize();

View file

@ -20,7 +20,7 @@ import type {
OptionsListResponse,
OptionsListSuccessResponse,
} from '../../../../../common/options_list/types';
import type { DataControlServices } from '../types';
import { coreServices, dataService } from '../../../../services/kibana_services';
const REQUEST_CACHE_SIZE = 50; // only store a max of 50 responses
const REQUEST_CACHE_TTL = 1000 * 60; // time to live = 1 minute
@ -80,8 +80,7 @@ export class OptionsListFetchCache {
public async runFetchRequest(
request: OptionsListRequest,
abortSignal: AbortSignal,
services: DataControlServices
abortSignal: AbortSignal
): Promise<OptionsListResponse> {
const requestHash = this.getRequestHash(request);
@ -90,11 +89,11 @@ export class OptionsListFetchCache {
} else {
const index = request.dataView.getIndexPattern();
const timeService = services.data.query.timefilter.timefilter;
const timeService = dataService.query.timefilter.timefilter;
const { query, filters, dataView, timeRange, field, ...passThroughProps } = request;
const timeFilter = timeRange ? timeService.createFilter(dataView, timeRange) : undefined;
const filtersToUse = [...(filters ?? []), ...(timeFilter ? [timeFilter] : [])];
const config = getEsQueryConfig(services.core.uiSettings);
const config = getEsQueryConfig(coreServices.uiSettings);
const esFilters = [buildEsQuery(dataView, query ?? [], filtersToUse ?? [], config)];
const requestBody = {
@ -105,7 +104,7 @@ export class OptionsListFetchCache {
runtimeFieldMap: dataView.toSpec?.().runtimeFieldMap,
};
const result = await services.core.http.fetch<OptionsListResponse>(
const result = await coreServices.http.fetch<OptionsListResponse>(
`/internal/controls/optionsList/${index}`,
{
version: '1',

View file

@ -7,21 +7,16 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { CoreSetup } from '@kbn/core/public';
import type { ControlsPluginStartDeps } from '../../../../types';
import { registerControlFactory } from '../../../control_factory_registry';
import { OPTIONS_LIST_CONTROL } from '../../../../../common';
import { untilPluginStartServicesReady } from '../../../../services/kibana_services';
import { registerControlFactory } from '../../../control_factory_registry';
export function registerOptionsListControl(coreSetup: CoreSetup<ControlsPluginStartDeps>) {
export function registerOptionsListControl() {
registerControlFactory(OPTIONS_LIST_CONTROL, async () => {
const [{ getOptionsListControlFactory }, [coreStart, depsStart]] = await Promise.all([
const [{ getOptionsListControlFactory }] = await Promise.all([
import('./get_options_list_control_factory'),
coreSetup.getStartServices(),
untilPluginStartServicesReady(),
]);
return getOptionsListControlFactory({
core: coreStart,
data: depsStart.data,
dataViews: depsStart.data.dataViews,
});
return getOptionsListControlFactory();
});
}

View file

@ -11,13 +11,11 @@ import React from 'react';
import { of } from 'rxjs';
import { estypes } from '@elastic/elasticsearch';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { DataViewField } from '@kbn/data-views-plugin/common';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { SerializedPanelState } from '@kbn/presentation-containers';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { dataService, dataViewsService } from '../../../../services/kibana_services';
import { getMockedBuildApi, getMockedControlGroupApi } from '../../mocks/control_mocks';
import { getRangesliderControlFactory } from './get_range_slider_control_factory';
import { RangesliderControlState } from './types';
@ -31,11 +29,10 @@ describe('RangesliderControlApi', () => {
const controlGroupApi = getMockedControlGroupApi();
const dataStartServiceMock = dataPluginMock.createStartContract();
let totalResults = DEFAULT_TOTAL_RESULTS;
let min: estypes.AggregationsSingleMetricAggregateBase['value'] = DEFAULT_MIN;
let max: estypes.AggregationsSingleMetricAggregateBase['value'] = DEFAULT_MAX;
dataStartServiceMock.search.searchSource.create = jest.fn().mockImplementation(() => {
dataService.search.searchSource.create = jest.fn().mockImplementation(() => {
let isAggsRequest = false;
return {
setField: (key: string) => {
@ -54,9 +51,8 @@ describe('RangesliderControlApi', () => {
},
};
});
const mockDataViews = dataViewPluginMocks.createStartContract();
mockDataViews.get = jest.fn().mockImplementation(async (id: string): Promise<DataView> => {
dataViewsService.get = jest.fn().mockImplementation(async (id: string): Promise<DataView> => {
if (id !== 'myDataViewId') {
throw new Error(`no data view found for id ${id}`);
}
@ -82,11 +78,7 @@ describe('RangesliderControlApi', () => {
} as unknown as DataView;
});
const factory = getRangesliderControlFactory({
core: coreMock.createStart(),
data: dataStartServiceMock,
dataViews: mockDataViews,
});
const factory = getRangesliderControlFactory();
beforeEach(() => {
totalResults = DEFAULT_TOTAL_RESULTS;

View file

@ -16,7 +16,7 @@ import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { RANGE_SLIDER_CONTROL } from '../../../../../common';
import { initializeDataControl } from '../initialize_data_control';
import type { DataControlFactory, DataControlServices } from '../types';
import type { DataControlFactory } from '../types';
import { RangeSliderControl } from './components/range_slider_control';
import { hasNoResults$ } from './has_no_results';
import { minMax$ } from './min_max';
@ -24,9 +24,10 @@ import { initializeRangeControlSelections } from './range_control_selections';
import { RangeSliderStrings } from './range_slider_strings';
import type { RangesliderControlApi, RangesliderControlState } from './types';
export const getRangesliderControlFactory = (
services: DataControlServices
): DataControlFactory<RangesliderControlState, RangesliderControlApi> => {
export const getRangesliderControlFactory = (): DataControlFactory<
RangesliderControlState,
RangesliderControlApi
> => {
return {
type: RANGE_SLIDER_CONTROL,
getIconType: () => 'controlsHorizontal',
@ -71,8 +72,7 @@ export const getRangesliderControlFactory = (
{
step: step$,
},
controlGroupApi,
services
controlGroupApi
);
const selections = initializeRangeControlSelections(
@ -111,13 +111,14 @@ export const getRangesliderControlFactory = (
}
);
const dataLoadingSubscription = combineLatest([loadingMinMax$, loadingHasNoResults$])
const dataLoadingSubscription = combineLatest([
loadingMinMax$,
loadingHasNoResults$,
dataControl.api.dataLoading,
])
.pipe(
map((values) => {
return values.some((value) => {
return value;
});
})
debounceTime(100),
map((values) => values.some((value) => value))
)
.subscribe((isLoading) => {
dataLoading$.next(isLoading);
@ -138,7 +139,6 @@ export const getRangesliderControlFactory = (
const min$ = new BehaviorSubject<number | undefined>(undefined);
const minMaxSubscription = minMax$({
controlFetch$,
data: services.data,
dataViews$: dataControl.api.dataViews,
fieldName$: dataControl.stateManager.fieldName,
setIsLoading: (isLoading: boolean) => {
@ -198,7 +198,6 @@ export const getRangesliderControlFactory = (
const selectionHasNoResults$ = new BehaviorSubject(false);
const hasNotResultsSubscription = hasNoResults$({
controlFetch$,
data: services.data,
dataViews$: dataControl.api.dataViews,
rangeFilters$: dataControl.api.filters$,
ignoreParentSettings$: controlGroupApi.ignoreParentSettings$,

View file

@ -8,25 +8,23 @@
*/
import { estypes } from '@elastic/elasticsearch';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataView } from '@kbn/data-views-plugin/public';
import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query';
import { PublishesDataViews } from '@kbn/presentation-publishing';
import { combineLatest, lastValueFrom, Observable, switchMap, tap } from 'rxjs';
import { Observable, combineLatest, lastValueFrom, switchMap, tap } from 'rxjs';
import { dataService } from '../../../../services/kibana_services';
import { ControlFetchContext } from '../../../control_group/control_fetch';
import { ControlGroupApi } from '../../../control_group/types';
import { DataControlApi } from '../types';
export function hasNoResults$({
controlFetch$,
data,
dataViews$,
rangeFilters$,
ignoreParentSettings$,
setIsLoading,
}: {
controlFetch$: Observable<ControlFetchContext>;
data: DataPublicPluginStart;
dataViews$?: PublishesDataViews['dataViews'];
rangeFilters$: DataControlApi['filters$'];
ignoreParentSettings$: ControlGroupApi['ignoreParentSettings$'];
@ -53,7 +51,6 @@ export function hasNoResults$({
prevRequestAbortController = abortController;
return await hasNoResults({
abortSignal: abortController.signal,
data,
dataView,
rangeFilter,
...controlFetchContext,
@ -71,7 +68,6 @@ export function hasNoResults$({
async function hasNoResults({
abortSignal,
data,
dataView,
filters,
query,
@ -79,14 +75,13 @@ async function hasNoResults({
timeRange,
}: {
abortSignal: AbortSignal;
data: DataPublicPluginStart;
dataView: DataView;
filters?: Filter[];
query?: Query | AggregateQuery;
rangeFilter: Filter;
timeRange?: TimeRange;
}): Promise<boolean> {
const searchSource = await data.search.searchSource.create();
const searchSource = await dataService.search.searchSource.create();
searchSource.setField('size', 0);
searchSource.setField('index', dataView);
// Tracking total hits accurately has a performance cost
@ -97,7 +92,7 @@ async function hasNoResults({
const allFilters = filters ? [...filters] : [];
allFilters.push(rangeFilter);
if (timeRange) {
const timeFilter = data.query.timefilter.timefilter.createFilter(dataView, timeRange);
const timeFilter = dataService.query.timefilter.timefilter.createFilter(dataView, timeRange);
if (timeFilter) allFilters.push(timeFilter);
}
if (allFilters.length) {

View file

@ -8,26 +8,24 @@
*/
import { estypes } from '@elastic/elasticsearch';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query';
import { PublishesDataViews, PublishingSubject } from '@kbn/presentation-publishing';
import { combineLatest, lastValueFrom, Observable, of, startWith, switchMap, tap } from 'rxjs';
import { apiPublishesReload } from '@kbn/presentation-publishing/interfaces/fetch/publishes_reload';
import { Observable, combineLatest, lastValueFrom, of, startWith, switchMap, tap } from 'rxjs';
import { dataService } from '../../../../services/kibana_services';
import { ControlFetchContext } from '../../../control_group/control_fetch';
import { ControlGroupApi } from '../../../control_group/types';
export function minMax$({
controlFetch$,
controlGroupApi,
data,
dataViews$,
fieldName$,
setIsLoading,
}: {
controlFetch$: Observable<ControlFetchContext>;
controlGroupApi: ControlGroupApi;
data: DataPublicPluginStart;
dataViews$: PublishesDataViews['dataViews'];
fieldName$: PublishingSubject<string>;
setIsLoading: (isLoading: boolean) => void;
@ -60,7 +58,6 @@ export function minMax$({
prevRequestAbortController = abortController;
return await getMinMax({
abortSignal: abortController.signal,
data,
dataView,
field: dataViewField,
...controlFetchContext,
@ -77,7 +74,6 @@ export function minMax$({
export async function getMinMax({
abortSignal,
data,
dataView,
field,
filters,
@ -85,20 +81,19 @@ export async function getMinMax({
timeRange,
}: {
abortSignal: AbortSignal;
data: DataPublicPluginStart;
dataView: DataView;
field: DataViewField;
filters?: Filter[];
query?: Query | AggregateQuery;
timeRange?: TimeRange;
}): Promise<{ min: number | undefined; max: number | undefined }> {
const searchSource = await data.search.searchSource.create();
const searchSource = await dataService.search.searchSource.create();
searchSource.setField('size', 0);
searchSource.setField('index', dataView);
const allFilters = filters ? [...filters] : [];
if (timeRange) {
const timeFilter = data.query.timefilter.timefilter.createFilter(dataView, timeRange);
const timeFilter = dataService.query.timefilter.timefilter.createFilter(dataView, timeRange);
if (timeFilter) allFilters.push(timeFilter);
}
if (allFilters.length) {

View file

@ -7,22 +7,17 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { CoreSetup } from '@kbn/core/public';
import type { ControlsPluginStartDeps } from '../../../../types';
import { registerControlFactory } from '../../../control_factory_registry';
import { RANGE_SLIDER_CONTROL } from '../../../../../common';
import { untilPluginStartServicesReady } from '../../../../services/kibana_services';
import { registerControlFactory } from '../../../control_factory_registry';
export function registerRangeSliderControl(coreSetup: CoreSetup<ControlsPluginStartDeps>) {
export function registerRangeSliderControl() {
registerControlFactory(RANGE_SLIDER_CONTROL, async () => {
const [{ getRangesliderControlFactory }, [coreStart, depsStart]] = await Promise.all([
const [{ getRangesliderControlFactory }] = await Promise.all([
import('./get_range_slider_control_factory'),
coreSetup.getStartServices(),
untilPluginStartServicesReady(),
]);
return getRangesliderControlFactory({
core: coreStart,
data: depsStart.data,
dataViews: depsStart.data.dataViews,
});
return getRangesliderControlFactory();
});
}

View file

@ -7,10 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { CoreStart } from '@kbn/core/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewField } from '@kbn/data-views-plugin/common';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { FieldFormatConvertFunction } from '@kbn/field-formats-plugin/common';
import {
HasEditCapabilities,
@ -62,12 +59,6 @@ export const isDataControlFactory = (
return typeof (factory as DataControlFactory).isFieldCompatible === 'function';
};
export interface DataControlServices {
core: CoreStart;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
}
interface DataControlField {
field: DataViewField;
compatibleControlTypes: string[];

View file

@ -9,6 +9,7 @@
import { EuiRangeTick } from '@elastic/eui';
import { TimeRange } from '@kbn/es-query';
import { coreServices, dataService } from '../../../services/kibana_services';
import {
FROM_INDEX,
getStepSize,
@ -17,7 +18,6 @@ import {
roundUpToNextStepSizeFactor,
TO_INDEX,
} from './time_utils';
import { Services } from './types';
export interface TimeRangeMeta {
format: string;
@ -29,12 +29,9 @@ export interface TimeRangeMeta {
timeRangeMin: number;
}
export function getTimeRangeMeta(
timeRange: TimeRange | undefined,
services: Services
): TimeRangeMeta {
const nextBounds = timeRangeToBounds(timeRange ?? getDefaultTimeRange(services), services);
const ticks = getTicks(nextBounds[FROM_INDEX], nextBounds[TO_INDEX], getTimezone(services));
export function getTimeRangeMeta(timeRange: TimeRange | undefined): TimeRangeMeta {
const nextBounds = timeRangeToBounds(timeRange ?? getDefaultTimeRange());
const ticks = getTicks(nextBounds[FROM_INDEX], nextBounds[TO_INDEX], getTimezone());
const { format, stepSize } = getStepSize(ticks);
return {
format,
@ -47,17 +44,17 @@ export function getTimeRangeMeta(
};
}
export function getTimezone(services: Services) {
return services.core.uiSettings.get('dateFormat:tz', 'Browser');
export function getTimezone() {
return coreServices.uiSettings.get('dateFormat:tz', 'Browser');
}
function getDefaultTimeRange(services: Services) {
const defaultTimeRange = services.core.uiSettings.get('timepicker:timeDefaults');
function getDefaultTimeRange() {
const defaultTimeRange = coreServices.uiSettings.get('timepicker:timeDefaults');
return defaultTimeRange ? defaultTimeRange : { from: 'now-15m', to: 'now' };
}
function timeRangeToBounds(timeRange: TimeRange, services: Services): [number, number] {
const timeRangeBounds = services.data.query.timefilter.timefilter.calculateBounds(timeRange);
function timeRangeToBounds(timeRange: TimeRange): [number, number] {
const timeRangeBounds = dataService.query.timefilter.timefilter.calculateBounds(timeRange);
return timeRangeBounds.min === undefined || timeRangeBounds.max === undefined
? [Date.now() - 1000 * 60 * 15, Date.now()]
: [timeRangeBounds.min.valueOf(), timeRangeBounds.max.valueOf()];

View file

@ -7,14 +7,15 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import React from 'react';
import { BehaviorSubject } from 'rxjs';
import dateMath from '@kbn/datemath';
import { TimeRange } from '@kbn/es-query';
import { StateComparators } from '@kbn/presentation-publishing';
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { BehaviorSubject } from 'rxjs';
import { dataService } from '../../../services/kibana_services';
import { getMockedControlGroupApi } from '../mocks/control_mocks';
import { ControlApiRegistration } from '../types';
import { getTimesliderControlFactory } from './get_timeslider_control_factory';
@ -28,18 +29,14 @@ describe('TimesliderControlApi', () => {
};
const controlGroupApi = getMockedControlGroupApi(dashboardApi);
const dataStartServiceMock = dataPluginMock.createStartContract();
dataStartServiceMock.query.timefilter.timefilter.calculateBounds = (timeRange: TimeRange) => {
dataService.query.timefilter.timefilter.calculateBounds = (timeRange: TimeRange) => {
const now = new Date();
return {
min: dateMath.parse(timeRange.from, { forceNow: now }),
max: dateMath.parse(timeRange.to, { roundUp: true, forceNow: now }),
};
};
const factory = getTimesliderControlFactory({
core: coreMock.createStart(),
data: dataStartServiceMock,
});
const factory = getTimesliderControlFactory();
let comparators: StateComparators<TimesliderControlState> | undefined;
function buildApiMock(
api: ControlApiRegistration<TimesliderControlApi>,

View file

@ -36,22 +36,23 @@ import {
roundDownToNextStepSizeFactor,
roundUpToNextStepSizeFactor,
} from './time_utils';
import { Services, Timeslice, TimesliderControlApi, TimesliderControlState } from './types';
import { Timeslice, TimesliderControlApi, TimesliderControlState } from './types';
const displayName = i18n.translate('controls.timesliderControl.displayName', {
defaultMessage: 'Time slider',
});
export const getTimesliderControlFactory = (
services: Services
): ControlFactory<TimesliderControlState, TimesliderControlApi> => {
export const getTimesliderControlFactory = (): ControlFactory<
TimesliderControlState,
TimesliderControlApi
> => {
return {
type: TIME_SLIDER_CONTROL,
getIconType: () => 'search',
getDisplayName: () => displayName,
buildControl: async (initialState, buildApi, uuid, controlGroupApi) => {
const { timeRangeMeta$, formatDate, cleanupTimeRangeSubscription } =
initTimeRangeSubscription(controlGroupApi, services);
initTimeRangeSubscription(controlGroupApi);
const timeslice$ = new BehaviorSubject<[number, number] | undefined>(undefined);
const isAnchored$ = new BehaviorSubject<boolean | undefined>(initialState.isAnchored);
const isPopoverOpen$ = new BehaviorSubject(false);

View file

@ -14,26 +14,23 @@ import moment from 'moment';
import { BehaviorSubject, skip } from 'rxjs';
import { getTimeRangeMeta, getTimezone, TimeRangeMeta } from './get_time_range_meta';
import { getMomentTimezone } from './time_utils';
import { Services } from './types';
export function initTimeRangeSubscription(controlGroupApi: unknown, services: Services) {
export function initTimeRangeSubscription(controlGroupApi: unknown) {
const timeRange$ =
apiHasParentApi(controlGroupApi) && apiPublishesTimeRange(controlGroupApi.parentApi)
? controlGroupApi.parentApi.timeRange$
: new BehaviorSubject<TimeRange | undefined>(undefined);
const timeRangeMeta$ = new BehaviorSubject<TimeRangeMeta>(
getTimeRangeMeta(timeRange$.value, services)
);
const timeRangeMeta$ = new BehaviorSubject<TimeRangeMeta>(getTimeRangeMeta(timeRange$.value));
const timeRangeSubscription = timeRange$.pipe(skip(1)).subscribe((timeRange) => {
timeRangeMeta$.next(getTimeRangeMeta(timeRange, services));
timeRangeMeta$.next(getTimeRangeMeta(timeRange));
});
return {
timeRangeMeta$,
formatDate: (epoch: number) => {
return moment
.tz(epoch, getMomentTimezone(getTimezone(services)))
.tz(epoch, getMomentTimezone(getTimezone()))
.locale(i18n.getLocale())
.format(timeRangeMeta$.value.format);
},

View file

@ -7,20 +7,16 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { CoreSetup } from '@kbn/core/public';
import type { ControlsPluginStartDeps } from '../../../types';
import { registerControlFactory } from '../../control_factory_registry';
import { TIME_SLIDER_CONTROL } from '../../../../common';
import { untilPluginStartServicesReady } from '../../../services/kibana_services';
import { registerControlFactory } from '../../control_factory_registry';
export function registerTimeSliderControl(coreSetup: CoreSetup<ControlsPluginStartDeps>) {
export function registerTimeSliderControl() {
registerControlFactory(TIME_SLIDER_CONTROL, async () => {
const [{ getTimesliderControlFactory }, [coreStart, depsStart]] = await Promise.all([
const [{ getTimesliderControlFactory }] = await Promise.all([
import('./get_timeslider_control_factory'),
coreSetup.getStartServices(),
untilPluginStartServicesReady(),
]);
return getTimesliderControlFactory({
core: coreStart,
data: depsStart.data,
});
return getTimesliderControlFactory();
});
}

View file

@ -7,8 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { CoreStart } from '@kbn/core/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { PublishesPanelTitle, PublishesTimeslice } from '@kbn/presentation-publishing';
import type { DefaultControlState } from '../../../../common';
import type { DefaultControlApi } from '../types';
@ -25,8 +23,3 @@ export interface TimesliderControlState extends DefaultControlState {
export type TimesliderControlApi = DefaultControlApi &
Pick<PublishesPanelTitle, 'defaultPanelTitle'> &
PublishesTimeslice;
export interface Services {
core: CoreStart;
data: DataPublicPluginStart;
}

View file

@ -40,15 +40,15 @@ export type DefaultControlApi = PublishesDataLoading &
HasType &
HasUniqueId &
HasParentApi<ControlGroupApi> & {
// Can not use HasSerializableState interface
// HasSerializableState types serializeState as function returning 'MaybePromise'
// Controls serializeState is sync
serializeState: () => SerializedPanelState<DefaultControlState>;
/** TODO: Make these non-public as part of https://github.com/elastic/kibana/issues/174961 */
setDataLoading: (loading: boolean) => void;
setBlockingError: (error: Error | undefined) => void;
grow: PublishingSubject<boolean | undefined>;
width: PublishingSubject<ControlWidth | undefined>;
// Can not use HasSerializableState interface
// HasSerializableState types serializeState as function returning 'MaybePromise'
// Controls serializeState is sync
serializeState: () => SerializedPanelState<DefaultControlState>;
};
export type ControlApiRegistration<ControlApi extends DefaultControlApi = DefaultControlApi> = Omit<
@ -62,7 +62,6 @@ export type ControlApiInitialization<ControlApi extends DefaultControlApi = Defa
'serializeState' | 'getTypeDisplayName' | 'clearSelections'
>;
// TODO: Move this to the Control plugin's setup contract
export interface ControlFactory<
State extends DefaultControlState = DefaultControlState,
ControlApi extends DefaultControlApi = DefaultControlApi

View file

@ -9,26 +9,22 @@
import React from 'react';
import { coreMock } from '@kbn/core/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks';
import { Filter } from '@kbn/es-query';
import { PublishesUnifiedSearch, PublishingSubject } from '@kbn/presentation-publishing';
import { act, render, waitFor } from '@testing-library/react';
import { ControlGroupRendererApi } from '.';
import { CONTROL_GROUP_TYPE } from '../..';
import { getControlGroupEmbeddableFactory } from '../control_group/get_control_group_factory';
import { ControlGroupRenderer, ControlGroupRendererProps } from './control_group_renderer';
import { CONTROL_GROUP_TYPE } from '../..';
type ParentApiType = PublishesUnifiedSearch & {
unifiedSearchFilters$?: PublishingSubject<Filter[] | undefined>;
};
describe('control group renderer', () => {
const core = coreMock.createStart();
const dataViews = dataViewPluginMocks.createStartContract();
const factory = getControlGroupEmbeddableFactory({ core, dataViews });
const factory = getControlGroupEmbeddableFactory();
const buildControlGroupSpy = jest.spyOn(factory, 'buildEmbeddable');
const mountControlGroupRenderer = async (

View file

@ -1,46 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlFactory, DefaultControlApi } from '../../react_controls/controls/types';
import { ControlsServiceType } from './types';
export type ControlsServiceFactory = PluginServiceFactory<ControlsServiceType>;
export const controlsServiceFactory = () => getStubControlsService();
export const getStubControlsService = () => {
const controlsFactoriesMap: { [key: string]: ControlFactory<any, any> } = {};
const mockRegisterControlFactory = async <
State extends object = object,
ApiType extends DefaultControlApi = DefaultControlApi
>(
controlType: string,
getFactory: () => Promise<ControlFactory<State, ApiType>>
) => {
controlsFactoriesMap[controlType] = (await getFactory()) as ControlFactory<any, any>;
};
const mockGetControlFactory = <
State extends object = object,
ApiType extends DefaultControlApi = DefaultControlApi
>(
type: string
) => {
return controlsFactoriesMap[type] as ControlFactory<State, ApiType>;
};
const getAllControlTypes = () => Object.keys(controlsFactoriesMap);
return {
registerControlFactory: mockRegisterControlFactory,
getControlFactory: mockGetControlFactory,
getAllControlTypes,
};
};

View file

@ -1,24 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
getAllControlTypes,
getControlFactory,
registerControlFactory,
} from '../../react_controls/control_factory_registry';
import { ControlsServiceType } from './types';
export const controlsServiceFactory = () => controlsService;
// export controls service directly for use in plugin setup lifecycle
export const controlsService: ControlsServiceType = {
registerControlFactory,
getControlFactory,
getAllControlTypes,
};

View file

@ -1,23 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import {
getAllControlTypes,
getControlFactory,
registerControlFactory,
} from '../../react_controls/control_factory_registry';
export type ControlsServiceFactory = PluginServiceFactory<ControlsServiceType>;
export interface ControlsServiceType {
registerControlFactory: typeof registerControlFactory;
getControlFactory: typeof getControlFactory;
getAllControlTypes: typeof getAllControlTypes;
}

View file

@ -1,24 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { analyticsServiceMock, coreMock, themeServiceMock } from '@kbn/core/public/mocks';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsCoreService } from './types';
export type CoreServiceFactory = PluginServiceFactory<ControlsCoreService>;
export const coreServiceFactory: CoreServiceFactory = () => {
const corePluginMock = coreMock.createStart();
return {
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
theme: themeServiceMock.createSetupContract(),
i18n: corePluginMock.i18n,
notifications: corePluginMock.notifications,
};
};

View file

@ -1,28 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsCoreService } from './types';
import { ControlsPluginStartDeps } from '../../types';
export type CoreServiceFactory = KibanaPluginServiceFactory<
ControlsCoreService,
ControlsPluginStartDeps
>;
export const coreServiceFactory: CoreServiceFactory = ({ coreStart }) => {
const { analytics, theme, i18n, notifications } = coreStart;
return {
analytics,
theme,
i18n,
notifications,
};
};

View file

@ -1,17 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { CoreStart } from '@kbn/core/public';
export interface ControlsCoreService {
analytics: CoreStart['analytics'];
i18n: CoreStart['i18n'];
theme: CoreStart['theme'];
notifications: CoreStart['notifications'];
}

View file

@ -1,31 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { of } from 'rxjs';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { ControlsDataService } from './types';
export type DataServiceFactory = PluginServiceFactory<ControlsDataService>;
export const dataServiceFactory: DataServiceFactory = () => ({
query: {} as unknown as DataPublicPluginStart['query'],
searchSource: {
create: () => ({
setField: () => {},
fetch$: () =>
of({
rawResponse: { aggregations: { minAgg: { value: 0 }, maxAgg: { value: 1000 } } },
}),
}),
} as unknown as DataPublicPluginStart['search']['searchSource'],
timefilter: {
createFilter: () => {},
} as unknown as DataPublicPluginStart['query']['timefilter']['timefilter'],
fetchFieldRange: () => Promise.resolve({ min: 0, max: 100 }),
});

View file

@ -1,29 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStartDeps } from '../../types';
import { ControlsDataService } from './types';
export type DataServiceFactory = KibanaPluginServiceFactory<
ControlsDataService,
ControlsPluginStartDeps
>;
export const dataServiceFactory: DataServiceFactory = ({ startPlugins }) => {
const {
data: { query: queryPlugin, search },
} = startPlugins;
return {
query: queryPlugin,
searchSource: search.searchSource,
timefilter: queryPlugin.timefilter.timefilter,
};
};

View file

@ -1,15 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
export interface ControlsDataService {
query: DataPublicPluginStart['query'];
searchSource: DataPublicPluginStart['search']['searchSource'];
timefilter: DataPublicPluginStart['query']['timefilter']['timefilter'];
}

View file

@ -1,52 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { DataView } from '@kbn/data-views-plugin/common';
import { ControlsDataViewsService } from './types';
export type DataViewsServiceFactory = PluginServiceFactory<ControlsDataViewsService>;
let currentDataView: DataView | undefined;
export const injectStorybookDataView = (dataView?: DataView) => (currentDataView = dataView);
export const dataViewsServiceFactory: DataViewsServiceFactory = () => ({
get: ((dataViewId) =>
new Promise((resolve, reject) =>
setTimeout(() => {
if (!currentDataView) {
reject(
new Error(
'mock DataViews service currentDataView is undefined, call injectStorybookDataView to set'
)
);
} else if (currentDataView.id === dataViewId) {
resolve(currentDataView);
} else {
reject(
new Error(
`mock DataViews service currentDataView.id: ${currentDataView.id} does not match requested dataViewId: ${dataViewId}`
)
);
}
}, 100)
) as unknown) as DataViewsPublicPluginStart['get'],
getIdsWithTitle: (() =>
new Promise((resolve) =>
setTimeout(() => {
const idsWithTitle: Array<{ id: string | undefined; title: string }> = [];
if (currentDataView) {
idsWithTitle.push({ id: currentDataView.id, title: currentDataView.title });
}
resolve(idsWithTitle);
}, 100)
) as unknown) as DataViewsPublicPluginStart['getIdsWithTitle'],
getDefaultId: () => Promise.resolve(currentDataView?.id ?? null),
});

View file

@ -1,29 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStartDeps } from '../../types';
import { ControlsDataViewsService } from './types';
export type DataViewsServiceFactory = KibanaPluginServiceFactory<
ControlsDataViewsService,
ControlsPluginStartDeps
>;
export const dataViewsServiceFactory: DataViewsServiceFactory = ({ startPlugins }) => {
const {
dataViews: { get, getIdsWithTitle, getDefaultId },
} = startPlugins;
return {
get,
getDefaultId,
getIdsWithTitle,
};
};

View file

@ -1,16 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
export interface ControlsDataViewsService {
get: DataViewsPublicPluginStart['get'];
getDefaultId: DataViewsPublicPluginStart['getDefaultId'];
getIdsWithTitle: DataViewsPublicPluginStart['getIdsWithTitle'];
}

View file

@ -1,19 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks';
import { ControlsEmbeddableService } from './types';
export type EmbeddableServiceFactory = PluginServiceFactory<ControlsEmbeddableService>;
export const embeddableServiceFactory: EmbeddableServiceFactory = () => {
const { doStart } = embeddablePluginMock.createInstance();
const start = doStart();
return { getEmbeddableFactory: start.getEmbeddableFactory };
};

View file

@ -1,23 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsEmbeddableService } from './types';
import { ControlsPluginStartDeps } from '../../types';
export type EmbeddableServiceFactory = KibanaPluginServiceFactory<
ControlsEmbeddableService,
ControlsPluginStartDeps
>;
export const embeddableServiceFactory: EmbeddableServiceFactory = ({ startPlugins }) => {
return {
getEmbeddableFactory: startPlugins.embeddable.getEmbeddableFactory,
};
};

View file

@ -1,14 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { EmbeddableStart } from '@kbn/embeddable-plugin/public';
export interface ControlsEmbeddableService {
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
}

View file

@ -1,19 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { HttpResponse } from '@kbn/core/public';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsHTTPService } from './types';
type HttpServiceFactory = PluginServiceFactory<ControlsHTTPService>;
export const httpServiceFactory: HttpServiceFactory = () => ({
get: async () => ({} as unknown as HttpResponse),
fetch: async () => ({} as unknown as HttpResponse),
});

View file

@ -1,27 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsHTTPService } from './types';
import { ControlsPluginStartDeps } from '../../types';
export type HttpServiceFactory = KibanaPluginServiceFactory<
ControlsHTTPService,
ControlsPluginStartDeps
>;
export const httpServiceFactory: HttpServiceFactory = ({ coreStart }) => {
const {
http: { get, fetch },
} = coreStart;
return {
get,
fetch,
};
};

View file

@ -1,15 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { CoreSetup } from '@kbn/core/public';
export interface ControlsHTTPService {
get: CoreSetup['http']['get'];
fetch: CoreSetup['http']['fetch'];
}

View file

@ -1,10 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export { pluginServices } from './plugin_services';

View file

@ -0,0 +1,42 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { BehaviorSubject } from 'rxjs';
import { CoreStart } from '@kbn/core/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { ControlsPluginStartDeps } from '../types';
export let coreServices: CoreStart;
export let dataService: DataPublicPluginStart;
export let dataViewsService: DataViewsPublicPluginStart;
const servicesReady$ = new BehaviorSubject(false);
export const setKibanaServices = (kibanaCore: CoreStart, deps: ControlsPluginStartDeps) => {
coreServices = kibanaCore;
dataService = deps.data;
dataViewsService = deps.dataViews;
servicesReady$.next(true);
};
export const untilPluginStartServicesReady = () => {
if (servicesReady$.value) return Promise.resolve();
return new Promise<void>((resolve) => {
const subscription = servicesReady$.subscribe((isInitialized) => {
if (isInitialized) {
subscription.unsubscribe();
resolve();
}
});
});
};

View file

@ -0,0 +1,23 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
import { setKibanaServices } from './kibana_services';
export const setStubKibanaServices = () => {
setKibanaServices(coreMock.createStart(), {
data: dataPluginMock.createStartContract(),
dataViews: dataViewPluginMocks.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
});
};

View file

@ -1,33 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
MountPoint,
OverlayFlyoutOpenOptions,
OverlayModalConfirmOptions,
OverlayRef,
} from '@kbn/core/public';
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsOverlaysService } from './types';
type OverlaysServiceFactory = PluginServiceFactory<ControlsOverlaysService>;
class StubRef implements OverlayRef {
public readonly onClose: Promise<void> = Promise.resolve();
public close(): Promise<void> {
return this.onClose;
}
}
export const overlaysServiceFactory: OverlaysServiceFactory = () => ({
openFlyout: (mount: MountPoint, options?: OverlayFlyoutOpenOptions) => new StubRef(),
openConfirm: (message: MountPoint | string, options?: OverlayModalConfirmOptions) =>
Promise.resolve(true),
});

View file

@ -1,27 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStartDeps } from '../../types';
import { ControlsOverlaysService } from './types';
export type OverlaysServiceFactory = KibanaPluginServiceFactory<
ControlsOverlaysService,
ControlsPluginStartDeps
>;
export const overlaysServiceFactory: OverlaysServiceFactory = ({ coreStart }) => {
const {
overlays: { openFlyout, openConfirm },
} = coreStart;
return {
openFlyout,
openConfirm,
};
};

View file

@ -1,20 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
MountPoint,
OverlayFlyoutOpenOptions,
OverlayModalConfirmOptions,
OverlayRef,
} from '@kbn/core/public';
export interface ControlsOverlaysService {
openFlyout(mount: MountPoint, options?: OverlayFlyoutOpenOptions): OverlayRef;
openConfirm(message: MountPoint | string, options?: OverlayModalConfirmOptions): Promise<boolean>;
}

View file

@ -1,54 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
PluginServiceProvider,
PluginServiceProviders,
PluginServiceRegistry,
PluginServices,
} from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStart } from '../types';
import { ControlsServices } from './types';
import { controlsServiceFactory } from './controls/controls.stub';
import { coreServiceFactory } from './core/core.stub';
import { dataServiceFactory } from './data/data.stub';
import { dataViewsServiceFactory } from './data_views/data_views.stub';
import { embeddableServiceFactory } from './embeddable/embeddable.stub';
import { httpServiceFactory } from './http/http.stub';
import { overlaysServiceFactory } from './overlays/overlays.stub';
import { settingsServiceFactory } from './settings/settings.stub';
import { unifiedSearchServiceFactory } from './unified_search/unified_search.stub';
import { storageServiceFactory } from './storage/storage_service.stub';
export const providers: PluginServiceProviders<ControlsServices> = {
embeddable: new PluginServiceProvider(embeddableServiceFactory),
controls: new PluginServiceProvider(controlsServiceFactory),
data: new PluginServiceProvider(dataServiceFactory),
dataViews: new PluginServiceProvider(dataViewsServiceFactory),
http: new PluginServiceProvider(httpServiceFactory),
overlays: new PluginServiceProvider(overlaysServiceFactory),
settings: new PluginServiceProvider(settingsServiceFactory),
core: new PluginServiceProvider(coreServiceFactory),
storage: new PluginServiceProvider(storageServiceFactory),
unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory),
};
export const pluginServices = new PluginServices<ControlsServices>();
export const registry = new PluginServiceRegistry<ControlsServices>(providers);
export const getStubPluginServices = (): ControlsPluginStart => {
pluginServices.setRegistry(registry.start({}));
return {
getControlFactory: pluginServices.getServices().controls.getControlFactory,
getAllControlTypes: pluginServices.getServices().controls.getAllControlTypes,
};
};

View file

@ -1,52 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
KibanaPluginServiceParams,
PluginServiceProvider,
PluginServiceProviders,
PluginServiceRegistry,
PluginServices,
} from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStartDeps } from '../types';
import { ControlsServices } from './types';
import { controlsServiceFactory } from './controls/controls_service';
import { coreServiceFactory } from './core/core_service';
import { dataServiceFactory } from './data/data_service';
import { dataViewsServiceFactory } from './data_views/data_views_service';
import { embeddableServiceFactory } from './embeddable/embeddable_service';
import { httpServiceFactory } from './http/http_service';
import { overlaysServiceFactory } from './overlays/overlays_service';
import { settingsServiceFactory } from './settings/settings_service';
import { controlsStorageServiceFactory } from './storage/storage_service';
import { unifiedSearchServiceFactory } from './unified_search/unified_search_service';
export const providers: PluginServiceProviders<
ControlsServices,
KibanaPluginServiceParams<ControlsPluginStartDeps>
> = {
controls: new PluginServiceProvider(controlsServiceFactory),
data: new PluginServiceProvider(dataServiceFactory),
dataViews: new PluginServiceProvider(dataViewsServiceFactory),
embeddable: new PluginServiceProvider(embeddableServiceFactory),
http: new PluginServiceProvider(httpServiceFactory),
overlays: new PluginServiceProvider(overlaysServiceFactory),
settings: new PluginServiceProvider(settingsServiceFactory),
storage: new PluginServiceProvider(controlsStorageServiceFactory),
core: new PluginServiceProvider(coreServiceFactory),
unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory),
};
export const pluginServices = new PluginServices<ControlsServices>();
export const registry = new PluginServiceRegistry<
ControlsServices,
KibanaPluginServiceParams<ControlsPluginStartDeps>
>(providers);

View file

@ -1,18 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsSettingsService } from './types';
export type SettingsServiceFactory = PluginServiceFactory<ControlsSettingsService>;
export const settingsServiceFactory: SettingsServiceFactory = () => ({
getTimezone: () => 'Browser',
getDateFormat: () => 'MMM D, YYYY @ HH:mm:ss.SSS',
getDefaultTimeRange: () => ({ from: 'now-15m', to: 'now' }),
});

View file

@ -1,32 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsSettingsService } from './types';
import { ControlsPluginStartDeps } from '../../types';
export type SettingsServiceFactory = KibanaPluginServiceFactory<
ControlsSettingsService,
ControlsPluginStartDeps
>;
export const settingsServiceFactory: SettingsServiceFactory = ({ coreStart }) => {
return {
getDateFormat: () => {
return coreStart.uiSettings.get('dateFormat', 'MMM D, YYYY @ HH:mm:ss.SSS');
},
getTimezone: () => {
return coreStart.uiSettings.get('dateFormat:tz', 'Browser');
},
getDefaultTimeRange: () => {
const defaultTimeRange = coreStart.uiSettings.get('timepicker:timeDefaults');
return defaultTimeRange ? defaultTimeRange : { from: 'now-15m', to: 'now' };
},
};
};

View file

@ -1,16 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { TimeRange } from '@kbn/es-query';
export interface ControlsSettingsService {
getTimezone: () => string;
getDateFormat: () => string;
getDefaultTimeRange: () => TimeRange;
}

View file

@ -1,20 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsStorageService } from './types';
type StorageServiceFactory = PluginServiceFactory<ControlsStorageService>;
export const storageServiceFactory: StorageServiceFactory = () => {
return {
getShowInvalidSelectionWarning: () => false,
setShowInvalidSelectionWarning: (value: boolean) => null,
};
};

View file

@ -1,31 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { ControlsStorageService } from './types';
const STORAGE_KEY = 'controls:showInvalidSelectionWarning';
class StorageService implements ControlsStorageService {
private storage: Storage;
constructor() {
this.storage = new Storage(localStorage);
}
getShowInvalidSelectionWarning = () => {
return this.storage.get(STORAGE_KEY);
};
setShowInvalidSelectionWarning = (value: boolean) => {
this.storage.set(STORAGE_KEY, value);
};
}
export const controlsStorageServiceFactory = () => new StorageService();

View file

@ -1,13 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export interface ControlsStorageService {
getShowInvalidSelectionWarning: () => boolean;
setShowInvalidSelectionWarning: (value: boolean) => void;
}

View file

@ -1,35 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { ControlsServiceType } from './controls/types';
import { ControlsCoreService } from './core/types';
import { ControlsDataService } from './data/types';
import { ControlsDataViewsService } from './data_views/types';
import { ControlsEmbeddableService } from './embeddable/types';
import { ControlsHTTPService } from './http/types';
import { ControlsOverlaysService } from './overlays/types';
import { ControlsSettingsService } from './settings/types';
import { ControlsStorageService } from './storage/types';
import { ControlsUnifiedSearchService } from './unified_search/types';
export interface ControlsServices {
// dependency services
dataViews: ControlsDataViewsService;
overlays: ControlsOverlaysService;
embeddable: ControlsEmbeddableService;
data: ControlsDataService;
unifiedSearch: ControlsUnifiedSearchService;
http: ControlsHTTPService;
settings: ControlsSettingsService;
core: ControlsCoreService;
// controls plugin's own services
controls: ControlsServiceType;
storage: ControlsStorageService;
}

View file

@ -1,14 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
export interface ControlsUnifiedSearchService {
autocomplete: UnifiedSearchPublicPluginStart['autocomplete'];
}

View file

@ -1,27 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { DataViewField } from '@kbn/data-views-plugin/common';
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import { ControlsUnifiedSearchService } from './types';
let valueSuggestionMethod = ({ field, query }: { field: DataViewField; query: string }) =>
Promise.resolve(['storybook', 'default', 'values']);
export const replaceValueSuggestionMethod = (
newMethod: ({ field, query }: { field: DataViewField; query: string }) => Promise<string[]>
) => (valueSuggestionMethod = newMethod);
export type UnifiedSearchServiceFactory = PluginServiceFactory<ControlsUnifiedSearchService>;
export const unifiedSearchServiceFactory: UnifiedSearchServiceFactory = () => ({
autocomplete: {
getValueSuggestions: valueSuggestionMethod,
} as unknown as UnifiedSearchPublicPluginStart['autocomplete'],
});

View file

@ -1,27 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public';
import { ControlsPluginStartDeps } from '../../types';
import { ControlsUnifiedSearchService } from './types';
export type UnifiedSearchServiceFactory = KibanaPluginServiceFactory<
ControlsUnifiedSearchService,
ControlsPluginStartDeps
>;
export const unifiedSearchServiceFactory: UnifiedSearchServiceFactory = ({ startPlugins }) => {
const {
unifiedSearch: { autocomplete },
} = startPlugins;
return {
autocomplete,
};
};

View file

@ -9,11 +9,8 @@
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import { ControlsServiceType } from './services/controls/types';
export interface CanClearSelections {
clearSelections: () => void;
@ -26,22 +23,11 @@ export const isClearableControl = (control: unknown): control is CanClearSelecti
/**
* Plugin types
*/
export interface ControlsPluginSetup {
registerControlFactory: ControlsServiceType['registerControlFactory'];
}
export interface ControlsPluginStart {
getControlFactory: ControlsServiceType['getControlFactory'];
getAllControlTypes: ControlsServiceType['getAllControlTypes'];
}
export interface ControlsPluginSetupDeps {
embeddable: EmbeddableSetup;
}
export interface ControlsPluginStartDeps {
uiActions: UiActionsStart;
embeddable: EmbeddableStart;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
}

View file

@ -10,7 +10,7 @@
import { SerializableRecord } from '@kbn/utility-types';
import {
DEFAULT_CONTROL_STYLE,
DEFAULT_CONTROL_LABEL_POSITION,
type ControlGroupRuntimeState,
type ControlGroupSerializedState,
type ControlPanelState,
@ -19,7 +19,7 @@ import {
export const getDefaultControlGroupState = (): SerializableControlGroupState => ({
panels: {},
labelPosition: DEFAULT_CONTROL_STYLE,
labelPosition: DEFAULT_CONTROL_LABEL_POSITION,
chainingSystem: 'HIERARCHICAL',
autoApplySelections: true,
ignoreParentSettings: {

View file

@ -15,7 +15,7 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => {
router.versioned
.get({
access: 'internal',
path: '/internal/controls/optionsList/getExpensiveQueriesSetting',
path: '/internal/controls/getExpensiveQueriesSetting',
})
.addVersion(
{

View file

@ -35,7 +35,6 @@
"@kbn/presentation-containers",
"@kbn/presentation-publishing",
"@kbn/content-management-utils",
"@kbn/core-lifecycle-browser",
"@kbn/field-formats-plugin",
"@kbn/presentation-panel-plugin",
"@kbn/shared-ux-utility"