[Maps] Add query bar inputs to geo threshold alerts tracked points & boundaries (#80871)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Aaron Caldwell 2020-11-18 14:47:46 -07:00 committed by GitHub
parent a2d288d134
commit 0546f98070
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 560 additions and 59 deletions

View file

@ -3,7 +3,7 @@
"server": true,
"version": "8.0.0",
"kibanaVersion": "kibana",
"requiredPlugins": ["alerts", "features", "triggersActionsUi", "kibanaReact"],
"requiredPlugins": ["alerts", "features", "triggersActionsUi", "kibanaReact", "savedObjects", "data"],
"configPath": ["xpack", "stack_alerts"],
"ui": true
}

View file

@ -0,0 +1,210 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render BoundaryIndexExpression 1`] = `
<ExpressionWithPopover
defaultValue="Select an index pattern and geo shape field"
expressionDescription="index"
popoverContent={
<React.Fragment>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoIndexPatternSelect"
labelType="label"
>
<GeoIndexPatternSelect
IndexPatternSelectComponent={null}
http={null}
includedGeoTypes={
Array [
"geo_shape",
]
}
onChange={[Function]}
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoField"
label="Geospatial field"
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select geo field"
value=""
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="boundaryNameFieldSelect"
label="Human-readable boundary name (optional)"
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select boundary name"
value="testNameField"
/>
</EuiFormRow>
</React.Fragment>
}
/>
`;
exports[`should render EntityIndexExpression 1`] = `
<ExpressionWithPopover
defaultValue="Select an index pattern and geo shape/point field"
expressionDescription="index"
isInvalid={false}
popoverContent={
<React.Fragment>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoIndexPatternSelect"
labelType="label"
>
<GeoIndexPatternSelect
IndexPatternSelectComponent={null}
http={null}
includedGeoTypes={
Array [
"geo_point",
]
}
onChange={[Function]}
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="thresholdTimeField"
label={
<FormattedMessage
defaultMessage="Time field"
id="xpack.stackAlerts.geoThreshold.timeFieldLabel"
values={Object {}}
/>
}
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select time field"
value="testDateField"
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoField"
label="Geospatial field"
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select geo field"
value="testGeoField"
/>
</EuiFormRow>
</React.Fragment>
}
/>
`;
exports[`should render EntityIndexExpression w/ invalid flag if invalid 1`] = `
<ExpressionWithPopover
defaultValue="Select an index pattern and geo shape/point field"
expressionDescription="index"
isInvalid={true}
popoverContent={
<React.Fragment>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoIndexPatternSelect"
labelType="label"
>
<GeoIndexPatternSelect
IndexPatternSelectComponent={null}
http={null}
includedGeoTypes={
Array [
"geo_point",
]
}
onChange={[Function]}
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="thresholdTimeField"
label={
<FormattedMessage
defaultMessage="Time field"
id="xpack.stackAlerts.geoThreshold.timeFieldLabel"
values={Object {}}
/>
}
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select time field"
value="testDateField"
/>
</EuiFormRow>
<EuiFormRow
describedByIds={Array []}
display="row"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="geoField"
label="Geospatial field"
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select geo field"
value="testGeoField"
/>
</EuiFormRow>
</React.Fragment>
}
/>
`;

View file

@ -0,0 +1,94 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { shallow } from 'enzyme';
import { EntityIndexExpression } from './expressions/entity_index_expression';
import { BoundaryIndexExpression } from './expressions/boundary_index_expression';
import { ApplicationStart, DocLinksStart, HttpSetup, ToastsStart } from 'kibana/public';
import {
ActionTypeRegistryContract,
AlertTypeRegistryContract,
IErrorObject,
} from '../../../../../triggers_actions_ui/public';
import { IIndexPattern } from '../../../../../../../src/plugins/data/common';
const alertsContext = {
http: (null as unknown) as HttpSetup,
alertTypeRegistry: (null as unknown) as AlertTypeRegistryContract,
actionTypeRegistry: (null as unknown) as ActionTypeRegistryContract,
toastNotifications: (null as unknown) as ToastsStart,
docLinks: (null as unknown) as DocLinksStart,
capabilities: (null as unknown) as ApplicationStart['capabilities'],
};
const alertParams = {
index: '',
indexId: '',
geoField: '',
entity: '',
dateField: '',
trackingEvent: '',
boundaryType: '',
boundaryIndexTitle: '',
boundaryIndexId: '',
boundaryGeoField: '',
};
test('should render EntityIndexExpression', async () => {
const component = shallow(
<EntityIndexExpression
dateField={'testDateField'}
geoField={'testGeoField'}
alertsContext={alertsContext}
errors={{} as IErrorObject}
setAlertParamsDate={() => {}}
setAlertParamsGeoField={() => {}}
setAlertProperty={() => {}}
setIndexPattern={() => {}}
indexPattern={('' as unknown) as IIndexPattern}
isInvalid={false}
/>
);
expect(component).toMatchSnapshot();
});
test('should render EntityIndexExpression w/ invalid flag if invalid', async () => {
const component = shallow(
<EntityIndexExpression
dateField={'testDateField'}
geoField={'testGeoField'}
alertsContext={alertsContext}
errors={{} as IErrorObject}
setAlertParamsDate={() => {}}
setAlertParamsGeoField={() => {}}
setAlertProperty={() => {}}
setIndexPattern={() => {}}
indexPattern={('' as unknown) as IIndexPattern}
isInvalid={true}
/>
);
expect(component).toMatchSnapshot();
});
test('should render BoundaryIndexExpression', async () => {
const component = shallow(
<BoundaryIndexExpression
alertParams={alertParams}
alertsContext={alertsContext}
errors={{} as IErrorObject}
boundaryIndexPattern={('' as unknown) as IIndexPattern}
setBoundaryIndexPattern={() => {}}
setBoundaryGeoField={() => {}}
setBoundaryNameField={() => {}}
boundaryNameField={'testNameField'}
/>
);
expect(component).toMatchSnapshot();
});

View file

@ -30,6 +30,12 @@ import { EntityIndexExpression } from './expressions/entity_index_expression';
import { EntityByExpression } from './expressions/entity_by_expression';
import { BoundaryIndexExpression } from './expressions/boundary_index_expression';
import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns';
import {
esQuery,
esKuery,
Query,
QueryStringInput,
} from '../../../../../../../src/plugins/data/public';
const DEFAULT_VALUES = {
TRACKING_EVENT: '',
@ -67,6 +73,18 @@ const labelForDelayOffset = (
</>
);
function validateQuery(query: Query) {
try {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
query.language === 'kuery'
? esKuery.fromKueryExpression(query.query)
: esQuery.luceneStringToDsl(query.query);
} catch (err) {
return false;
}
return true;
}
export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeParamsExpressionProps<
GeoThresholdAlertParams,
AlertsContextValue
@ -74,6 +92,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
const {
index,
indexId,
indexQuery,
geoField,
entity,
dateField,
@ -81,6 +100,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
boundaryType,
boundaryIndexTitle,
boundaryIndexId,
boundaryIndexQuery,
boundaryGeoField,
boundaryNameField,
delayOffsetWithUnits,
@ -102,6 +122,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
}
};
const [indexQueryInput, setIndexQueryInput] = useState<Query>(
indexQuery || {
query: '',
language: 'kuery',
}
);
const [boundaryIndexPattern, _setBoundaryIndexPattern] = useState<IIndexPattern>({
id: '',
fields: [],
@ -118,6 +144,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
}
};
const [boundaryIndexQueryInput, setBoundaryIndexQueryInput] = useState<Query>(
boundaryIndexQuery || {
query: '',
language: 'kuery',
}
);
const [delayOffset, _setDelayOffset] = useState<number>(0);
function setDelayOffset(_delayOffset: number) {
setAlertParams('delayOffsetWithUnits', `${_delayOffset}${delayOffsetUnit}`);
@ -248,6 +280,23 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
indexFields={indexPattern.fields}
isInvalid={indexId && dateField && geoField ? !entity : false}
/>
<EuiSpacer size="s" />
<EuiFlexItem>
<QueryStringInput
disableAutoFocus
bubbleSubmitEvent
indexPatterns={indexPattern ? [indexPattern] : []}
query={indexQueryInput}
onChange={(query) => {
if (query.language) {
if (validateQuery(query)) {
setAlertParams('indexQuery', query);
}
setIndexQueryInput(query);
}
}}
/>
</EuiFlexItem>
<EuiSpacer size="l" />
<EuiTitle size="xs">
@ -313,6 +362,24 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
boundaryNameField={boundaryNameField}
/>
<EuiSpacer size="s" />
<EuiFlexItem>
<QueryStringInput
disableAutoFocus
bubbleSubmitEvent
indexPatterns={boundaryIndexPattern ? [boundaryIndexPattern] : []}
query={boundaryIndexQueryInput}
onChange={(query) => {
if (query.language) {
if (validateQuery(query)) {
setAlertParams('boundaryIndexQuery', query);
}
setBoundaryIndexQueryInput(query);
}
}}
/>
</EuiFlexItem>
<EuiSpacer size="l" />
</Fragment>
);
};

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Query } from '../../../../../../src/plugins/data/common';
export enum TrackingEvent {
entered = 'entered',
exited = 'exited',
@ -22,6 +24,8 @@ export interface GeoThresholdAlertParams {
boundaryGeoField: string;
boundaryNameField?: string;
delayOffsetWithUnits?: string;
indexQuery?: Query;
boundaryIndexQuery?: Query;
}
// Will eventually include 'geo_shape'

View file

@ -15,6 +15,7 @@ import {
ActionVariable,
AlertTypeState,
} from '../../../../alerts/server';
import { Query } from '../../../../../../src/plugins/data/common/query';
export const GEO_THRESHOLD_ID = '.geo-threshold';
export type TrackingEvent = 'entered' | 'exited';
@ -155,6 +156,8 @@ export const ParamsSchema = schema.object({
boundaryGeoField: schema.string({ minLength: 1 }),
boundaryNameField: schema.maybe(schema.string({ minLength: 1 })),
delayOffsetWithUnits: schema.maybe(schema.string({ minLength: 1 })),
indexQuery: schema.maybe(schema.any({})),
boundaryIndexQuery: schema.maybe(schema.any({})),
});
export interface GeoThresholdParams {
@ -170,6 +173,8 @@ export interface GeoThresholdParams {
boundaryGeoField: string;
boundaryNameField?: string;
delayOffsetWithUnits?: string;
indexQuery?: Query;
boundaryIndexQuery?: Query;
}
export function getAlertType(

View file

@ -7,6 +7,13 @@
import { ILegacyScopedClusterClient } from 'kibana/server';
import { SearchResponse } from 'elasticsearch';
import { Logger } from 'src/core/server';
import {
Query,
IIndexPattern,
fromKueryExpression,
toElasticsearchQuery,
luceneStringToDsl,
} from '../../../../../../src/plugins/data/common';
export const OTHER_CATEGORY = 'other';
// Consider dynamically obtaining from config?
@ -14,6 +21,19 @@ const MAX_TOP_LEVEL_QUERY_SIZE = 0;
const MAX_SHAPES_QUERY_SIZE = 10000;
const MAX_BUCKETS_LIMIT = 65535;
export const getEsFormattedQuery = (query: Query, indexPattern?: IIndexPattern) => {
let esFormattedQuery;
const queryLanguage = query.language;
if (queryLanguage === 'kuery') {
const ast = fromKueryExpression(query.query);
esFormattedQuery = toElasticsearchQuery(ast, indexPattern);
} else {
esFormattedQuery = luceneStringToDsl(query.query);
}
return esFormattedQuery;
};
export async function getShapesFilters(
boundaryIndexTitle: string,
boundaryGeoField: string,
@ -21,7 +41,8 @@ export async function getShapesFilters(
callCluster: ILegacyScopedClusterClient['callAsCurrentUser'],
log: Logger,
alertId: string,
boundaryNameField?: string
boundaryNameField?: string,
boundaryIndexQuery?: Query
) {
const filters: Record<string, unknown> = {};
const shapesIdsNamesMap: Record<string, unknown> = {};
@ -30,8 +51,10 @@ export async function getShapesFilters(
index: boundaryIndexTitle,
body: {
size: MAX_SHAPES_QUERY_SIZE,
...(boundaryIndexQuery ? { query: getEsFormattedQuery(boundaryIndexQuery) } : {}),
},
});
boundaryData.hits.hits.forEach(({ _index, _id }) => {
filters[_id] = {
geo_shape: {
@ -66,6 +89,7 @@ export async function executeEsQueryFactory(
boundaryGeoField,
geoField,
boundaryIndexTitle,
indexQuery,
}: {
entity: string;
index: string;
@ -74,6 +98,7 @@ export async function executeEsQueryFactory(
geoField: string;
boundaryIndexTitle: string;
boundaryNameField?: string;
indexQuery?: Query;
},
{ callCluster }: { callCluster: ILegacyScopedClusterClient['callAsCurrentUser'] },
log: Logger,
@ -83,6 +108,19 @@ export async function executeEsQueryFactory(
gteDateTime: Date | null,
ltDateTime: Date | null
): Promise<SearchResponse<unknown> | undefined> => {
let esFormattedQuery;
if (indexQuery) {
const gteEpochDateTime = gteDateTime ? new Date(gteDateTime).getTime() : null;
const ltEpochDateTime = ltDateTime ? new Date(ltDateTime).getTime() : null;
const dateRangeUpdatedQuery =
indexQuery.language === 'kuery'
? `(${dateField} >= "${gteEpochDateTime}" and ${dateField} < "${ltEpochDateTime}") and (${indexQuery.query})`
: `(${dateField}:[${gteDateTime} TO ${ltDateTime}]) AND (${indexQuery.query})`;
esFormattedQuery = getEsFormattedQuery({
query: dateRangeUpdatedQuery,
language: indexQuery.language,
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const esQuery: Record<string, any> = {
index,
@ -120,27 +158,29 @@ export async function executeEsQueryFactory(
},
},
},
query: {
bool: {
must: [],
filter: [
{
match_all: {},
},
{
range: {
[dateField]: {
...(gteDateTime ? { gte: gteDateTime } : {}),
lt: ltDateTime, // 'less than' to prevent overlap between intervals
format: 'strict_date_optional_time',
query: esFormattedQuery
? esFormattedQuery
: {
bool: {
must: [],
filter: [
{
match_all: {},
},
},
{
range: {
[dateField]: {
...(gteDateTime ? { gte: gteDateTime } : {}),
lt: ltDateTime, // 'less than' to prevent overlap between intervals
format: 'strict_date_optional_time',
},
},
},
],
should: [],
must_not: [],
},
],
should: [],
must_not: [],
},
},
},
stored_fields: ['*'],
docvalue_fields: [
{

View file

@ -194,7 +194,8 @@ export const getGeoThresholdExecutor = (log: Logger) =>
services.callCluster,
log,
alertId,
params.boundaryNameField
params.boundaryNameField,
params.boundaryIndexQuery
);
const executeEsQuery = await executeEsQueryFactory(params, services, log, shapesFilters);

View file

@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { getEsFormattedQuery } from '../es_query_builder';
describe('esFormattedQuery', () => {
it('lucene queries are converted correctly', async () => {
const testLuceneQuery1 = {
query: `"airport": "Denver"`,
language: 'lucene',
};
const esFormattedQuery1 = getEsFormattedQuery(testLuceneQuery1);
expect(esFormattedQuery1).toStrictEqual({ query_string: { query: '"airport": "Denver"' } });
const testLuceneQuery2 = {
query: `title:"Fun with turnips" AND text:Cabbage, cabbage and more cabbage!`,
language: 'lucene',
};
const esFormattedQuery2 = getEsFormattedQuery(testLuceneQuery2);
expect(esFormattedQuery2).toStrictEqual({
query_string: {
query: `title:"Fun with turnips" AND text:Cabbage, cabbage and more cabbage!`,
},
});
});
it('kuery queries are converted correctly', async () => {
const testKueryQuery1 = {
query: `"airport": "Denver"`,
language: 'kuery',
};
const esFormattedQuery1 = getEsFormattedQuery(testKueryQuery1);
expect(esFormattedQuery1).toStrictEqual({
bool: { minimum_should_match: 1, should: [{ match_phrase: { airport: 'Denver' } }] },
});
const testKueryQuery2 = {
query: `"airport": "Denver" and ("animal": "goat" or "animal": "narwhal")`,
language: 'kuery',
};
const esFormattedQuery2 = getEsFormattedQuery(testKueryQuery2);
expect(esFormattedQuery2).toStrictEqual({
bool: {
filter: [
{ bool: { should: [{ match_phrase: { airport: 'Denver' } }], minimum_should_match: 1 } },
{
bool: {
should: [
{
bool: { should: [{ match_phrase: { animal: 'goat' } }], minimum_should_match: 1 },
},
{
bool: {
should: [{ match_phrase: { animal: 'narwhal' } }],
minimum_should_match: 1,
},
},
],
minimum_should_match: 1,
},
},
],
},
});
});
});

View file

@ -4,8 +4,8 @@
"server": true,
"ui": true,
"optionalPlugins": ["alerts", "features", "home"],
"requiredPlugins": ["management", "charts", "data"],
"requiredPlugins": ["management", "charts", "data", "kibanaReact", "savedObjects"],
"configPath": ["xpack", "trigger_actions_ui"],
"extraPublicDirs": ["public/common", "public/common/constants"],
"requiredBundles": ["home", "alerts", "esUiShared"]
"requiredBundles": ["home", "alerts", "esUiShared", "kibanaReact", "kibanaUtils"]
}

View file

@ -15,6 +15,7 @@ import {
ChromeBreadcrumb,
CoreStart,
ScopedHistory,
SavedObjectsClientContract,
} from 'kibana/public';
import { KibanaFeature } from '../../../features/common';
import { Section, routeToAlertDetails } from './constants';
@ -24,6 +25,7 @@ import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { PluginStartContract as AlertingStart } from '../../../alerts/public';
import { suspendedComponentWithProps } from './lib/suspended_component_with_props';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
const TriggersActionsUIHome = lazy(async () => import('./home'));
const AlertDetailsRoute = lazy(
@ -31,13 +33,14 @@ const AlertDetailsRoute = lazy(
);
export interface AppDeps {
dataPlugin: DataPublicPluginStart;
data: DataPublicPluginStart;
charts: ChartsPluginStart;
chrome: ChromeStart;
alerts?: AlertingStart;
navigateToApp: CoreStart['application']['navigateToApp'];
docLinks: DocLinksStart;
toastNotifications: ToastsSetup;
storage?: Storage;
http: HttpSetup;
uiSettings: IUiSettingsClient;
setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void;
@ -45,6 +48,9 @@ export interface AppDeps {
actionTypeRegistry: ActionTypeRegistryContract;
alertTypeRegistry: AlertTypeRegistryContract;
history: ScopedHistory;
savedObjects?: {
client: SavedObjectsClientContract;
};
kibanaFeatures: KibanaFeature[];
}

View file

@ -5,6 +5,7 @@
*/
import React, { createContext, useContext } from 'react';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
import { AppDeps } from './app';
const AppContext = createContext<AppDeps | null>(null);
@ -16,7 +17,11 @@ export const AppContextProvider = ({
appDeps: AppDeps | null;
children: React.ReactNode;
}) => {
return appDeps ? <AppContext.Provider value={appDeps}>{children}</AppContext.Provider> : null;
return appDeps ? (
<KibanaContextProvider services={appDeps}>
<AppContext.Provider value={appDeps}>{children}</AppContext.Provider>
</KibanaContextProvider>
) : null;
};
export const useAppDependencies = (): AppDeps => {

View file

@ -6,21 +6,20 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { SavedObjectsClientContract } from 'src/core/public';
import { App, AppDeps } from './app';
import { setSavedObjectsClient } from '../common/lib/data_apis';
interface BootDeps extends AppDeps {
element: HTMLElement;
savedObjects: SavedObjectsClientContract;
I18nContext: any;
}
export const boot = (bootDeps: BootDeps) => {
const { I18nContext, element, savedObjects, ...appDeps } = bootDeps;
const { I18nContext, element, ...appDeps } = bootDeps;
setSavedObjectsClient(savedObjects);
if (appDeps.savedObjects) {
setSavedObjectsClient(appDeps.savedObjects.client);
}
render(
<I18nContext>

View file

@ -55,7 +55,7 @@ describe('actions_connectors_list component empty', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -165,7 +165,7 @@ describe('actions_connectors_list component with items', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -256,7 +256,7 @@ describe('actions_connectors_list component empty with show only capability', ()
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -348,7 +348,7 @@ describe('actions_connectors_list with show only capability', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -452,7 +452,7 @@ describe('actions_connectors_list component with disabled items', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
injectedMetadata: mockes.injectedMetadata,

View file

@ -42,7 +42,7 @@ jest.mock('../../../app_context', () => ({
toastNotifications: mockes.notifications.toasts,
docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' },
uiSettings: mockes.uiSettings,
dataPlugin: jest.fn(),
data: jest.fn(),
charts: jest.fn(),
})),
}));

View file

@ -70,7 +70,7 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
uiSettings,
docLinks,
charts,
dataPlugin,
data,
setBreadcrumbs,
chrome,
} = useAppDependencies();
@ -162,11 +162,11 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
uiSettings,
docLinks,
charts,
dataFieldsFormats: dataPlugin.fieldFormats,
dataFieldsFormats: data.fieldFormats,
reloadAlerts: setAlert,
capabilities,
dataUi: dataPlugin.ui,
dataIndexPatterns: dataPlugin.indexPatterns,
dataUi: data.ui,
dataIndexPatterns: data.indexPatterns,
}}
>
<AlertEdit

View file

@ -82,7 +82,7 @@ describe('alert_add', () => {
toastNotifications: mocks.notifications.toasts,
http: mocks.http,
uiSettings: mocks.uiSettings,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
actionTypeRegistry,
alertTypeRegistry,

View file

@ -108,7 +108,7 @@ describe('alerts_list component empty', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -279,7 +279,7 @@ describe('alerts_list component with items', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -362,7 +362,7 @@ describe('alerts_list component empty with show only capability', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,
@ -483,7 +483,7 @@ describe('alerts_list with show only capability', () => {
const deps = {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
toastNotifications: mockes.notifications.toasts,

View file

@ -83,7 +83,7 @@ export const AlertsList: React.FunctionComponent = () => {
uiSettings,
docLinks,
charts,
dataPlugin,
data,
kibanaFeatures,
} = useAppDependencies();
const canExecuteActions = hasExecuteActionsCapability(capabilities);
@ -668,10 +668,10 @@ export const AlertsList: React.FunctionComponent = () => {
uiSettings,
docLinks,
charts,
dataFieldsFormats: dataPlugin.fieldFormats,
dataFieldsFormats: data.fieldFormats,
capabilities,
dataUi: dataPlugin.ui,
dataIndexPatterns: dataPlugin.indexPatterns,
dataUi: data.ui,
dataIndexPatterns: data.indexPatterns,
kibanaFeatures,
}}
>

View file

@ -26,20 +26,20 @@ export async function getMockedAppDependencies() {
const kibanaFeatures = await featuresPluginMock.createStart().getFeatures();
return {
chrome,
docLinks,
dataPlugin: dataPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
charts: chartPluginMock.createStartContract(),
alerting: alertingPluginMock.createStartContract(),
chrome,
navigateToApp,
docLinks,
toastNotifications: coreSetupMock.notifications.toasts,
http: coreSetupMock.http,
uiSettings: coreSetupMock.uiSettings,
navigateToApp,
capabilities,
history: scopedHistoryMock.create(),
setBreadcrumbs: jest.fn(),
capabilities,
actionTypeRegistry,
alertTypeRegistry,
history: scopedHistoryMock.create(),
alerting: alertingPluginMock.createStartContract(),
kibanaFeatures,
};
}

View file

@ -17,6 +17,7 @@ export {
AlertTypeModel,
ActionType,
ActionTypeRegistryContract,
AlertTypeRegistryContract,
AlertTypeParamsExpressionProps,
ValidationResult,
ActionVariable,

View file

@ -22,6 +22,7 @@ import {
import { ChartsPluginStart } from '../../../../src/plugins/charts/public';
import { PluginStartContract as AlertingStart } from '../../alerts/public';
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
export interface TriggersAndActionsUIPublicPluginSetup {
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
@ -102,16 +103,17 @@ export class Plugin
const { boot } = await import('./application/boot');
const kibanaFeatures = await pluginsStart.features.getFeatures();
return boot({
dataPlugin: pluginsStart.data,
data: pluginsStart.data,
charts: pluginsStart.charts,
alerts: pluginsStart.alerts,
element: params.element,
toastNotifications: coreStart.notifications.toasts,
storage: new Storage(window.localStorage),
http: coreStart.http,
uiSettings: coreStart.uiSettings,
docLinks: coreStart.docLinks,
chrome: coreStart.chrome,
savedObjects: coreStart.savedObjects.client,
savedObjects: coreStart.savedObjects,
I18nContext: coreStart.i18n.Context,
capabilities: coreStart.application.capabilities,
navigateToApp: coreStart.application.navigateToApp,