Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 32 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1b12a98
Add index search query bar
Oct 13, 2020
59339c4
Add second query bar and update services used to be compatible with q…
Oct 16, 2020
3f2ad7d
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Oct 16, 2020
fa4f0ef
Verify syntax and send to server. Add handling for shapes only logic …
Oct 19, 2020
2349822
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Oct 20, 2020
cb7d3b8
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 2, 2020
6680db0
Construct es queries filters server side with interval dates
Nov 2, 2020
ed259c6
Fix how query bars repopulate for alert edit
Nov 3, 2020
8f9fce2
Add parenthesis to query conditions to structure conditions correctly
Nov 3, 2020
29f1b30
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 3, 2020
4f692c4
Type fixes and clean up
Nov 4, 2020
5f5df0c
More type fixes and safety checks on the server side
Nov 4, 2020
56d646d
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 4, 2020
8e86d59
Type fix
Nov 4, 2020
791e57f
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 11, 2020
a740033
Add tests for esFormattedQuery
Nov 11, 2020
2499530
Review feedback. Remove 'temp' in names, add 'Input'
Nov 11, 2020
120c0c9
Add snapshot tests of index components
Nov 12, 2020
faf41dd
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 12, 2020
0b6fc7a
Type updates
Nov 12, 2020
fb27dc5
Snapshot updates
Nov 12, 2020
e52ed1f
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 12, 2020
ff8aaef
Update snapshot
Nov 12, 2020
56132d4
Update kibana.json to cover deps
Nov 13, 2020
870c16c
Add in kibana deps missed by merge
Nov 14, 2020
8475bf0
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 16, 2020
818dade
Update deps missed by merge
Nov 16, 2020
a4586e0
Type updates
Nov 16, 2020
984d802
Merge branch 'master' into add-query-inputs-to-geo-tracker
kibanamachine Nov 16, 2020
af9e073
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 18, 2020
733c080
Merge remote-tracking branch 'upstream/master' into add-query-inputs-…
Nov 18, 2020
c07d6f4
Add back optional plugin and tweak required plugins to undo merge cha…
Nov 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand All @@ -170,6 +173,8 @@ export interface GeoThresholdParams {
boundaryGeoField: string;
boundaryNameField?: string;
delayOffsetWithUnits?: string;
indexQuery?: Query;
boundaryIndexQuery?: Query;
}

export function getAlertType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,42 @@
import { ILegacyScopedClusterClient } from 'kibana/server';
import { SearchResponse } from 'elasticsearch';
import { Logger } from '../../types';
import {
Query,
IIndexPattern,
fromKueryExpression,
toElasticsearchQuery,
luceneStringToDsl,
} from '../../../../../../src/plugins/data/common';

export const OTHER_CATEGORY = 'other';
// Consider dynamically obtaining from config?
const MAX_TOP_LEVEL_QUERY_SIZE = 0;
const MAX_SHAPES_QUERY_SIZE = 10000;
const MAX_BUCKETS_LIMIT = 65535;

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,
geoField: string,
callCluster: ILegacyScopedClusterClient['callAsCurrentUser'],
log: Logger,
alertId: string,
boundaryNameField?: string
boundaryNameField?: string,
boundaryIndexQuery?: Query
) {
const filters: Record<string, unknown> = {};
const shapesIdsNamesMap: Record<string, unknown> = {};
Expand All @@ -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: {
Expand Down Expand Up @@ -66,6 +89,7 @@ export async function executeEsQueryFactory(
boundaryGeoField,
geoField,
boundaryIndexTitle,
indexQuery,
}: {
entity: string;
index: string;
Expand All @@ -74,6 +98,7 @@ export async function executeEsQueryFactory(
geoField: string;
boundaryIndexTitle: string;
boundaryNameField?: string;
indexQuery?: Query;
},
{ callCluster }: { callCluster: ILegacyScopedClusterClient['callAsCurrentUser'] },
log: Logger,
Expand All @@ -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,
Expand Down Expand Up @@ -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: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ export const getGeoThresholdExecutor = ({ logger: log }: { logger: Logger }) =>
services.callCluster,
log,
alertId,
params.boundaryNameField
params.boundaryNameField,
params.boundaryIndexQuery
);

const executeEsQuery = await executeEsQueryFactory(params, services, log, shapesFilters);
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/triggers_actions_ui/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"server": true,
"ui": true,
"optionalPlugins": ["alerts", "stackAlerts", "features", "home"],
"requiredPlugins": ["management", "charts", "data", "kibanaReact"],
"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"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ChromeBreadcrumb,
CoreStart,
ScopedHistory,
SavedObjectsClientContract,
} from 'kibana/public';
import { Section, routeToAlertDetails } from './constants';
import { KibanaFeature } from '../../../features/common';
Expand All @@ -24,27 +25,32 @@ 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(
() => import('./sections/alert_details/components/alert_details_route')
);

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;
capabilities: ApplicationStart['capabilities'];
actionTypeRegistry: ActionTypeRegistryContract;
alertTypeRegistry: AlertTypeRegistryContract;
history: ScopedHistory;
savedObjects?: {
client: SavedObjectsClientContract;
};
kibanaFeatures: KibanaFeature[];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/index_threshold_api';

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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import { EntityByExpression } from './expressions/entity_by_expression';
import { BoundaryIndexExpression } from './expressions/boundary_index_expression';
import { IIndexPattern } from '../../../../../../../../../src/plugins/data/common/index_patterns';
import { getTimeOptions } from '../../../../../common/lib/get_time_options';
import {
esQuery,
Query,
QueryStringInput,
} from '../../../../../../../../../src/plugins/data/public';
import { esKuery } from '../../../../../../../../../src/plugins/data/public';

const DEFAULT_VALUES = {
TRACKING_EVENT: '',
Expand Down Expand Up @@ -65,20 +71,34 @@ 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
>> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => {
const {
index,
indexId,
indexQuery,
geoField,
entity,
dateField,
trackingEvent,
boundaryType,
boundaryIndexTitle,
boundaryIndexId,
boundaryIndexQuery,
boundaryGeoField,
boundaryNameField,
delayOffsetWithUnits,
Expand All @@ -100,6 +120,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
}
};
const [tempIndexQuery, setTempIndexQuery] = useState<Query>(
kindsun marked this conversation as resolved.
Show resolved Hide resolved
indexQuery || {
query: '',
language: 'kuery',
}
);
const [boundaryIndexPattern, _setBoundaryIndexPattern] = useState<IIndexPattern>({
id: '',
fields: [],
Expand All @@ -116,6 +142,12 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
}
};
const [tempBoundaryIndexQuery, setTempBoundaryIndexQuery] = useState<Query>(
boundaryIndexQuery || {
query: '',
language: 'kuery',
}
);
const [delayOffset, _setDelayOffset] = useState<number>(0);
function setDelayOffset(_delayOffset: number) {
setAlertParams('delayOffsetWithUnits', `${_delayOffset}${delayOffsetUnit}`);
Expand Down Expand Up @@ -246,6 +278,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={tempIndexQuery}
onChange={(query) => {
if (query.language) {
if (validateQuery(query)) {
setAlertParams('indexQuery', query);
}
setTempIndexQuery(query);
}
}}
/>
</EuiFlexItem>

<EuiSpacer size="l" />
<EuiTitle size="xs">
Expand Down Expand Up @@ -314,6 +363,24 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP
}
boundaryNameField={boundaryNameField}
/>
<EuiSpacer size="s" />
<EuiFlexItem>
<QueryStringInput
disableAutoFocus
bubbleSubmitEvent
indexPatterns={boundaryIndexPattern ? [boundaryIndexPattern] : []}
query={tempBoundaryIndexQuery}
onChange={(query) => {
if (query.language) {
if (validateQuery(query)) {
setAlertParams('boundaryIndexQuery', query);
}
setTempBoundaryIndexQuery(query);
}
}}
/>
</EuiFlexItem>
<EuiSpacer size="l" />
</Fragment>
);
};
Expand Down
Loading