From 5bfd704dd8a508fc909f5e868fa39fbc0a30d885 Mon Sep 17 00:00:00 2001 From: Tre Date: Fri, 5 Jun 2020 10:49:06 -0600 Subject: [PATCH 01/27] [QA] slack notify on failure (#68126) --- .ci/Jenkinsfile_coverage | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage index 3f4732f15f334..650ef94e1d3da 100644 --- a/.ci/Jenkinsfile_coverage +++ b/.ci/Jenkinsfile_coverage @@ -11,8 +11,11 @@ kibanaPipeline(timeoutMinutes: 240) { 'CODE_COVERAGE=1', // Enables coverage. Needed for multiple ci scripts, such as remote.ts, test/scripts/*.sh, schema.js, etc. ]) { workers.base(name: 'coverage-worker', size: 'l', ramDisk: false, bootstrapped: false) { - kibanaCoverage.runTests() - handleIngestion(TIME_STAMP) + catchError { + kibanaCoverage.runTests() + handleIngestion(TIME_STAMP) + } + handleFail() } } kibanaPipeline.sendMail() @@ -29,4 +32,13 @@ def handleIngestion(timestamp) { kibanaCoverage.uploadCoverageStaticSite(timestamp) } +def handleFail() { + def buildStatus = buildUtils.getBuildStatus() + if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') { + slackNotifications.sendFailedBuild( + channel: '#kibana-qa', + username: 'Kibana QA' + ) + } +} From 66138a8e7195bf65b887c475dee28cf53cff9c02 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Fri, 5 Jun 2020 11:09:45 -0700 Subject: [PATCH 02/27] [DOCS] Adds link from remote clusters to index patterns (#68406) --- docs/management/managing-remote-clusters.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc index 00ec5c7d2ddea..51d9f42a0b83e 100644 --- a/docs/management/managing-remote-clusters.asciidoc +++ b/docs/management/managing-remote-clusters.asciidoc @@ -31,6 +31,10 @@ to reproduce indices in the remote cluster on a local cluster. [role="screenshot"] image::images/add_remote_cluster.png[][UI for adding a remote cluster] +To create an index pattern to search across clusters, +use the same syntax that you’d use in a raw cross-cluster search request in {es}: :. +See <> for examples. + [float] [[manage-remote-clusters]] === Manage remote clusters From eaca7ee008b9d46285da19e4fd755d0037fb3d33 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Fri, 5 Jun 2020 11:10:52 -0700 Subject: [PATCH 03/27] [DOCS] Adds note about configuring File Data Visualizer (#68407) --- docs/setup/connect-to-elasticsearch.asciidoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index 216195e05154b..6137e028db3fd 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -21,7 +21,10 @@ to see all that you can do in {kib}. To visualize data in a CSV, JSON, or log file, you can upload it using the File Data Visualizer. On the home page, click *Import a CSV, NDSON, or log file*, and then drag your file into the -File Data Visualizer. You can upload a file up to 100 MB. +File Data Visualizer. + +You can upload a file up to 100 MB. This value is configurable up to 1 GB in +<>. [role="screenshot"] image::images/add-data-fv.png[File Data Visualizer] From e3d88a4f09dc83327b633f0899a129444aa62740 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 5 Jun 2020 15:29:16 -0400 Subject: [PATCH 04/27] [SECURITY SOLEIL] Fix selection of event type when no siem index signal created (#68291) * fix selection of event type when no siem index signal created * including the term signal for the old timeline * fix import path * Add a specific msg in the inspect modal if we do not have the alert index created * fix import if eventType is siganl to match it to alert * forget to update test * fix signal view Co-authored-by: Elastic Machine --- .../security_solution/common/constants.ts | 1 + .../common/components/inspect/modal.test.tsx | 30 +++- .../common/components/inspect/modal.tsx | 12 +- .../common/components/inspect/translations.ts | 7 + .../components/timeline/index.test.tsx | 133 ++++++++++++++++++ .../timelines/components/timeline/index.tsx | 7 +- .../timeline/search_or_filter/pick_events.tsx | 2 +- .../public/timelines/containers/index.tsx | 2 +- .../public/timelines/store/timeline/model.ts | 2 +- 9 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 482794804685d..d04d1f2c91b97 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -31,6 +31,7 @@ export const DEFAULT_INTERVAL_PAUSE = true; export const DEFAULT_INTERVAL_TYPE = 'manual'; export const DEFAULT_INTERVAL_VALUE = 300000; // ms export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges'; +export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51'; /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/modal.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/modal.test.tsx index 153a1703059c2..3451ddacb6538 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/modal.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/modal.test.tsx @@ -9,7 +9,8 @@ import { mount } from 'enzyme'; import React from 'react'; import { ThemeProvider } from 'styled-components'; -import { ModalInspectQuery } from './modal'; +import { NO_ALERT_INDEX } from '../../../../common/constants'; +import { ModalInspectQuery, formatIndexPatternRequested } from './modal'; const request = '{"index": ["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"allowNoIndices": true, "ignoreUnavailable": true, "body": { "aggregations": {"hosts": {"cardinality": {"field": "host.name" } }, "hosts_histogram": {"auto_date_histogram": {"field": "@timestamp","buckets": "6"},"aggs": { "count": {"cardinality": {"field": "host.name" }}}}}, "query": {"bool": {"filter": [{"range": { "@timestamp": {"gte": 1562290224506,"lte": 1562376624506 }}}]}}, "size": 0, "track_total_hits": false}}'; @@ -244,4 +245,31 @@ describe('Modal Inspect', () => { expect(closeModal).toHaveBeenCalled(); }); }); + + describe('formatIndexPatternRequested', () => { + test('Return specific messages to NO_ALERT_INDEX if we only have one index and we match the index name `NO_ALERT_INDEX`', () => { + const expected = formatIndexPatternRequested([NO_ALERT_INDEX]); + expect(expected).toEqual({'No alert index found'}); + }); + + test('Ignore NO_ALERT_INDEX if you have more than one indices', () => { + const expected = formatIndexPatternRequested([NO_ALERT_INDEX, 'indice-1']); + expect(expected).toEqual('indice-1'); + }); + + test('Happy path', () => { + const expected = formatIndexPatternRequested(['indice-1, indice-2']); + expect(expected).toEqual('indice-1, indice-2'); + }); + + test('Empty array with no indices', () => { + const expected = formatIndexPatternRequested([]); + expect(expected).toEqual('Sorry about that, something went wrong.'); + }); + + test('Undefined indices', () => { + const expected = formatIndexPatternRequested(undefined); + expect(expected).toEqual('Sorry about that, something went wrong.'); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/modal.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/modal.tsx index 1563c005af5b6..e9f7edf86d4ba 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/modal.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/modal.tsx @@ -22,6 +22,7 @@ import numeral from '@elastic/numeral'; import React, { ReactNode } from 'react'; import styled from 'styled-components'; +import { NO_ALERT_INDEX } from '../../../../common/constants'; import * as i18n from './translations'; const DescriptionListStyled = styled(EuiDescriptionList)` @@ -88,6 +89,15 @@ const manageStringify = (object: Record | Response): string => } }; +export const formatIndexPatternRequested = (indices: string[] = []) => { + if (indices.length === 1 && indices[0] === NO_ALERT_INDEX) { + return {i18n.NO_ALERT_INDEX_FOUND}; + } + return indices.length > 0 + ? indices.filter((i) => i !== NO_ALERT_INDEX).join(', ') + : i18n.SOMETHING_WENT_WRONG; +}; + export const ModalInspectQuery = ({ closeModal, isShowing = false, @@ -113,7 +123,7 @@ export const ModalInspectQuery = ({ ), description: ( - {inspectRequest != null ? inspectRequest.index.join(', ') : i18n.SOMETHING_WENT_WRONG} + {formatIndexPatternRequested(inspectRequest?.index ?? [])} ), }, diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/translations.ts b/x-pack/plugins/security_solution/public/common/components/inspect/translations.ts index c51423087911f..4a8da8050dd9a 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/inspect/translations.ts @@ -60,3 +60,10 @@ export const REQUEST_TIMESTAMP_DESC = i18n.translate( defaultMessage: 'Time when the start of the request has been logged', } ); + +export const NO_ALERT_INDEX_FOUND = i18n.translate( + 'xpack.securitySolution.inspect.modal.noAlertIndexFound', + { + defaultMessage: 'No alert index found', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx new file mode 100644 index 0000000000000..581fa125d21e2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -0,0 +1,133 @@ +/* + * 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 { mount } from 'enzyme'; +import React from 'react'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { act } from 'react-dom/test-utils'; +import useResizeObserver from 'use-resize-observer/polyfilled'; + +import { + useSignalIndex, + ReturnSignalIndex, +} from '../../../alerts/containers/detection_engine/alerts/use_signal_index'; +import { mocksSource } from '../../../common/containers/source/mock'; +import { wait } from '../../../common/lib/helpers'; +import { defaultHeaders, mockTimelineData, TestProviders } from '../../../common/mock'; +import { Direction } from '../../../graphql/types'; +import { timelineQuery } from '../../containers/index.gql_query'; +import { timelineActions } from '../../store/timeline'; + +import { Sort } from './body/sort'; +import { mockDataProviders } from './data_providers/mock/mock_data_providers'; +import { StatefulTimeline, Props as StatefulTimelineProps } from './index'; +import { Timeline } from './timeline'; + +jest.mock('../../../common/lib/kibana'); + +const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; +jest.mock('use-resize-observer/polyfilled'); +mockUseResizeObserver.mockImplementation(() => ({})); + +const mockUseSignalIndex: jest.Mock = useSignalIndex as jest.Mock; +jest.mock('../../../alerts/containers/detection_engine/alerts/use_signal_index'); + +describe('StatefulTimeline', () => { + let props = {} as StatefulTimelineProps; + const sort: Sort = { + columnId: '@timestamp', + sortDirection: Direction.desc, + }; + const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf(); + const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf(); + + const mocks = [ + { request: { query: timelineQuery }, result: { data: { events: mockTimelineData } } }, + ...mocksSource, + ]; + + beforeEach(() => { + props = { + addProvider: timelineActions.addProvider, + columns: defaultHeaders, + createTimeline: timelineActions.createTimeline, + dataProviders: mockDataProviders, + eventType: 'raw', + end: endDate, + filters: [], + id: 'foo', + isLive: false, + itemsPerPage: 5, + itemsPerPageOptions: [5, 10, 20], + kqlMode: 'search', + kqlQueryExpression: '', + onClose: jest.fn(), + onDataProviderEdited: timelineActions.dataProviderEdited, + removeColumn: timelineActions.removeColumn, + removeProvider: timelineActions.removeProvider, + show: true, + showCallOutUnauthorizedMsg: false, + sort, + start: startDate, + updateColumns: timelineActions.updateColumns, + updateDataProviderEnabled: timelineActions.updateDataProviderEnabled, + updateDataProviderExcluded: timelineActions.updateDataProviderExcluded, + updateDataProviderKqlQuery: timelineActions.updateDataProviderKqlQuery, + updateHighlightedDropAndProviderId: timelineActions.updateHighlightedDropAndProviderId, + updateItemsPerPage: timelineActions.updateItemsPerPage, + updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions, + updateSort: timelineActions.updateSort, + upsertColumn: timelineActions.upsertColumn, + usersViewing: ['elastic'], + }; + }); + + describe('indexToAdd', () => { + test('Make sure that indexToAdd return an unknown index if signalIndex does not exist', async () => { + mockUseSignalIndex.mockImplementation(() => ({ + loading: false, + signalIndexExists: false, + signalIndexName: undefined, + })); + const wrapper = mount( + + + + + + ); + await act(async () => { + await wait(); + wrapper.update(); + const timeline = wrapper.find(Timeline); + expect(timeline.props().indexToAdd).toEqual([ + 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51', + ]); + }); + }); + + test('Make sure that indexToAdd return siem signal index if signalIndex exist', async () => { + mockUseSignalIndex.mockImplementation(() => ({ + loading: false, + signalIndexExists: true, + signalIndexName: 'mock-siem-signals-index', + })); + const wrapper = mount( + + + + + + ); + await act(async () => { + await wait(); + wrapper.update(); + const timeline = wrapper.find(Timeline); + expect(timeline.props().indexToAdd).toEqual(['mock-siem-signals-index']); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index c52be64f94bf1..42fd6422d3a38 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -8,6 +8,7 @@ import React, { useEffect, useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; +import { NO_ALERT_INDEX } from '../../../../common/constants'; import { WithSource } from '../../../common/containers/source'; import { useSignalIndex } from '../../../alerts/containers/detection_engine/alerts/use_signal_index'; import { inputsModel, inputsSelectors, State } from '../../../common/store'; @@ -30,7 +31,7 @@ export interface OwnProps { usersViewing: string[]; } -type Props = OwnProps & PropsFromRedux; +export type Props = OwnProps & PropsFromRedux; const StatefulTimelineComponent = React.memo( ({ @@ -67,11 +68,11 @@ const StatefulTimelineComponent = React.memo( eventType && signalIndexExists && signalIndexName != null && - ['signal', 'all'].includes(eventType) + ['signal', 'alert', 'all'].includes(eventType) ) { return [signalIndexName]; } - return []; + return [NO_ALERT_INDEX]; // Following index does not exist so we won't show any events; }, [eventType, signalIndexExists, signalIndexName]); const onDataProviderRemoved: OnDataProviderRemoved = useCallback( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx index 5a3805af0ca43..b0682290ee849 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx @@ -79,7 +79,7 @@ const PickEventTypeComponents: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 5efcb84539123..7363a60974275 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -92,7 +92,7 @@ class TimelineQueryComponent extends QueryTemplate< indexPattern == null || (indexPattern != null && indexPattern.title === '') ? [ ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []), - ...(['all', 'signal'].includes(eventType) ? indexToAdd : []), + ...(['all', 'alert', 'signal'].includes(eventType) ? indexToAdd : []), ] : indexPattern?.title.split(',') ?? []; const variables: GetTimelineQuery.Variables = { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index f7e848e8a9e1b..caad70226365a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -18,7 +18,7 @@ import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/t export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; -export type EventType = 'all' | 'raw' | 'alert'; +export type EventType = 'all' | 'raw' | 'alert' | 'signal'; export type ColumnHeaderType = 'not-filtered' | 'text-filter'; From 1f99144ef686c6bbf7ffd833a68c263f5d6704ab Mon Sep 17 00:00:00 2001 From: Dmitry Lemeshko Date: Fri, 5 Jun 2020 23:28:49 +0200 Subject: [PATCH 05/27] update script to always download node (#68421) Co-authored-by: Elastic Machine --- vars/kibanaCoverage.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/kibanaCoverage.groovy b/vars/kibanaCoverage.groovy index 0305f86475a9a..66b16566418b5 100644 --- a/vars/kibanaCoverage.groovy +++ b/vars/kibanaCoverage.groovy @@ -98,7 +98,7 @@ def collectVcsInfo(title) { def generateReports(title) { kibanaPipeline.bash(""" - source src/dev/ci_setup/setup_env.sh + source src/dev/ci_setup/setup_env.sh true # bootstrap from x-pack folder cd x-pack yarn kbn bootstrap --prefer-offline From 77c8aee3cd2de3bdbde8970b8421731539fe3585 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Fri, 5 Jun 2020 17:33:38 -0400 Subject: [PATCH 06/27] [Maps] Fix mb-style interpolate style rule (#68413) --- .../vector/properties/__tests__/test_util.ts | 15 ++ .../properties/dynamic_size_property.js | 8 +- .../properties/dynamic_size_property.test.tsx | 143 ++++++++++-------- 3 files changed, 100 insertions(+), 66 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts index a8fba834d65ab..f4ef9bdbe5b6d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts @@ -25,6 +25,21 @@ class MockField extends AbstractField { } } +export class MockMbMap { + _paintPropertyCalls: unknown[]; + + constructor() { + this._paintPropertyCalls = []; + } + setPaintProperty(...args: unknown[]) { + this._paintPropertyCalls.push([...args]); + } + + getPaintPropertyCalls(): unknown[] { + return this._paintPropertyCalls; + } +} + export const mockField: IField = new MockField({ fieldName: 'foobar', origin: FIELD_ORIGIN.SOURCE, diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.js index 898da439c44aa..a0af2fbb939d8 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.js @@ -110,6 +110,9 @@ export class DynamicSizeProperty extends DynamicStyleProperty { _getMbDataDrivenSize({ targetName, minSize, maxSize, minValue, maxValue }) { const lookup = this.supportsMbFeatureState() ? 'feature-state' : 'get'; + + const stops = + minValue === maxValue ? [maxValue, maxSize] : [minValue, minSize, maxValue, maxSize]; return [ 'interpolate', ['linear'], @@ -120,10 +123,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty { fieldName: targetName, fallback: 0, }), - minValue, - minSize, - maxValue, - maxSize, + ...stops, ]; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx index 34f3e796f409f..c60547f3606c5 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IVectorStyle } from '../vector_style'; - jest.mock('ui/new_platform'); jest.mock('../components/vector_style_editor', () => ({ VectorStyleEditor: () => { @@ -18,68 +16,22 @@ import { shallow } from 'enzyme'; // @ts-ignore import { DynamicSizeProperty } from './dynamic_size_property'; -import { StyleMeta } from '../style_meta'; -import { FIELD_ORIGIN, VECTOR_STYLES } from '../../../../../common/constants'; -import { DataRequest } from '../../../util/data_request'; -import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; +import { VECTOR_STYLES } from '../../../../../common/constants'; import { IField } from '../../../fields/field'; +import { MockMbMap } from './__tests__/test_util'; -// @ts-ignore -const mockField: IField = { - async getLabel() { - return 'foobar_label'; - }, - getName() { - return 'foobar'; - }, - getRootName() { - return 'foobar'; - }, - getOrigin() { - return FIELD_ORIGIN.SOURCE; - }, - supportsFieldMeta() { - return true; - }, - canValueBeFormatted() { - return true; - }, - async getDataType() { - return 'number'; - }, -}; +import { mockField, MockLayer, MockStyle } from './__tests__/test_util'; -// @ts-ignore -const mockLayer: IVectorLayer = { - getDataRequest(): DataRequest | undefined { - return undefined; - }, - getStyle(): IVectorStyle { - // @ts-ignore - return { - getStyleMeta(): StyleMeta { - return new StyleMeta({ - geometryTypes: { - isPointsOnly: true, - isLinesOnly: false, - isPolygonsOnly: false, - }, - fieldMeta: { - foobar: { - range: { min: 0, max: 100, delta: 100 }, - categories: { categories: [] }, - }, - }, - }); - }, - }; - }, -}; - -const makeProperty: DynamicSizeProperty = (options: object) => { - return new DynamicSizeProperty(options, VECTOR_STYLES.ICON_SIZE, mockField, mockLayer, () => { - return (x: string) => x + '_format'; - }); +const makeProperty = (options: object, mockStyle: MockStyle, field: IField = mockField) => { + return new DynamicSizeProperty( + options, + VECTOR_STYLES.ICON_SIZE, + field, + new MockLayer(mockStyle), + () => { + return (x: string) => x + '_format'; + } + ); }; const defaultLegendParams = { @@ -89,7 +41,7 @@ const defaultLegendParams = { describe('renderLegendDetailRow', () => { test('Should render as range', async () => { - const sizeProp = makeProperty(); + const sizeProp = makeProperty({}, new MockStyle({ min: 0, max: 100 })); const legendRow = sizeProp.renderLegendDetailRow(defaultLegendParams); const component = shallow(legendRow); @@ -100,3 +52,70 @@ describe('renderLegendDetailRow', () => { expect(component).toMatchSnapshot(); }); }); + +describe('syncSize', () => { + test('Should sync with circle-radius prop', async () => { + const sizeProp = makeProperty({ minSize: 8, maxSize: 32 }, new MockStyle({ min: 0, max: 100 })); + const mockMbMap = new MockMbMap(); + + sizeProp.syncCircleRadiusWithMb('foobar', mockMbMap); + + expect(mockMbMap.getPaintPropertyCalls()).toEqual([ + [ + 'foobar', + 'circle-radius', + [ + 'interpolate', + ['linear'], + [ + 'coalesce', + [ + 'case', + ['==', ['feature-state', 'foobar'], null], + -1, + ['max', ['min', ['to-number', ['feature-state', 'foobar']], 100], 0], + ], + 0, + ], + 0, + 8, + 100, + 32, + ], + ], + ]); + }); + + test('Should truncate interpolate expression to max when no delta', async () => { + const sizeProp = makeProperty( + { minSize: 8, maxSize: 32 }, + new MockStyle({ min: 100, max: 100 }) + ); + const mockMbMap = new MockMbMap(); + + sizeProp.syncCircleRadiusWithMb('foobar', mockMbMap); + + expect(mockMbMap.getPaintPropertyCalls()).toEqual([ + [ + 'foobar', + 'circle-radius', + [ + 'interpolate', + ['linear'], + [ + 'coalesce', + [ + 'case', + ['==', ['feature-state', 'foobar'], null], + 99, + ['max', ['min', ['to-number', ['feature-state', 'foobar']], 100], 100], + ], + 0, + ], + 100, + 32, + ], + ], + ]); + }); +}); From 2559dbe3ba57a6e9e8705524a45ea0bf3031385b Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Sat, 6 Jun 2020 01:05:02 +0200 Subject: [PATCH 07/27] Optimize saved objects getScopedClient and HTTP API (#68221) * Create a single repository to be shared by all calls to getScopedClient * Cache migrator.getActiveMappings to improve createRepository * Use typeregistry.getAllTypes instead of getRootPropertiesObjects(mappings) * Don't validate plugin's config every time it's read * Fix saved_objects_mixin --- src/core/server/plugins/plugin_context.ts | 6 +++--- .../migrations/kibana/kibana_migrator.ts | 6 +++++- .../server/saved_objects/service/lib/repository.ts | 2 +- .../server/saved_objects/saved_objects_mixin.js | 3 +-- .../spaces/server/spaces_service/spaces_service.ts | 11 +++++++---- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 7afb607192cae..f0db3a25e313d 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -17,7 +17,7 @@ * under the License. */ -import { map } from 'rxjs/operators'; +import { map, shareReplay } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; import { CoreContext } from '../core_context'; import { PluginWrapper } from './plugin'; @@ -107,8 +107,8 @@ export function createPluginInitializerContext( * @param ConfigClass A class (not an instance of a class) that contains a * static `schema` that we validate the config at the given `path` against. */ - create() { - return coreContext.configService.atPath(pluginManifest.configPath); + create() { + return coreContext.configService.atPath(pluginManifest.configPath).pipe(shareReplay(1)); }, createIfExists() { return coreContext.configService.optionalAtPath(pluginManifest.configPath); diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index 4f69d45c192e9..69b57a498936e 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -74,6 +74,7 @@ export class KibanaMigrator { private readonly status$ = new BehaviorSubject({ status: 'waiting', }); + private readonly activeMappings: IndexMapping; /** * Creates an instance of KibanaMigrator. @@ -100,6 +101,9 @@ export class KibanaMigrator { validateDoc: docValidator(savedObjectValidations || {}), log: this.log, }); + // Building the active mappings (and associated md5sums) is an expensive + // operation so we cache the result + this.activeMappings = buildActiveMappings(this.mappingProperties); } /** @@ -172,7 +176,7 @@ export class KibanaMigrator { * */ public getActiveMappings(): IndexMapping { - return buildActiveMappings(this.mappingProperties); + return this.activeMappings; } /** diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index e23f8dec5927c..b093fe779cab7 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -136,7 +136,7 @@ export class SavedObjectsRepository { injectedConstructor: any = SavedObjectsRepository ): ISavedObjectsRepository { const mappings = migrator.getActiveMappings(); - const allTypes = Object.keys(getRootPropertiesObjects(mappings)); + const allTypes = typeRegistry.getAllTypes().map((t) => t.name); const serializer = new SavedObjectsSerializer(typeRegistry); const visibleTypes = allTypes.filter((type) => !typeRegistry.isHidden(type)); diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.js b/src/legacy/server/saved_objects/saved_objects_mixin.js index 7d84c27bd1ef0..63839b9d0f1d7 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.js @@ -27,14 +27,13 @@ import { importSavedObjectsFromStream, resolveSavedObjectsImportErrors, } from '../../../core/server/saved_objects'; -import { getRootPropertiesObjects } from '../../../core/server/saved_objects/mappings'; import { convertTypesToLegacySchema } from '../../../core/server/saved_objects/utils'; export function savedObjectsMixin(kbnServer, server) { const migrator = kbnServer.newPlatform.__internals.kibanaMigrator; const typeRegistry = kbnServer.newPlatform.start.core.savedObjects.getTypeRegistry(); const mappings = migrator.getActiveMappings(); - const allTypes = Object.keys(getRootPropertiesObjects(mappings)); + const allTypes = typeRegistry.getAllTypes().map((t) => t.name); const schema = new SavedObjectsSchema(convertTypesToLegacySchema(typeRegistry.getAllTypes())); const visibleTypes = allTypes.filter((type) => !schema.isHiddenType(type)); diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts index 759b0606a5e8b..cf181a78efcb8 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts @@ -68,14 +68,18 @@ export class SpacesService { return spaceId; }; + const internalRepositoryPromise = getStartServices().then(([coreStart]) => + coreStart.savedObjects.createInternalRepository(['space']) + ); + const getScopedClient = async (request: KibanaRequest) => { const [coreStart] = await getStartServices(); + const internalRepository = await internalRepositoryPromise; return config$ .pipe( + take(1), map((config) => { - const internalRepository = coreStart.savedObjects.createInternalRepository(['space']); - const callWithRequestRepository = coreStart.savedObjects.createScopedRepository( request, ['space'] @@ -92,8 +96,7 @@ export class SpacesService { internalRepository, request ); - }), - take(1) + }) ) .toPromise(); }; From 48ef260e11ab04ee2c5e93eabcfad4bbf832885a Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Sat, 6 Jun 2020 11:25:12 +0200 Subject: [PATCH 08/27] [SIEM] Fix timeline buildGlobalQuery (#68320) --- .../components/timeline/helpers.test.tsx | 78 +++++++++++++++---- .../timelines/components/timeline/helpers.tsx | 49 ++++++------ 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx index ede8d7cfded58..1038ac4b69587 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx @@ -18,7 +18,7 @@ const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf(); describe('Build KQL Query', () => { test('Build KQL query with one data provider', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1"'); }); @@ -56,18 +56,40 @@ describe('Build KQL Query', () => { }); test('Build KQL query with two data provider', () => { - const dataProviders = mockDataProviders.slice(0, 2); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('(name : "Provider 1") or (name : "Provider 2")'); + }); + + test('Build KQL query with two data provider and first is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].enabled = false; + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 2"'); + }); + + test('Build KQL query with two data provider and second is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[1].enabled = false; const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); - expect(cleanUpKqlQuery(kqlQuery)).toEqual('(name : "Provider 1" ) or (name : "Provider 2" )'); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1"'); }); test('Build KQL query with one data provider and one and', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); - dataProviders[0].and = mockDataProviders.slice(1, 2); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1" and name : "Provider 2"'); }); + test('Build KQL query with one disabled data provider and one and', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); + dataProviders[0].enabled = false; + dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 2"'); + }); + test('Build KQL query with one data provider and one and as timestamp (string input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); @@ -106,28 +128,50 @@ describe('Build KQL Query', () => { test('Build KQL query with two data provider and multiple and', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' ); }); + test('Build KQL query with two data provider and multiple and and first data provider is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].enabled = false; + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual( + '(name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' + ); + }); + + test('Build KQL query with two data provider and multiple and and first and provider is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[0].and[0].enabled = false; + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual( + '(name : "Provider 1" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' + ); + }); + test('Build KQL query with all data provider', () => { const kqlQuery = buildGlobalQuery(mockDataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( - '(name : "Provider 1" ) or (name : "Provider 2" ) or (name : "Provider 3" ) or (name : "Provider 4" ) or (name : "Provider 5" ) or (name : "Provider 6" ) or (name : "Provider 7" ) or (name : "Provider 8" ) or (name : "Provider 9" ) or (name : "Provider 10" )' + '(name : "Provider 1") or (name : "Provider 2") or (name : "Provider 3") or (name : "Provider 4") or (name : "Provider 5") or (name : "Provider 6") or (name : "Provider 7") or (name : "Provider 8") or (name : "Provider 9") or (name : "Provider 10")' ); }); test('Build complex KQL query with and and or', () => { const dataProviders = cloneDeep(mockDataProviders); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( - '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5") or (name : "Provider 3" ) or (name : "Provider 4" ) or (name : "Provider 5" ) or (name : "Provider 6" ) or (name : "Provider 7" ) or (name : "Provider 8" ) or (name : "Provider 9" ) or (name : "Provider 10" )' + '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5") or (name : "Provider 3") or (name : "Provider 4") or (name : "Provider 5") or (name : "Provider 6") or (name : "Provider 7") or (name : "Provider 8") or (name : "Provider 9") or (name : "Provider 10")' ); }); }); @@ -223,7 +267,7 @@ describe('Combined Queries', () => { }); test('Only Data Provider', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -338,7 +382,7 @@ describe('Combined Queries', () => { }); test('Data Provider & KQL search query', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -356,7 +400,7 @@ describe('Combined Queries', () => { }); test('Data Provider & KQL filter query', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -375,8 +419,8 @@ describe('Combined Queries', () => { test('Data Provider & KQL search query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -395,8 +439,8 @@ describe('Combined Queries', () => { test('Data Provider & KQL filter query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const { filterQuery } = combineQueries({ config, dataProviders, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index da74d22575f85..b5481e9d4eee2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -63,35 +63,30 @@ const buildQueryMatch = ( : `${dataProvider.queryMatch.field} ${EXISTS_OPERATOR}` }`.trim(); -const buildQueryForAndProvider = ( - dataAndProviders: DataProvidersAnd[], - browserFields: BrowserFields -) => - dataAndProviders - .reduce((andQuery, andDataProvider) => { - const prepend = (q: string) => `${q !== '' ? `${q} and ` : ''}`; - return andDataProvider.enabled - ? `${prepend(andQuery)} ${buildQueryMatch(andDataProvider, browserFields)}` - : andQuery; - }, '') - .trim(); - export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: BrowserFields) => dataProviders - .reduce((query, dataProvider: DataProvider, i) => { - const prepend = (q: string) => `${q !== '' ? `${q} or ` : ''}`; - const openParen = i >= 0 && dataProviders.length > 1 ? '(' : ''; - const closeParen = i >= 0 && dataProviders.length > 1 ? ')' : ''; - return dataProvider.enabled - ? `${prepend(query)}${openParen}${buildQueryMatch(dataProvider, browserFields)} - ${ - dataProvider.and.length > 0 - ? ` and ${buildQueryForAndProvider(dataProvider.and, browserFields)}` - : '' - }${closeParen}`.trim() - : query; - }, '') - .trim(); + .reduce((queries: string[], dataProvider: DataProvider) => { + const flatDataProviders = [dataProvider, ...dataProvider.and]; + const activeDataProviders = flatDataProviders.filter( + (flatDataProvider) => flatDataProvider.enabled + ); + + if (!activeDataProviders.length) return queries; + + const activeDataProvidersQueries = activeDataProviders.map((activeDataProvider) => + buildQueryMatch(activeDataProvider, browserFields) + ); + + const activeDataProvidersQueryMatch = activeDataProvidersQueries.join(' and '); + + return [...queries, activeDataProvidersQueryMatch]; + }, []) + .filter((queriesItem) => !isEmpty(queriesItem)) + .reduce((globalQuery: string, queryMatch: string, index: number, queries: string[]) => { + if (queries.length <= 1) return queryMatch; + + return !index ? `(${queryMatch})` : `${globalQuery} or (${queryMatch})`; + }, ''); export const combineQueries = ({ config, From 07e3c9cb41c6997f59f8f307bbe34b0229fe647d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 8 Jun 2020 08:07:35 +0100 Subject: [PATCH 09/27] [Observability] Landing page for Observability (#67467) * creating overview page and menu * styling the home page * adjusting breadcrumb * renaming isnt working * renaming isnt working * renaming isnt working * fixing import * fixing scroll when resize window * fixing eslint errors * prepending links * adding target option * refactoring * adding dark mode support * fixing prettier format * fixing i18n * reverting some unnecessary changes * addressing PR comments * fixing functional tests * ordering observability menu * fixing tests * addressing PR comments * fixing test * fixing scroll * addressing pr comments * addressing pr comments Co-authored-by: Elastic Machine --- x-pack/.i18nrc.json | 3 +- x-pack/plugins/apm/public/plugin.ts | 2 +- x-pack/plugins/infra/public/plugin.ts | 4 +- .../public/application/index.tsx | 29 +++ .../public/assets/observability_overview.png | Bin 0 -> 98273 bytes .../public/context/plugin_context.tsx | 14 ++ .../public/hooks/use_plugin_context.tsx | 12 + .../observability/public/pages/home/index.tsx | 205 ++++++++++++++++++ .../public/pages/home/section.ts | 84 +++++++ x-pack/plugins/observability/public/plugin.ts | 32 ++- x-pack/plugins/uptime/public/apps/plugin.ts | 2 +- .../advanced_settings_security.ts | 6 +- .../apps/apm/feature_controls/apm_security.ts | 4 +- .../feature_controls/canvas_security.ts | 4 +- .../feature_controls/dev_tools_security.ts | 4 +- .../feature_controls/discover_security.ts | 6 +- .../graph/feature_controls/graph_security.ts | 4 +- .../index_patterns_security.ts | 6 +- .../infrastructure_security.ts | 4 +- .../infra/feature_controls/logs_security.ts | 4 +- .../maps/feature_controls/maps_security.ts | 4 +- .../feature_controls/timelion_security.ts | 4 +- .../feature_controls/uptime_security.ts | 4 +- .../feature_controls/visualize_security.ts | 6 +- 24 files changed, 408 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugins/observability/public/application/index.tsx create mode 100644 x-pack/plugins/observability/public/assets/observability_overview.png create mode 100644 x-pack/plugins/observability/public/context/plugin_context.tsx create mode 100644 x-pack/plugins/observability/public/hooks/use_plugin_context.tsx create mode 100644 x-pack/plugins/observability/public/pages/home/index.tsx create mode 100644 x-pack/plugins/observability/public/pages/home/section.ts diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index d17e5d1a74a30..85b40d33c4089 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -48,7 +48,8 @@ "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "plugins/upgrade_assistant", "xpack.uptime": ["plugins/uptime"], - "xpack.watcher": "plugins/watcher" + "xpack.watcher": "plugins/watcher", + "xpack.observability": "plugins/observability" }, "translations": [ "plugins/translations/translations/zh-CN.json", diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 76320efe617ea..0939c51b16605 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -75,7 +75,7 @@ export class ApmPlugin implements Plugin { core.application.register({ id: 'apm', title: 'APM', - order: 8100, + order: 8300, euiIconType: 'apmApp', appRoute: '/app/apm', icon: 'plugins/apm/public/icon.svg', diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index ead5644d19fa2..deae78e22c6a1 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -64,7 +64,7 @@ export class Plugin defaultMessage: 'Logs', }), euiIconType: 'logsApp', - order: 8000, + order: 8100, appRoute: '/app/logs', category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { @@ -89,7 +89,7 @@ export class Plugin defaultMessage: 'Metrics', }), euiIconType: 'metricsApp', - order: 8001, + order: 8200, appRoute: '/app/metrics', category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx new file mode 100644 index 0000000000000..21a9fabf445f1 --- /dev/null +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -0,0 +1,29 @@ +/* + * 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 ReactDOM from 'react-dom'; +import { EuiThemeProvider } from '../../../../legacy/common/eui_styled_components'; +import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; +import { Home } from '../pages/home'; +import { PluginContext } from '../context/plugin_context'; + +export const renderApp = (core: CoreStart, { element }: AppMountParameters) => { + const i18nCore = core.i18n; + const isDarkMode = core.uiSettings.get('theme:darkMode'); + ReactDOM.render( + + + + + + + , + element + ); + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/plugins/observability/public/assets/observability_overview.png b/x-pack/plugins/observability/public/assets/observability_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..70be08af9745ad4d7ab427d9b3d4c1d2d2f1e75a GIT binary patch literal 98273 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�s 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

{^Fs1wC!ys~qb#haBPfuxcrj$rVVBk_t#x* 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 NE2St|D{dJ4{{R5buN43Q literal 0 HcmV?d00001 diff --git a/x-pack/plugins/observability/public/context/plugin_context.tsx b/x-pack/plugins/observability/public/context/plugin_context.tsx new file mode 100644 index 0000000000000..7d705e7a6cc05 --- /dev/null +++ b/x-pack/plugins/observability/public/context/plugin_context.tsx @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createContext } from 'react'; +import { AppMountContext } from 'kibana/public'; + +export interface PluginContextValue { + core: AppMountContext['core']; +} + +export const PluginContext = createContext({} as PluginContextValue); diff --git a/x-pack/plugins/observability/public/hooks/use_plugin_context.tsx b/x-pack/plugins/observability/public/hooks/use_plugin_context.tsx new file mode 100644 index 0000000000000..eeec115f0d286 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_plugin_context.tsx @@ -0,0 +1,12 @@ +/* + * 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 { useContext } from 'react'; +import { PluginContext } from '../context/plugin_context'; + +export function usePluginContext() { + return useContext(PluginContext); +} diff --git a/x-pack/plugins/observability/public/pages/home/index.tsx b/x-pack/plugins/observability/public/pages/home/index.tsx new file mode 100644 index 0000000000000..b9b567bef4ab4 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/home/index.tsx @@ -0,0 +1,205 @@ +/* + * 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 { + EuiButton, + EuiCard, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiIcon, + EuiImage, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect } from 'react'; +import styled from 'styled-components'; +import { usePluginContext } from '../../hooks/use_plugin_context'; +import { appsSection, tryItOutItemsSection } from './section'; + +const Container = styled.div` + min-height: calc(100vh - 48px); + background: ${(props) => props.theme.eui.euiColorEmptyShade}; +`; + +const Title = styled.div` + background-color: ${(props) => props.theme.eui.euiPageBackgroundColor}; + border-bottom: ${(props) => props.theme.eui.euiBorderThin}; +`; + +const Page = styled.div` + width: 100%; + max-width: 1200px; + margin: 0 auto; + overflow: hidden; +} +`; + +const EuiCardWithoutPadding = styled(EuiCard)` + padding: 0; +`; + +export const Home = () => { + const { core } = usePluginContext(); + + useEffect(() => { + core.chrome.setBreadcrumbs([ + { + text: i18n.translate('xpack.observability.home.breadcrumb.observability', { + defaultMessage: 'Observability', + }), + }, + { + text: i18n.translate('xpack.observability.home.breadcrumb.gettingStarted', { + defaultMessage: 'Getting started', + }), + }, + ]); + }, [core]); + + return ( + + + <Page> + <EuiSpacer size="xxl" /> + <EuiFlexGroup> + <EuiFlexItem grow={false}> + <EuiIcon type="logoObservability" size="xxl" /> + </EuiFlexItem> + <EuiFlexItem> + <EuiTitle size="m"> + <h1> + {i18n.translate('xpack.observability.home.title', { + defaultMessage: 'Observability', + })} + </h1> + </EuiTitle> + </EuiFlexItem> + </EuiFlexGroup> + <EuiSpacer size="xxl" /> + </Page> + + + + + {/* title and description */} + + +

+ {i18n.translate('xpack.observability.home.sectionTitle', { + defaultMessage: 'Observability built on the Elastic Stack', + })} +

+ + + + {i18n.translate('xpack.observability.home.sectionsubtitle', { + defaultMessage: + 'Bring your logs, metrics, and APM traces together at scale in a single stack so you can monitor and react to events happening anywhere in your environment.', + })} + + + + {/* Apps sections */} + + + + + + {appsSection.map((app) => ( + + } + title={ + +

{app.title}

+
+ } + description={app.description} + /> +
+ ))} +
+
+ + + +
+
+ + {/* Get started button */} + + + + + {i18n.translate('xpack.observability.home.getStatedButton', { + defaultMessage: 'Get started', + })} + + + + + + + + {/* Try it out */} + + + + +

+ {i18n.translate('xpack.observability.home.tryItOut', { + defaultMessage: 'Try it out', + })} +

+
+
+
+
+ + {/* Try it out sections */} + + + {tryItOutItemsSection.map((item) => ( + + } + title={ + +

{item.title}

+
+ } + description={item.description} + target={item.target} + href={item.href} + /> +
+ ))} +
+ +
+ + + + ); +}; diff --git a/x-pack/plugins/observability/public/pages/home/section.ts b/x-pack/plugins/observability/public/pages/home/section.ts new file mode 100644 index 0000000000000..f8bbfbfa30548 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/home/section.ts @@ -0,0 +1,84 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +interface ISection { + id: string; + title: string; + icon: string; + description: string; + href?: string; + target?: '_blank'; +} + +export const appsSection: ISection[] = [ + { + id: 'logs', + title: i18n.translate('xpack.observability.section.apps.logs.title', { + defaultMessage: 'Logs', + }), + icon: 'logoLogging', + description: i18n.translate('xpack.observability.section.apps.logs.description', { + defaultMessage: + 'The Elastic Stack (sometimes known as the ELK Stack) is the most popular open source logging platform.', + }), + }, + { + id: 'apm', + title: i18n.translate('xpack.observability.section.apps.apm.title', { + defaultMessage: 'APM', + }), + icon: 'logoAPM', + description: i18n.translate('xpack.observability.section.apps.apm.description', { + defaultMessage: + 'See exactly where your application is spending time so you can quickly fix issues and feel good about the code you push.', + }), + }, + { + id: 'metrics', + title: i18n.translate('xpack.observability.section.apps.metrics.title', { + defaultMessage: 'Metrics', + }), + icon: 'logoMetrics', + description: i18n.translate('xpack.observability.section.apps.metrics.description', { + defaultMessage: + 'Already using the Elastic Stack for logs? Add metrics in just a few steps and correlate metrics and logs in one place.', + }), + }, + { + id: 'uptime', + title: i18n.translate('xpack.observability.section.apps.uptime.title', { + defaultMessage: 'Uptime', + }), + icon: 'logoUptime', + description: i18n.translate('xpack.observability.section.apps.uptime.description', { + defaultMessage: + 'React to availability issues across your apps and services before they affect users.', + }), + }, +]; + +export const tryItOutItemsSection: ISection[] = [ + { + id: 'demo', + title: i18n.translate('xpack.observability.section.tryItOut.demo.title', { + defaultMessage: 'Demo Playground', + }), + icon: 'play', + description: '', + href: 'https://demo.elastic.co/', + target: '_blank', + }, + { + id: 'sampleData', + title: i18n.translate('xpack.observability.section.tryItOut.sampleData.title', { + defaultMessage: 'Add sample data', + }), + icon: 'documents', + description: '', + href: '/app/home#/tutorial_directory/sampleData', + }, +]; diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index a7eb1c50a0392..f2c88a7b1c056 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -3,13 +3,37 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Plugin as PluginClass, PluginInitializerContext } from 'kibana/public'; +import { + AppMountParameters, + CoreSetup, + DEFAULT_APP_CATEGORIES, + Plugin as PluginClass, + PluginInitializerContext, +} from '../../../../src/core/public'; export type ClientSetup = void; export type ClientStart = void; -export class Plugin implements PluginClass { +export class Plugin implements PluginClass { constructor(context: PluginInitializerContext) {} - start() {} - setup() {} + + public setup(core: CoreSetup) { + core.application.register({ + id: 'observability-overview', + title: 'Overview', + order: 8000, + appRoute: '/app/observability', + category: DEFAULT_APP_CATEGORIES.observability, + + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services + const [coreStart] = await core.getStartServices(); + + return renderApp(coreStart, params); + }, + }); + } + public start() {} } diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts index 5d9bbacb49006..d3a67f81004da 100644 --- a/x-pack/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/plugins/uptime/public/apps/plugin.ts @@ -56,7 +56,7 @@ export class UptimePlugin appRoute: '/app/uptime#/', id: PLUGIN.ID, euiIconType: 'uptimeApp', - order: 8900, + order: 8400, title: PLUGIN.TITLE, category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts index cda41c4e60591..d1acbf436bbe7 100644 --- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts +++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts @@ -69,7 +69,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`allows settings to be changed`, async () => { @@ -125,7 +125,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`does not allow settings to be changed`, async () => { @@ -177,7 +177,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`does not allow navigation to advanced settings; redirects to management home`, async () => { diff --git a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts index aa12543004656..4c3c1556d621c 100644 --- a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts +++ b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts @@ -60,7 +60,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows apm navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['APM', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.contain('APM'); }); it('can navigate to APM app', async () => { @@ -109,7 +109,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows apm navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['APM', 'Stack Management']); + expect(navLinks).to.contain('APM'); }); it('can navigate to APM app', async () => { diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts index e9fa4ccf8e48b..b776d358b1673 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts @@ -66,7 +66,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows canvas navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Canvas', 'Stack Management']); + expect(navLinks).to.contain('Canvas'); }); it(`landing page shows "Create new workpad" button`, async () => { @@ -142,7 +142,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows canvas navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Canvas', 'Stack Management']); + expect(navLinks).to.contain('Canvas'); }); it(`landing page shows disabled "Create new workpad" button`, async () => { diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts index 803ff6399a035..5d5f6b8aaa324 100644 --- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts +++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts @@ -63,7 +63,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Dev Tools navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Dev Tools', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.contain('Dev Tools'); }); describe('console', () => { @@ -144,7 +144,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it(`shows 'Dev Tools' navlink`, async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Dev Tools', 'Stack Management']); + expect(navLinks).to.contain('Dev Tools'); }); describe('console', () => { diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 03a5cc6ac8fa0..6a11daa8d2c26 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -82,7 +82,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Discover', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.contain('Discover'); }); it('shows save button', async () => { @@ -169,7 +169,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.contain('Discover'); }); it(`doesn't show save button`, async () => { @@ -260,7 +260,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.contain('Discover'); }); it(`doesn't show save button`, async () => { diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 9121028c14404..f13d73bc95dbe 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -64,7 +64,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows graph navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Graph', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.contain('Graph'); }); it('landing page shows "Create new graph" button', async () => { @@ -127,7 +127,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows graph navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Graph', 'Stack Management']); + expect(navLinks).to.contain('Graph'); }); it('does not show a "Create new Workspace" button', async () => { diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts index cd892c4424290..a6d2c13cd2b31 100644 --- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts +++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts @@ -71,7 +71,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`index pattern listing shows create button`, async () => { @@ -125,7 +125,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`index pattern listing doesn't show create button`, async () => { @@ -177,7 +177,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.contain('Stack Management'); }); it(`doesn't show Index Patterns in management side-nav`, async () => { diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts index 6e186fc9ab9b2..a15b2b33b229c 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts @@ -61,7 +61,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows metrics navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Metrics', 'Stack Management']); + expect(navLinks).to.contain('Metrics'); }); describe('infrastructure landing page without data', () => { @@ -177,7 +177,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows metrics navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Metrics', 'Stack Management']); + expect(navLinks).to.contain('Metrics'); }); describe('infrastructure landing page without data', () => { diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts index fafc88287a6ab..ce83a22fb2e1f 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts @@ -58,7 +58,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows logs navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Logs', 'Stack Management']); + expect(navLinks).to.contain('Logs'); }); describe('logs landing page without data', () => { @@ -121,7 +121,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows logs navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Logs', 'Stack Management']); + expect(navLinks).to.contain('Logs'); }); describe('logs landing page without data', () => { diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts index 2449430ac85c2..f1c5b3f82f7da 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts +++ b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts @@ -66,7 +66,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows maps navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Maps', 'Stack Management']); + expect(navLinks).to.contain('Maps'); }); it(`allows a map to be created`, async () => { @@ -153,7 +153,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Maps navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Maps', 'Stack Management']); + expect(navLinks).to.contain('Maps'); }); it(`does not show create new button`, async () => { diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts index a3ade23f5c178..5021bd8cce0fc 100644 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts +++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts @@ -60,7 +60,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Timelion', 'Stack Management']); + expect(navLinks).to.contain('Timelion'); }); it(`allows a timelion sheet to be created`, async () => { @@ -112,7 +112,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Timelion', 'Stack Management']); + expect(navLinks).to.contain('Timelion'); }); it(`does not allow a timelion sheet to be created`, async () => { diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts index ae13cf0742432..991cd07dce513 100644 --- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts +++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts @@ -64,7 +64,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows uptime navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Uptime', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.contain('Uptime'); }); it('can navigate to Uptime app', async () => { @@ -115,7 +115,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows uptime navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Uptime', 'Stack Management']); + expect(navLinks).to.contain('Uptime'); }); it('can navigate to Uptime app', async () => { diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 9410a6f9435f2..f74643939477c 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -77,7 +77,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.contain('Visualize'); }); it(`landing page shows "Create new Visualization" button`, async () => { @@ -201,7 +201,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.contain('Visualize'); }); it(`landing page shows "Create new Visualization" button`, async () => { @@ -316,7 +316,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.contain('Visualize'); }); it(`landing page shows "Create new Visualization" button`, async () => { From 09eaa1c57a951680b88f8db87af079d17fc3ef36 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Mon, 8 Jun 2020 11:37:44 +0200 Subject: [PATCH 10/27] Add app arch team as owner of datemath package (#66880) Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4716346277029..48d70910f9bf1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -37,6 +37,7 @@ /examples/url_generators_examples/ @elastic/kibana-app-arch /examples/url_generators_explorer/ @elastic/kibana-app-arch /packages/kbn-interpreter/ @elastic/kibana-app-arch +/packages/elastic-datemath/ @elastic/kibana-app-arch /src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch /src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch From 00cff3cd7e19770f66ae12ac694f0ed015a17821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 8 Jun 2020 12:03:05 +0100 Subject: [PATCH 11/27] Adding key to avoid react warning (#68491) --- x-pack/plugins/observability/public/pages/home/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/pages/home/index.tsx b/x-pack/plugins/observability/public/pages/home/index.tsx index b9b567bef4ab4..072d3c47d3a55 100644 --- a/x-pack/plugins/observability/public/pages/home/index.tsx +++ b/x-pack/plugins/observability/public/pages/home/index.tsx @@ -113,7 +113,7 @@ export const Home = () => { {appsSection.map((app) => ( - + Date: Mon, 8 Jun 2020 13:12:03 +0100 Subject: [PATCH 12/27] Add embed mode options in the Share UI (#58435) --- .../ui/public/styles/_legacy/_base.scss | 4 - .../application/dashboard_app_controller.tsx | 143 +++++++++++- .../embeddable/dashboard_container.tsx | 2 + .../dashboard_container_factory.tsx | 1 + .../viewport/dashboard_viewport.tsx | 18 +- .../get_sample_dashboard_input.ts | 1 + src/plugins/dashboard/public/plugin.tsx | 23 +- .../ui/filter_bar/_global_filter_group.scss | 4 + .../public/angular/kbn_top_nav.js | 4 +- .../public/top_nav_menu/top_nav_menu.test.tsx | 23 +- .../public/top_nav_menu/top_nav_menu.tsx | 48 ++-- .../url_panel_content.test.tsx.snap | 207 +++++++++++++++++- .../public/components/share_context_menu.tsx | 4 +- .../components/url_panel_content.test.tsx | 10 + .../public/components/url_panel_content.tsx | 100 +++++++-- .../public/services/share_menu_manager.tsx | 2 + src/plugins/share/public/types.ts | 11 + test/functional/apps/dashboard/embed_mode.js | 29 ++- .../public/app/dashboard_input.ts | 1 + 19 files changed, 564 insertions(+), 71 deletions(-) diff --git a/src/legacy/ui/public/styles/_legacy/_base.scss b/src/legacy/ui/public/styles/_legacy/_base.scss index fd0a1335f9685..877ae033ae584 100644 --- a/src/legacy/ui/public/styles/_legacy/_base.scss +++ b/src/legacy/ui/public/styles/_legacy/_base.scss @@ -64,10 +64,6 @@ input[type='checkbox'], padding-bottom: $euiSizeS; } - .globalQueryBar { - padding: 0px $euiSizeS $euiSizeS $euiSizeS; - } - > nav, > navbar { z-index: 2 !important; diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index a59d1e8c546d4..206ef4f3d4313 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -19,8 +19,9 @@ import _, { uniq } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { EUI_MODAL_CANCEL_BUTTON } from '@elastic/eui'; -import React from 'react'; +import { EUI_MODAL_CANCEL_BUTTON, EuiCheckboxGroup } from '@elastic/eui'; +import { EuiCheckboxGroupIdToSelectedMap } from '@elastic/eui/src/components/form/checkbox/checkbox_group'; +import React, { useState, ReactElement } from 'react'; import ReactDOM from 'react-dom'; import angular from 'angular'; @@ -94,6 +95,25 @@ export interface DashboardAppControllerDependencies extends RenderDeps { navigation: NavigationStart; } +enum UrlParams { + SHOW_TOP_MENU = 'show-top-menu', + SHOW_QUERY_INPUT = 'show-query-input', + SHOW_TIME_FILTER = 'show-time-filter', + SHOW_FILTER_BAR = 'show-filter-bar', + HIDE_FILTER_BAR = 'hide-filter-bar', +} + +interface UrlParamsSelectedMap { + [UrlParams.SHOW_TOP_MENU]: boolean; + [UrlParams.SHOW_QUERY_INPUT]: boolean; + [UrlParams.SHOW_TIME_FILTER]: boolean; + [UrlParams.SHOW_FILTER_BAR]: boolean; +} + +interface UrlParamValues extends Omit { + [UrlParams.HIDE_FILTER_BAR]: boolean; +} + export class DashboardAppController { // Part of the exposed plugin API - do not remove without careful consideration. appStatus: { @@ -133,8 +153,16 @@ export class DashboardAppController { const filterManager = queryService.filterManager; const queryFilter = filterManager; const timefilter = queryService.timefilter.timefilter; - let showSearchBar = true; - let showQueryBar = true; + const isEmbeddedExternally = Boolean($routeParams.embed); + + // url param rules should only apply when embedded (e.g. url?embed=true) + const shouldForceDisplay = (param: string): boolean => + isEmbeddedExternally && Boolean($routeParams[param]); + + const forceShowTopNavMenu = shouldForceDisplay(UrlParams.SHOW_TOP_MENU); + const forceShowQueryInput = shouldForceDisplay(UrlParams.SHOW_QUERY_INPUT); + const forceShowDatePicker = shouldForceDisplay(UrlParams.SHOW_TIME_FILTER); + const forceHideFilterBar = shouldForceDisplay(UrlParams.HIDE_FILTER_BAR); let lastReloadRequestTime = 0; const dash = ($scope.dash = $route.current.locals.dash); @@ -251,9 +279,6 @@ export class DashboardAppController { } }; - const showFilterBar = () => - $scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode(); - const getEmptyScreenProps = ( shouldShowEditHelp: boolean, isEmptyInReadOnlyMode: boolean @@ -299,6 +324,7 @@ export class DashboardAppController { viewMode: dashboardStateManager.getViewMode(), panels: embeddablesMap, isFullScreenMode: dashboardStateManager.getFullScreenMode(), + isEmbeddedExternally, isEmptyState: shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadonlyMode, useMargins: dashboardStateManager.getUseMargins(), lastReloadRequestTime, @@ -590,17 +616,33 @@ export class DashboardAppController { dashboardStateManager.setSavedQueryId(savedQueryId); }; + const shouldShowFilterBar = (forceHide: boolean): boolean => + !forceHide && ($scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode()); + + const shouldShowNavBarComponent = (forceShow: boolean): boolean => + (forceShow || $scope.isVisible) && !dashboardStateManager.getFullScreenMode(); + const getNavBarProps = () => { const isFullScreenMode = dashboardStateManager.getFullScreenMode(); const screenTitle = dashboardStateManager.getTitle(); + const showTopNavMenu = shouldShowNavBarComponent(forceShowTopNavMenu); + const showQueryInput = shouldShowNavBarComponent(forceShowQueryInput); + const showDatePicker = shouldShowNavBarComponent(forceShowDatePicker); + const showQueryBar = showQueryInput || showDatePicker; + const showFilterBar = shouldShowFilterBar(forceHideFilterBar); + const showSearchBar = showQueryBar || showFilterBar; + return { appName: 'dashboard', - config: $scope.isVisible ? $scope.topNavMenu : undefined, + config: showTopNavMenu ? $scope.topNavMenu : undefined, className: isFullScreenMode ? 'kbnTopNavMenu-isFullScreen' : undefined, screenTitle, + showTopNavMenu, showSearchBar, showQueryBar, - showFilterBar: showFilterBar(), + showQueryInput, + showDatePicker, + showFilterBar, indexPatterns: $scope.indexPatterns, showSaveQuery: $scope.showSaveQuery, query: $scope.model.query, @@ -798,7 +840,6 @@ export class DashboardAppController { } = {}; navActions[TopNavIds.FULL_SCREEN] = () => { dashboardStateManager.setFullScreenMode(true); - showQueryBar = false; updateNavBar(); }; navActions[TopNavIds.EXIT_EDIT_MODE] = () => onChangeViewMode(ViewMode.VIEW); @@ -923,6 +964,80 @@ export class DashboardAppController { if (share) { // the share button is only availabale if "share" plugin contract enabled navActions[TopNavIds.SHARE] = (anchorElement) => { + const EmbedUrlParamExtension = ({ + setParamValue, + }: { + setParamValue: (paramUpdate: UrlParamValues) => void; + }): ReactElement => { + const [urlParamsSelectedMap, setUrlParamsSelectedMap] = useState({ + [UrlParams.SHOW_TOP_MENU]: false, + [UrlParams.SHOW_QUERY_INPUT]: false, + [UrlParams.SHOW_TIME_FILTER]: false, + [UrlParams.SHOW_FILTER_BAR]: true, + }); + + const checkboxes = [ + { + id: UrlParams.SHOW_TOP_MENU, + label: i18n.translate('dashboard.embedUrlParamExtension.topMenu', { + defaultMessage: 'Top menu', + }), + }, + { + id: UrlParams.SHOW_QUERY_INPUT, + label: i18n.translate('dashboard.embedUrlParamExtension.query', { + defaultMessage: 'Query', + }), + }, + { + id: UrlParams.SHOW_TIME_FILTER, + label: i18n.translate('dashboard.embedUrlParamExtension.timeFilter', { + defaultMessage: 'Time filter', + }), + }, + { + id: UrlParams.SHOW_FILTER_BAR, + label: i18n.translate('dashboard.embedUrlParamExtension.filterBar', { + defaultMessage: 'Filter bar', + }), + }, + ]; + + const handleChange = (param: string): void => { + const urlParamsSelectedMapUpdate = { + ...urlParamsSelectedMap, + [param]: !urlParamsSelectedMap[param as keyof UrlParamsSelectedMap], + }; + setUrlParamsSelectedMap(urlParamsSelectedMapUpdate); + + const urlParamValues = { + [UrlParams.SHOW_TOP_MENU]: urlParamsSelectedMap[UrlParams.SHOW_TOP_MENU], + [UrlParams.SHOW_QUERY_INPUT]: urlParamsSelectedMap[UrlParams.SHOW_QUERY_INPUT], + [UrlParams.SHOW_TIME_FILTER]: urlParamsSelectedMap[UrlParams.SHOW_TIME_FILTER], + [UrlParams.HIDE_FILTER_BAR]: !urlParamsSelectedMap[UrlParams.SHOW_FILTER_BAR], + [param === UrlParams.SHOW_FILTER_BAR ? UrlParams.HIDE_FILTER_BAR : param]: + param === UrlParams.SHOW_FILTER_BAR + ? urlParamsSelectedMap[UrlParams.SHOW_FILTER_BAR] + : !urlParamsSelectedMap[param as keyof UrlParamsSelectedMap], + }; + setParamValue(urlParamValues); + }; + + return ( + + ); + }; + share.toggleShareContextMenu({ anchorElement, allowEmbed: true, @@ -935,6 +1050,12 @@ export class DashboardAppController { title: dash.title, }, isDirty: dashboardStateManager.getIsDirty(), + embedUrlParamExtensions: [ + { + paramName: 'embed', + component: EmbedUrlParamExtension, + }, + ], }); }; } @@ -955,8 +1076,6 @@ export class DashboardAppController { const visibleSubscription = chrome.getIsVisible$().subscribe((isVisible) => { $scope.$evalAsync(() => { $scope.isVisible = isVisible; - showSearchBar = isVisible || showFilterBar(); - showQueryBar = !dashboardStateManager.getFullScreenMode() && isVisible; updateNavBar(); }); }); diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 5d4cc851cf455..2121ca4c784bd 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -57,6 +57,7 @@ export interface DashboardContainerInput extends ContainerInput { useMargins: boolean; title: string; description?: string; + isEmbeddedExternally: boolean; isFullScreenMode: boolean; panels: { [panelId: string]: DashboardPanelState; @@ -105,6 +106,7 @@ export class DashboardContainer extends Container { return { panels: {}, + isEmbeddedExternally: false, isFullScreenMode: false, useMargins: true, }; diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx index 429837583b648..9ee50426b19bb 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -36,6 +36,7 @@ interface State { title: string; description?: string; panels: { [key: string]: PanelState }; + isEmbeddedExternally?: boolean; isEmptyState?: boolean; } @@ -52,6 +53,7 @@ export class DashboardViewport extends React.Component {isFullScreenMode && ( )} {renderEmpty && renderEmpty()} @@ -116,7 +122,14 @@ export class DashboardViewport extends React.Component )} diff --git a/src/plugins/dashboard/public/application/test_helpers/get_sample_dashboard_input.ts b/src/plugins/dashboard/public/application/test_helpers/get_sample_dashboard_input.ts index 4ceac90672cb3..825a69155ba22 100644 --- a/src/plugins/dashboard/public/application/test_helpers/get_sample_dashboard_input.ts +++ b/src/plugins/dashboard/public/application/test_helpers/get_sample_dashboard_input.ts @@ -27,6 +27,7 @@ export function getSampleDashboardInput( id: '123', filters: [], useMargins: false, + isEmbeddedExternally: false, isFullScreenMode: false, title: 'My Dashboard', query: { diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 0de3982039928..a3338ab3bbcbb 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -167,15 +167,26 @@ export class DashboardPlugin const getStartServices = async () => { const [coreStart, deps] = await core.getStartServices(); - const useHideChrome = () => { + const useHideChrome = ({ toggleChrome } = { toggleChrome: true }) => { React.useEffect(() => { - coreStart.chrome.setIsVisible(false); - return () => coreStart.chrome.setIsVisible(true); - }, []); + if (toggleChrome) { + coreStart.chrome.setIsVisible(false); + } + + return () => { + if (toggleChrome) { + coreStart.chrome.setIsVisible(true); + } + }; + }, [toggleChrome]); }; - const ExitFullScreenButton: React.FC = (props) => { - useHideChrome(); + const ExitFullScreenButton: React.FC< + ExitFullScreenButtonProps & { + toggleChrome: boolean; + } + > = ({ toggleChrome, ...props }) => { + useHideChrome({ toggleChrome }); return ; }; return { diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss index 1c47c28097454..731c9f4d7f18d 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss @@ -3,6 +3,10 @@ padding: 0px $euiSizeS $euiSizeS $euiSizeS; } +.globalQueryBar:first-child { + padding-top: $euiSizeS; +} + .globalQueryBar:not(:empty) { padding-bottom: $euiSizeS; } diff --git a/src/plugins/kibana_legacy/public/angular/kbn_top_nav.js b/src/plugins/kibana_legacy/public/angular/kbn_top_nav.js index a0faf4a6a071c..b3fbe8baadec3 100644 --- a/src/plugins/kibana_legacy/public/angular/kbn_top_nav.js +++ b/src/plugins/kibana_legacy/public/angular/kbn_top_nav.js @@ -94,11 +94,11 @@ export const createTopNavHelper = ({ TopNavMenu }) => (reactDirective) => { // All modifiers default to true. // Set to false to hide subcomponents. 'showSearchBar', - 'showFilterBar', 'showQueryBar', 'showQueryInput', - 'showDatePicker', 'showSaveQuery', + 'showDatePicker', + 'showFilterBar', 'appName', 'screenTitle', diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx index 74cfd125c2e3a..46384fb3f27d5 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx @@ -29,6 +29,7 @@ const dataShim = { }; describe('TopNavMenu', () => { + const WRAPPER_SELECTOR = '.kbnTopNavMenu__wrapper'; const TOP_NAV_ITEM_SELECTOR = 'TopNavMenuItem'; const SEARCH_BAR_SELECTOR = 'SearchBar'; const menuItems: TopNavMenuData[] = [ @@ -51,18 +52,28 @@ describe('TopNavMenu', () => { it('Should render nothing when no config is provided', () => { const component = shallowWithIntl(); + expect(component.find(WRAPPER_SELECTOR).length).toBe(0); + expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); + expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); + }); + + it('Should not render menu items when config is empty', () => { + const component = shallowWithIntl(); + expect(component.find(WRAPPER_SELECTOR).length).toBe(0); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render 1 menu item', () => { const component = shallowWithIntl(); + expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(1); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render multiple menu items', () => { const component = shallowWithIntl(); + expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(menuItems.length); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); @@ -71,15 +82,25 @@ describe('TopNavMenu', () => { const component = shallowWithIntl( ); - + expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1); }); + it('Should render menu items and search bar', () => { + const component = shallowWithIntl( + + ); + expect(component.find(WRAPPER_SELECTOR).length).toBe(1); + expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(menuItems.length); + expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1); + }); + it('Should render with a class name', () => { const component = shallowWithIntl( { return ( + {renderItems()} + + ); + } + + function renderSearchBar(): ReactElement | null { // Validate presense of all required fields - if (!showSearchBar || !props.data) return; + if (!showSearchBar || !props.data) return null; const { SearchBar } = props.data.ui; return ; } @@ -70,16 +95,7 @@ export function TopNavMenu(props: TopNavMenuProps) { const className = classNames('kbnTopNavMenu', props.className); return ( - - {renderItems()} - + {renderMenu(className)} {renderSearchBar()} ); diff --git a/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap b/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap index 8787e0c027375..cae7aa96a7c0e 100644 --- a/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap +++ b/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap @@ -44,7 +44,9 @@ exports[`share url panel content render 1`] = ` gutterSize="none" responsive={false} > - + - + - + - + - + - + - + - + `; + +exports[`should show url param extensions 1`] = ` + + + + } + labelType="label" + > + + + + + + + } + position="bottom" + /> + + , + }, + Object { + "data-test-subj": "exportAsSavedObject", + "disabled": false, + "id": "savedObject", + "label": + + + + + + } + position="bottom" + /> + + , + }, + ] + } + /> + + + + + + + + + } + onChange={[Function]} + /> + + + + } + position="bottom" + /> + + + + + + + + + +`; diff --git a/src/plugins/share/public/components/share_context_menu.tsx b/src/plugins/share/public/components/share_context_menu.tsx index c12e9dabd1938..26426853ddabe 100644 --- a/src/plugins/share/public/components/share_context_menu.tsx +++ b/src/plugins/share/public/components/share_context_menu.tsx @@ -26,7 +26,7 @@ import { EuiContextMenu, EuiContextMenuPanelDescriptor } from '@elastic/eui'; import { HttpStart } from 'kibana/public'; import { UrlPanelContent } from './url_panel_content'; -import { ShareMenuItem, ShareContextMenuPanelItem } from '../types'; +import { ShareMenuItem, ShareContextMenuPanelItem, UrlParamExtension } from '../types'; interface Props { allowEmbed: boolean; @@ -39,6 +39,7 @@ interface Props { onClose: () => void; basePath: string; post: HttpStart['post']; + embedUrlParamExtensions?: UrlParamExtension[]; } export class ShareContextMenu extends Component { @@ -100,6 +101,7 @@ export class ShareContextMenu extends Component { basePath={this.props.basePath} post={this.props.post} shareableUrl={this.props.shareableUrl} + urlParamExtensions={this.props.embedUrlParamExtensions} /> ), }; diff --git a/src/plugins/share/public/components/url_panel_content.test.tsx b/src/plugins/share/public/components/url_panel_content.test.tsx index bd30dbf002df8..481f8312f4262 100644 --- a/src/plugins/share/public/components/url_panel_content.test.tsx +++ b/src/plugins/share/public/components/url_panel_content.test.tsx @@ -202,3 +202,13 @@ describe('share url panel content', () => { }); }); }); + +test('should show url param extensions', () => { + const TestExtension = () =>
; + const extensions = [{ paramName: 'testExtension', component: TestExtension }]; + const component = shallow( + + ); + expect(component.find('TestExtension').length).toBe(1); + expect(component).toMatchSnapshot(); +}); diff --git a/src/plugins/share/public/components/url_panel_content.tsx b/src/plugins/share/public/components/url_panel_content.tsx index 2ece2052c4b95..65a8538693a49 100644 --- a/src/plugins/share/public/components/url_panel_content.tsx +++ b/src/plugins/share/public/components/url_panel_content.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { Component } from 'react'; +import React, { Component, ReactElement } from 'react'; import { EuiButton, @@ -41,6 +41,7 @@ import { HttpStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { shortenUrl } from '../lib/url_shortener'; +import { UrlParamExtension } from '../types'; interface Props { allowShortUrl: boolean; @@ -50,6 +51,7 @@ interface Props { shareableUrl?: string; basePath: string; post: HttpStart['post']; + urlParamExtensions?: UrlParamExtension[]; } export enum ExportUrlAsType { @@ -57,12 +59,19 @@ export enum ExportUrlAsType { EXPORT_URL_AS_SNAPSHOT = 'snapshot', } +interface UrlParams { + [extensionName: string]: { + [queryParam: string]: boolean; + }; +} + interface State { exportUrlAs: ExportUrlAsType; useShortUrl: boolean; isCreatingShortUrl: boolean; url?: string; shortUrlErrorMsg?: string; + urlParams?: UrlParams; } export class UrlPanelContent extends Component { @@ -100,7 +109,7 @@ export class UrlPanelContent extends Component { {this.renderExportAsRadioGroup()} - + {this.renderUrlParamExtensions()} {this.renderShortUrlSwitch()} @@ -151,6 +160,13 @@ export class UrlPanelContent extends Component { } }; + private updateUrlParams = (url: string) => { + const embedUrl = this.props.isEmbedded ? this.makeUrlEmbeddable(url) : url; + const extendUrl = this.state.urlParams ? this.getUrlParamExtensions(embedUrl) : embedUrl; + + return extendUrl; + }; + private getSavedObjectUrl = () => { if (this.isNotSaved()) { return; @@ -166,7 +182,7 @@ export class UrlPanelContent extends Component { // Get the application route, after the hash, and remove the #. const parsedAppUrl = parseUrl(parsedUrl.hash.slice(1), true); - let formattedUrl = formatUrl({ + const formattedUrl = formatUrl({ protocol: parsedUrl.protocol, auth: parsedUrl.auth, host: parsedUrl.host, @@ -180,28 +196,42 @@ export class UrlPanelContent extends Component { }, }), }); - if (this.props.isEmbedded) { - formattedUrl = this.makeUrlEmbeddable(formattedUrl); - } - return formattedUrl; + return this.updateUrlParams(formattedUrl); }; private getSnapshotUrl = () => { - let url = this.props.shareableUrl || window.location.href; - if (this.props.isEmbedded) { - url = this.makeUrlEmbeddable(url); - } - return url; + const url = this.props.shareableUrl || window.location.href; + + return this.updateUrlParams(url); }; - private makeUrlEmbeddable = (url: string) => { - const embedQueryParam = '?embed=true'; + private makeUrlEmbeddable = (url: string): string => { + const embedParam = '?embed=true'; const urlHasQueryString = url.indexOf('?') !== -1; + if (urlHasQueryString) { - return url.replace('?', `${embedQueryParam}&`); + return url.replace('?', `${embedParam}&`); } - return `${url}${embedQueryParam}`; + + return `${url}${embedParam}`; + }; + + private getUrlParamExtensions = (url: string): string => { + const { urlParams } = this.state; + return urlParams + ? Object.keys(urlParams).reduce((urlAccumulator, key) => { + const urlParam = urlParams[key]; + return urlParam + ? Object.keys(urlParam).reduce((queryAccumulator, queryParam) => { + const isQueryParamEnabled = urlParam[queryParam]; + return isQueryParamEnabled + ? queryAccumulator + `&${queryParam}=true` + : queryAccumulator; + }, urlAccumulator) + : urlAccumulator; + }, url) + : url; }; private makeIframeTag = (url?: string) => { @@ -247,6 +277,10 @@ export class UrlPanelContent extends Component { } // "Use short URL" is checked but shortUrl has not been generated yet so one needs to be created. + this.createShortUrl(); + }; + + private createShortUrl = async () => { this.setState({ isCreatingShortUrl: true, shortUrlErrorMsg: undefined, @@ -262,7 +296,7 @@ export class UrlPanelContent extends Component { this.setState( { isCreatingShortUrl: false, - useShortUrl: isChecked, + useShortUrl: true, }, this.setUrl ); @@ -321,7 +355,7 @@ export class UrlPanelContent extends Component { private renderWithIconTip = (child: React.ReactNode, tipContent: React.ReactNode) => { return ( - {child} + {child} @@ -397,4 +431,34 @@ export class UrlPanelContent extends Component { ); }; + + private renderUrlParamExtensions = (): ReactElement | void => { + if (!this.props.urlParamExtensions) { + return; + } + + const setParamValue = (paramName: string) => ( + values: { [queryParam: string]: boolean } = {} + ): void => { + const stateUpdate = { + urlParams: { + ...this.state.urlParams, + [paramName]: { + ...values, + }, + }, + }; + this.setState(stateUpdate, this.state.useShortUrl ? this.createShortUrl : this.setUrl); + }; + + return ( + + {this.props.urlParamExtensions.map(({ paramName, component: UrlParamComponent }) => ( + + + + ))} + + ); + }; } diff --git a/src/plugins/share/public/services/share_menu_manager.tsx b/src/plugins/share/public/services/share_menu_manager.tsx index 35116efa85961..3325c5503fe89 100644 --- a/src/plugins/share/public/services/share_menu_manager.tsx +++ b/src/plugins/share/public/services/share_menu_manager.tsx @@ -67,6 +67,7 @@ export class ShareMenuManager { shareableUrl, post, basePath, + embedUrlParamExtensions, }: ShowShareMenuOptions & { menuItems: ShareMenuItem[]; post: HttpStart['post']; @@ -102,6 +103,7 @@ export class ShareMenuManager { onClose={this.onClose} post={post} basePath={basePath} + embedUrlParamExtensions={embedUrlParamExtensions} /> diff --git a/src/plugins/share/public/types.ts b/src/plugins/share/public/types.ts index 6b20f1f53a28c..8dda9f1195a39 100644 --- a/src/plugins/share/public/types.ts +++ b/src/plugins/share/public/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { ComponentType } from 'react'; import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; /** @@ -80,9 +81,19 @@ export interface ShareMenuProvider { getShareMenuItems: (context: ShareContext) => ShareMenuItem[]; } +interface UrlParamExtensionProps { + setParamValue: (values: {}) => void; +} + +export interface UrlParamExtension { + paramName: string; + component: ComponentType; +} + /** @public */ export interface ShowShareMenuOptions extends Omit { anchorElement: HTMLElement; allowEmbed: boolean; allowShortUrl: boolean; + embedUrlParamExtensions?: UrlParamExtension[]; } diff --git a/test/functional/apps/dashboard/embed_mode.js b/test/functional/apps/dashboard/embed_mode.js index 65ef75f3f65e1..a1828143555b0 100644 --- a/test/functional/apps/dashboard/embed_mode.js +++ b/test/functional/apps/dashboard/embed_mode.js @@ -20,6 +20,7 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { + const testSubjects = getService('testSubjects'); const retry = getService('retry'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); @@ -28,6 +29,13 @@ export default function ({ getService, getPageObjects }) { const globalNav = getService('globalNav'); describe('embed mode', () => { + const urlParamExtensions = [ + 'show-top-menu=true', + 'show-query-input=true', + 'show-time-filter=true', + 'hide-filter-bar=true', + ]; + before(async () => { await esArchiver.load('dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ @@ -54,9 +62,28 @@ export default function ({ getService, getPageObjects }) { }); }); + it('shows or hides elements based on URL params', async () => { + await testSubjects.missingOrFail('top-nav'); + await testSubjects.missingOrFail('queryInput'); + await testSubjects.missingOrFail('superDatePickerToggleQuickMenuButton'); + await testSubjects.existOrFail('showFilterActions'); + + const currentUrl = await browser.getCurrentUrl(); + const newUrl = [currentUrl].concat(urlParamExtensions).join('&'); + // Embed parameter only works on a hard refresh. + const useTimeStamp = true; + await browser.get(newUrl.toString(), useTimeStamp); + + await testSubjects.existOrFail('top-nav'); + await testSubjects.existOrFail('queryInput'); + await testSubjects.existOrFail('superDatePickerToggleQuickMenuButton'); + await testSubjects.missingOrFail('showFilterActions'); + }); + after(async function () { const currentUrl = await browser.getCurrentUrl(); - const newUrl = currentUrl.replace('&embed=true', ''); + const replaceParams = ['', 'embed=true'].concat(urlParamExtensions).join('&'); + const newUrl = currentUrl.replace(replaceParams, ''); // First use the timestamp to cause a hard refresh so the new embed parameter works correctly. let useTimeStamp = true; await browser.get(newUrl.toString(), useTimeStamp); diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts index 6f4e1f052f5e0..21b12e2134767 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts @@ -92,6 +92,7 @@ export const dashboardInput: DashboardContainerInput = { }, }, }, + isEmbeddedExternally: false, isFullScreenMode: false, filters: [], useMargins: true, From 49171516d7d110abe8cfa1f9f1fe0707262b1999 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 8 Jun 2020 15:01:55 +0200 Subject: [PATCH 13/27] Unskip embeddables/adding_children suite (#68111) Giving it another chance *cross fingers* Co-authored-by: Elastic Machine --- test/examples/embeddables/adding_children.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts index 25fe7ee607f72..a26ce4c40e2ea 100644 --- a/test/examples/embeddables/adding_children.ts +++ b/test/examples/embeddables/adding_children.ts @@ -25,8 +25,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); const flyout = getService('flyout'); - // FLAKY: https://github.com/elastic/kibana/issues/58692 - describe.skip('creating and adding children', () => { + describe('creating and adding children', () => { before(async () => { await testSubjects.click('embeddablePanelExamplae'); }); From 77ab709976cf068bf5f602c5e7ad27e7c8384a95 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:19:32 -0400 Subject: [PATCH 14/27] Bump pdfmake package version (#68395) --- x-pack/package.json | 2 +- yarn.lock | 268 +++++++++++++++++--------------------------- 2 files changed, 104 insertions(+), 166 deletions(-) diff --git a/x-pack/package.json b/x-pack/package.json index c46d364e0ac46..227547ce15fd1 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -299,7 +299,7 @@ "oppsy": "^2.0.0", "p-retry": "^4.2.0", "papaparse": "^5.2.0", - "pdfmake": "^0.1.63", + "pdfmake": "^0.1.65", "pluralize": "3.1.0", "pngjs": "3.4.0", "polished": "^1.9.2", diff --git a/yarn.lock b/yarn.lock index 678ddfb052cd3..f4d5409f755ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5629,17 +5629,12 @@ acorn-walk@^6.0.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== -acorn-walk@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" - integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== - -acorn-walk@^7.1.1: +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@5.X, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.5.0: +acorn@5.X, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.5.0: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== @@ -5659,7 +5654,12 @@ acorn@^6.0.1, acorn@^6.2.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1: +acorn@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== + +acorn@^7.1.0, acorn@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== @@ -7426,7 +7426,7 @@ babel-preset-react-app@^9.0.0: babel-plugin-macros "2.6.1" babel-plugin-transform-react-remove-prop-types "0.4.24" -babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -7555,12 +7555,12 @@ base64-js@0.0.8: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" integrity sha1-EQHpVE9KdrG8OybUUsqW16NeeXg= -base64-js@^1.0.2, base64-js@^1.1.2: +base64-js@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" integrity sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw== -base64-js@^1.2.1, base64-js@^1.3.1: +base64-js@^1.1.2, base64-js@^1.2.1, base64-js@^1.3.0, base64-js@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== @@ -7973,17 +7973,7 @@ breadth-filter@^2.0.0: dependencies: object.entries "^1.0.4" -brfs@^1.4.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3" - integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ== - dependencies: - quote-stream "^1.0.1" - resolve "^1.1.5" - static-module "^2.2.0" - through2 "^2.0.0" - -brfs@^2.0.2: +brfs@^2.0.0, brfs@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/brfs/-/brfs-2.0.2.tgz#44237878fa82aa479ce4f5fe2c1796ec69f07845" integrity sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ== @@ -8023,20 +8013,13 @@ browser-process-hrtime@^0.1.2: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" integrity sha1-Ql1opY00R/AqBKqJQYf86K+Le44= -browser-resolve@^1.11.3: +browser-resolve@^1.11.3, browser-resolve@^1.8.1: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== dependencies: resolve "1.1.7" -browser-resolve@^1.8.1: - version "1.11.2" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" - integrity sha1-j/CbCixCFxihBRwmCzLkj0QpOM4= - dependencies: - resolve "1.1.7" - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -8072,7 +8055,7 @@ browserify-des@^1.0.0: des.js "^1.0.0" inherits "^2.0.1" -browserify-optional@^1.0.0: +browserify-optional@^1.0.0, browserify-optional@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/browserify-optional/-/browserify-optional-1.0.1.tgz#1e13722cfde0d85f121676c2a72ced533a018869" integrity sha1-HhNyLP3g2F8SFnbCpyztUzoBiGk= @@ -9269,7 +9252,7 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= -clone@^1.0.0, clone@^1.0.1, clone@^1.0.2: +clone@^1.0.0, clone@^1.0.2, clone@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= @@ -9864,7 +9847,7 @@ contour_plot@^0.0.1: resolved "https://registry.yarnpkg.com/contour_plot/-/contour_plot-0.0.1.tgz#475870f032b8e338412aa5fc507880f0bf495c77" integrity sha1-R1hw8DK44zhBKqX8UHiA8L9JXHc= -convert-source-map@1.7.0, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -9881,13 +9864,6 @@ convert-source-map@^0.3.3: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= -convert-source-map@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== - dependencies: - safe-buffer "~5.1.1" - convex-hull@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/convex-hull/-/convex-hull-1.0.3.tgz#20a3aa6ce87f4adea2ff7d17971c9fc1c67e1fff" @@ -10878,6 +10854,11 @@ dargs@^5.1.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" integrity sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk= +dash-ast@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" + integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -11085,12 +11066,7 @@ deep-eql@^0.1.3: dependencies: type-detect "0.1.1" -deep-equal@^1.0.0, deep-equal@^1.0.1, deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -deep-equal@^1.1.1, deep-equal@~1.1.1: +deep-equal@^1.0.0, deep-equal@^1.1.1, deep-equal@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== @@ -11102,6 +11078,11 @@ deep-equal@^1.1.1, deep-equal@~1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-equal@^1.0.1, deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-extend@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -11462,12 +11443,10 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -dfa@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.1.0.tgz#d30218bd10d030fa421df3ebbc82285463a31781" - integrity sha1-0wIYvRDQMPpCHfPrvIIoVGOjF4E= - dependencies: - babel-runtime "^6.11.6" +dfa@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" + integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== di@^0.0.1: version "0.0.1" @@ -12502,7 +12481,7 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -escodegen@^1.11.1, escodegen@^1.8.1: +escodegen@^1.11.1: version "1.12.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== @@ -12549,18 +12528,6 @@ escodegen@~1.2.0: optionalDependencies: source-map "~0.1.30" -escodegen@~1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" - integrity sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" @@ -13422,16 +13389,6 @@ faker@1.1.0: resolved "https://registry.yarnpkg.com/faker/-/faker-1.1.0.tgz#230738ebd37edad9de4a421de12922bd8206a872" integrity sha1-Iwc469N+2tneSkId4SkivYIGqHI= -falafel@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c" - integrity sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw= - dependencies: - acorn "^5.0.0" - foreach "^2.0.5" - isarray "0.0.1" - object-keys "^1.0.6" - fancy-log@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" @@ -14066,20 +14023,20 @@ font-awesome@4.7.0: integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM= fontkit@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/fontkit/-/fontkit-1.8.0.tgz#deb9351619e90ddc91707b6156a9f14c8ab11554" - integrity sha512-EFDRCca7khfQWYu1iFhsqeABpi87f03MBdkT93ZE6YhqCdMzb5Eojb6c4dlJikGv5liuhByyzA7ikpIPTSBWbQ== + version "1.8.1" + resolved "https://registry.yarnpkg.com/fontkit/-/fontkit-1.8.1.tgz#ae77485376f1096b45548bf6ced9a07af62a7846" + integrity sha512-BsNCjDoYRxmNWFdAuK1y9bQt+igIxGtTC9u/jSFjR9MKhmI00rP1fwSvERt+5ddE82544l0XH5mzXozQVUy2Tw== dependencies: - babel-runtime "^6.11.6" - brfs "^1.4.0" + babel-runtime "^6.26.0" + brfs "^2.0.0" brotli "^1.2.0" - browserify-optional "^1.0.0" - clone "^1.0.1" + browserify-optional "^1.0.1" + clone "^1.0.4" deep-equal "^1.0.0" - dfa "^1.0.0" + dfa "^1.2.0" restructure "^0.5.3" tiny-inflate "^1.0.2" - unicode-properties "^1.0.0" + unicode-properties "^1.2.2" unicode-trie "^0.3.0" for-each@^0.3.2: @@ -14120,11 +14077,6 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - foreachasync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" @@ -16476,6 +16428,13 @@ iconv-lite@^0.5.0: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.1.tgz#b2425d3c7b18f7219f2ca663d103bddb91718d64" + integrity sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q== + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-utils@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.0.0.tgz#d52cf4bcdcfa1c45c2dbefb4ffdf6b00ef608098" @@ -20094,12 +20053,12 @@ madge@3.4.4: rc "^1.2.7" walkdir "^0.0.12" -magic-string@^0.22.4: - version "0.22.5" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" - integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== +magic-string@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e" + integrity sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg== dependencies: - vlq "^0.2.2" + sourcemap-codec "^1.4.1" majo@^0.4.1: version "0.4.1" @@ -21956,16 +21915,11 @@ object-identity-map@^1.0.2: dependencies: object.entries "^1.1.0" -object-inspect@^1.7.0, object-inspect@~1.7.0: +object-inspect@^1.6.0, object-inspect@^1.7.0, object-inspect@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== -object-inspect@~1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" - integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== - object-inspect@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" @@ -21976,7 +21930,7 @@ object-is@^1.0.1, object-is@^1.0.2: resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -23049,12 +23003,12 @@ pdfkit@>=0.8.1, pdfkit@^0.11.0: linebreak "^1.0.2" png-js "^1.0.0" -pdfmake@^0.1.63: - version "0.1.63" - resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.63.tgz#0001ecffe4ab30843485ce08744393315721e3f1" - integrity sha512-TjchyLVDzaEmtaDNUrceNrm0QvNIFERYOeDwHwMUQ1twGy68Uhjd1MKsb9DGAh8SuB8MCWQXB7m4k7cUevLjoA== +pdfmake@^0.1.65: + version "0.1.65" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.65.tgz#09c4cf796809ec5fce789343560a36780ff47e37" + integrity sha512-MgzRyiKSP3IEUH7vm4oj3lpikmk5oCD9kYxiJM6Z2Xf6CP9EcikeSDey2rGd4WVvn79Y0TGqz2+to8FtWP8MrA== dependencies: - iconv-lite "^0.5.0" + iconv-lite "^0.5.1" linebreak "^1.0.2" pdfkit "^0.11.0" svg-to-pdfkit "^0.1.8" @@ -24096,7 +24050,7 @@ quickselect@^2.0.0: resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== -quote-stream@^1.0.1, quote-stream@~1.0.2: +quote-stream@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" integrity sha1-hJY/jJwmuULhU/7rU6rnRlK34LI= @@ -25173,7 +25127,7 @@ read-pkg@^5.1.1, read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2": +"readable-stream@1 || 2", readable-stream@~2.3.3: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -25205,7 +25159,7 @@ readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0": string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -26166,7 +26120,14 @@ resolve@1.8.1, resolve@^1.3.2, resolve@^1.8.1: dependencies: path-parse "^1.0.5" -resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7: +resolve@^1.1.5: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.1.6, resolve@^1.1.7: version "1.5.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" integrity sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw== @@ -26743,11 +26704,12 @@ schema-utils@^2.4.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6 ajv-keywords "^3.4.1" scope-analyzer@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.0.5.tgz#72c9c6770c3e66984f84c7d3c7045998a1a7db8a" - integrity sha512-+U5H0417mnTEstCD5VwOYO7V4vYuSqwqjFap40ythe67bhMFL5C3UgPwyBv7KDJsqUBIKafOD57xMlh1rN7eaw== + version "2.1.1" + resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.1.1.tgz#5156c27de084d74bf75af9e9506aaf95c6e73dd6" + integrity sha512-azEAihtQ9mEyZGhfgTJy3IbOWEzeOrYbg7NcYEshPKnKd+LZmC3TNd5dmDxbLBsTG/JVWmCp+vDJ03vJjeXMHg== dependencies: array-from "^2.1.1" + dash-ast "^1.0.0" es6-map "^0.1.5" es6-set "^0.1.5" es6-symbol "^3.1.1" @@ -27842,20 +27804,13 @@ statehood@6.x.x: iron "5.x.x" joi "13.x.x" -static-eval@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.3.tgz#cb62fc79946bd4d5f623a45ad428233adace4d72" - integrity sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w== +static-eval@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71" + integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA== dependencies: escodegen "^1.11.1" -static-eval@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" - integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== - dependencies: - escodegen "^1.8.1" - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -27864,44 +27819,24 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -static-module@^2.2.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf" - integrity sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ== - dependencies: - concat-stream "~1.6.0" - convert-source-map "^1.5.1" - duplexer2 "~0.1.4" - escodegen "~1.9.0" - falafel "^2.1.0" - has "^1.0.1" - magic-string "^0.22.4" - merge-source-map "1.0.4" - object-inspect "~1.4.0" - quote-stream "~1.0.2" - readable-stream "~2.3.3" - shallow-copy "~0.0.1" - static-eval "^2.0.0" - through2 "~2.0.3" - static-module@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/static-module/-/static-module-3.0.3.tgz#cc2301ed3fe353e2d2a2195137013853676f9960" - integrity sha512-RDaMYaI5o/ym0GkCqL/PlD1Pn216omp8fY81okxZ6f6JQxWW5tptOw9reXoZX85yt/scYvbWIt6uoszeyf+/MQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-3.0.4.tgz#bfbd1d1c38dd1fbbf0bb4af0c1b3ae18a93a2b68" + integrity sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw== dependencies: acorn-node "^1.3.0" concat-stream "~1.6.0" convert-source-map "^1.5.1" duplexer2 "~0.1.4" - escodegen "~1.9.0" + escodegen "^1.11.1" has "^1.0.1" - magic-string "^0.22.4" + magic-string "0.25.1" merge-source-map "1.0.4" - object-inspect "~1.4.0" + object-inspect "^1.6.0" readable-stream "~2.3.3" scope-analyzer "^2.0.1" shallow-copy "~0.0.1" - static-eval "^2.0.2" + static-eval "^2.0.5" through2 "~2.0.3" stats-lite@^2.2.0: @@ -29098,9 +29033,9 @@ tiny-emitter@^2.0.0: integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow== tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.2.tgz#93d9decffc8805bd57eae4310f0b745e9b6fb3a7" - integrity sha1-k9nez/yIBb1X6uQxDwt0Xptvs6c= + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== tiny-invariant@^1.0.2: version "1.0.4" @@ -30321,13 +30256,13 @@ unicode-match-property-value-ecmascript@^1.2.0: resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== -unicode-properties@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-properties/-/unicode-properties-1.1.0.tgz#7a96eef49f75682ea69d2315eec9ac43ffdf00c1" - integrity sha1-epbu9J91aC6mnSMV7smsQ//fAME= +unicode-properties@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/unicode-properties/-/unicode-properties-1.3.1.tgz#cc642b6314bde2c691d65dd94cece09ed84f1282" + integrity sha512-nIV3Tf3LcUEZttY/2g4ZJtGXhWwSkuLL+rCu0DIAMbjyVPj+8j5gNVz4T/sVbnQybIsd5SFGkPKg/756OY6jlA== dependencies: - brfs "^1.4.0" - unicode-trie "^0.3.0" + base64-js "^1.3.0" + unicode-trie "^2.0.0" unicode-property-aliases-ecmascript@^1.0.4: version "1.0.4" @@ -30355,6 +30290,14 @@ unicode-trie@^1.0.0: pako "^0.2.5" tiny-inflate "^1.0.0" +unicode-trie@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-2.0.0.tgz#8fd8845696e2e14a8b67d78fa9e0dd2cad62fec8" + integrity sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ== + dependencies: + pako "^0.2.5" + tiny-inflate "^1.0.0" + unified@^6.1.5: version "6.1.6" resolved "https://registry.yarnpkg.com/unified/-/unified-6.1.6.tgz#5ea7f807a0898f1f8acdeefe5f25faa010cc42b1" @@ -31360,11 +31303,6 @@ vision@^5.3.3: items "2.x.x" joi "13.x.x" -vlq@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" - integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== - vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" From de4eaf9ae70cc225db8bb92a43661b8cfbd4ab85 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Mon, 8 Jun 2020 15:31:14 +0200 Subject: [PATCH 15/27] fix #66185 (#66186) --- ...-data-public.indexpattern._constructor_.md | 4 ++-- .../index_patterns/_pattern_cache.ts | 2 +- .../index_patterns/index_pattern.test.ts | 3 +++ .../index_patterns/index_pattern.ts | 19 +++++++++++-------- src/plugins/data/public/public.api.md | 3 ++- .../public/lib/resolve_saved_objects.ts | 5 +++-- .../apps/management/_import_objects.js | 1 - 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md index 4159247bb7c32..6256709e2ee36 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `IndexPattern` class Signature: ```typescript -constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: any); +constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache); ``` ## Parameters @@ -20,5 +20,5 @@ constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObj | getConfig | any | | | savedObjectsClient | SavedObjectsClientContract | | | apiClient | IIndexPatternsApiClient | | -| patternCache | any | | +| patternCache | PatternCache | | diff --git a/src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts b/src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts index eb6c69b414316..a3653bb529fa3 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/_pattern_cache.ts @@ -19,7 +19,7 @@ import { IndexPattern } from './index_pattern'; -interface PatternCache { +export interface PatternCache { get: (id: string) => IndexPattern; set: (id: string, value: IndexPattern) => IndexPattern; clear: (id: string) => void; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts index e4058007e0a57..84135bb5d1e2b 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts @@ -85,6 +85,9 @@ const savedObjectsClient = { const patternCache = { clear: jest.fn(), + get: jest.fn(), + set: jest.fn(), + clearAll: jest.fn(), }; const config = { diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts index 3d54009d0fdca..84ea12a1f684f 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -44,6 +44,7 @@ import { flattenHitWrapper } from './flatten_hit'; import { IIndexPatternsApiClient } from './index_patterns_api_client'; import { getNotifications, getFieldFormats } from '../../services'; import { TypeMeta } from './types'; +import { PatternCache } from './_pattern_cache'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const type = 'index-pattern'; @@ -65,12 +66,13 @@ export class IndexPattern implements IIndexPattern { private version: string | undefined; private savedObjectsClient: SavedObjectsClientContract; - private patternCache: any; + private patternCache: PatternCache; private getConfig: any; private sourceFilters?: []; private originalBody: { [key: string]: any } = {}; public fieldsFetcher: any; // probably want to factor out any direct usage and change to private private shortDotsEnable: boolean = false; + private apiClient: IIndexPatternsApiClient; private mapping: MappingObject = expandShorthand({ title: ES_FIELD_TYPES.TEXT, @@ -99,7 +101,7 @@ export class IndexPattern implements IIndexPattern { getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, - patternCache: any + patternCache: PatternCache ) { this.id = id; this.savedObjectsClient = savedObjectsClient; @@ -117,6 +119,7 @@ export class IndexPattern implements IIndexPattern { }); this.fields = this.createFieldList(this, [], this.shortDotsEnable); + this.apiClient = apiClient; this.fieldsFetcher = createFieldsFetcher( this, apiClient, @@ -396,8 +399,8 @@ export class IndexPattern implements IIndexPattern { duplicateId, this.getConfig, this.savedObjectsClient, - this.patternCache, - this.fieldsFetcher + this.apiClient, + this.patternCache ); await duplicatePattern.destroy(); } @@ -445,8 +448,8 @@ export class IndexPattern implements IIndexPattern { this.id, this.getConfig, this.savedObjectsClient, - this.patternCache, - this.fieldsFetcher + this.apiClient, + this.patternCache ); return samePattern.init().then(() => { // What keys changed from now and what the server returned @@ -489,7 +492,7 @@ export class IndexPattern implements IIndexPattern { this.version = samePattern.version; // Clear cache - this.patternCache.clear(this.id); + this.patternCache.clear(this.id!); // Try the save again return this.save(saveAttempts); @@ -545,8 +548,8 @@ export class IndexPattern implements IIndexPattern { } destroy() { - this.patternCache.clear(this.id); if (this.id) { + this.patternCache.clear(this.id); return this.savedObjectsClient.delete(type, this.id); } } diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index dcdb528ac8b7d..e6f6ba0c1bd12 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -842,7 +842,8 @@ export type IMetricAggType = MetricAggType; // @public (undocumented) export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IIndexPatternsApiClient" needs to be exported by the entry point index.d.ts - constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: any); + // Warning: (ae-forgotten-export) The symbol "PatternCache" needs to be exported by the entry point index.d.ts + constructor(id: string | undefined, getConfig: any, savedObjectsClient: SavedObjectsClientContract, apiClient: IIndexPatternsApiClient, patternCache: PatternCache); // (undocumented) [key: string]: any; // (undocumented) diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts index 952e9628d2846..79b8c33b84cfe 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts @@ -27,6 +27,7 @@ import { IIndexPattern, injectSearchSourceReferences, } from '../../../data/public'; +import { FailedImport } from './process_import_response'; type SavedObjectsRawDoc = Record; @@ -277,7 +278,7 @@ export async function resolveSavedObjects( // Keep track of how many we actually import because the user // can cancel an override let importedObjectCount = 0; - const failedImports: any[] = []; + const failedImports: FailedImport[] = []; // Start with the index patterns since everything is dependent on them await awaitEachItemInParallel(docTypes.indexPatterns, async (indexPatternDoc) => { try { @@ -291,7 +292,7 @@ export async function resolveSavedObjects( importedObjectCount++; } } catch (error) { - failedImports.push({ indexPatternDoc, error }); + failedImports.push({ obj: indexPatternDoc as any, error }); } }); diff --git a/test/functional/apps/management/_import_objects.js b/test/functional/apps/management/_import_objects.js index 6b40837808387..6306d11eadb65 100644 --- a/test/functional/apps/management/_import_objects.js +++ b/test/functional/apps/management/_import_objects.js @@ -356,7 +356,6 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.importFile( path.join(__dirname, 'exports', '_import_objects_with_index_patterns.json') ); - await PageObjects.settings.checkImportFailedWarning(); await PageObjects.settings.clickImportDone(); const objects = await PageObjects.settings.getSavedObjectsInTable(); From c29fcbb46bfde68b437df790f011c3d745c8585c Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:45:06 -0400 Subject: [PATCH 16/27] Bump decompress package version (#68386) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f4d5409f755ce..e47af2c6fe284 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11041,9 +11041,9 @@ decompress-unzip@^4.0.1: yauzl "^2.4.2" decompress@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" - integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" From f89e911f64a40744b8d88e3221ee8403a72c9e54 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 8 Jun 2020 09:47:51 -0400 Subject: [PATCH 17/27] Kibana developer examples landing page (#67049) * Kibana developer examples * Batch explorer tests should be run in examples config * Fix tests * add codeowner for new developer examples plugin & readme cleanup * Try to frame embeddable wording based on what a developer's goals are. * Add noopener noreferer, fix bad merge * Remove bfetch.png Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 1 + examples/alerting_example/kibana.json | 2 +- examples/alerting_example/public/plugin.tsx | 22 +++- examples/bfetch_explorer/kibana.json | 2 +- examples/bfetch_explorer/public/plugin.tsx | 25 +++- examples/developer_examples/README.md | 36 ++++++ examples/developer_examples/kibana.json | 9 ++ examples/developer_examples/navigation.png | Bin 0 -> 28522 bytes examples/developer_examples/public/app.tsx | 117 ++++++++++++++++++ .../developer_examples}/public/index.ts | 6 +- examples/developer_examples/public/plugin.ts | 68 ++++++++++ .../developer_examples/public/types.ts | 16 ++- examples/developer_examples/tsconfig.json | 15 +++ examples/embeddable_explorer/kibana.json | 2 +- .../public/embeddables.png | Bin 0 -> 88399 bytes .../embeddable_explorer/public/plugin.tsx | 31 ++++- examples/search_explorer/kibana.json | 2 +- examples/search_explorer/public/plugin.tsx | 31 ++++- .../state_containers_examples/kibana.json | 2 +- .../public/plugin.ts | 68 +++++++++- .../public/with_data_services/application.tsx | 2 +- .../with_data_services/components/app.tsx | 9 +- examples/ui_actions_explorer/kibana.json | 2 +- .../ui_actions_explorer/public/plugin.tsx | 28 ++++- .../ui_actions_explorer/public/ui_actions.png | Bin 0 -> 108883 bytes .../url_generators_examples/public/plugin.tsx | 3 +- examples/url_generators_explorer/kibana.json | 2 +- .../url_generators_explorer/public/plugin.tsx | 35 +++++- .../bfetch_explorer/batched_function.ts | 5 +- .../bfetch_explorer/index.ts | 7 +- test/examples/config.js | 1 + test/examples/embeddables/index.ts | 4 +- test/examples/search/index.ts | 4 +- test/examples/state_sync/index.ts | 2 - test/examples/state_sync/todo_app.ts | 7 +- test/examples/ui_actions/index.ts | 4 +- test/plugin_functional/config.js | 1 - .../kbn_tp_bfetch_explorer/kibana.json | 10 -- .../kbn_tp_bfetch_explorer/package.json | 17 --- .../kbn_tp_bfetch_explorer/tsconfig.json | 21 ---- 40 files changed, 510 insertions(+), 109 deletions(-) create mode 100644 examples/developer_examples/README.md create mode 100644 examples/developer_examples/kibana.json create mode 100644 examples/developer_examples/navigation.png create mode 100644 examples/developer_examples/public/app.tsx rename {test/plugin_functional/plugins/kbn_tp_bfetch_explorer => examples/developer_examples}/public/index.ts (82%) create mode 100644 examples/developer_examples/public/plugin.ts rename test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts => examples/developer_examples/public/types.ts (68%) create mode 100644 examples/developer_examples/tsconfig.json create mode 100644 examples/embeddable_explorer/public/embeddables.png create mode 100644 examples/ui_actions_explorer/public/ui_actions.png rename test/{plugin_functional/test_suites => examples}/bfetch_explorer/batched_function.ts (93%) rename test/{plugin_functional/test_suites => examples}/bfetch_explorer/index.ts (83%) delete mode 100644 test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json delete mode 100644 test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json delete mode 100644 test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 48d70910f9bf1..472d29ed29413 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -34,6 +34,7 @@ /src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui # App Architecture +/examples/developer_examples/ @elastic/kibana-app-arch /examples/url_generators_examples/ @elastic/kibana-app-arch /examples/url_generators_explorer/ @elastic/kibana-app-arch /packages/kbn-interpreter/ @elastic/kibana-app-arch diff --git a/examples/alerting_example/kibana.json b/examples/alerting_example/kibana.json index 2b6389649cef9..6c04218ca45e2 100644 --- a/examples/alerting_example/kibana.json +++ b/examples/alerting_example/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["triggers_actions_ui", "charts", "data", "alerts", "actions"], + "requiredPlugins": ["triggers_actions_ui", "charts", "data", "alerts", "actions", "developerExamples"], "optionalPlugins": [] } diff --git a/examples/alerting_example/public/plugin.tsx b/examples/alerting_example/public/plugin.tsx index 524ff18bd434e..f0635a1071f64 100644 --- a/examples/alerting_example/public/plugin.tsx +++ b/examples/alerting_example/public/plugin.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerts/public'; import { ChartsPluginStart } from '../../../src/plugins/charts/public'; import { TriggersAndActionsUIPublicPluginSetup } from '../../../x-pack/plugins/triggers_actions_ui/public'; @@ -25,6 +25,7 @@ import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { getAlertType as getAlwaysFiringAlertType } from './alert_types/always_firing'; import { getAlertType as getPeopleInSpaceAlertType } from './alert_types/astros'; import { registerNavigation } from './alert_types'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; export type Setup = void; export type Start = void; @@ -32,6 +33,7 @@ export type Start = void; export interface AlertingExamplePublicSetupDeps { alerts: AlertingSetup; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; + developerExamples: DeveloperExamplesSetup; } export interface AlertingExamplePublicStartDeps { @@ -44,11 +46,12 @@ export interface AlertingExamplePublicStartDeps { export class AlertingExamplePlugin implements Plugin { public setup( core: CoreSetup, - { alerts, triggers_actions_ui }: AlertingExamplePublicSetupDeps + { alerts, triggers_actions_ui, developerExamples }: AlertingExamplePublicSetupDeps ) { core.application.register({ id: 'AlertingExample', title: 'Alerting Example', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const [coreStart, depsStart] = await core.getStartServices(); const { renderApp } = await import('./application'); @@ -60,6 +63,21 @@ export class AlertingExamplePlugin implements Plugin Promise<{ num: number }>; @@ -27,6 +28,7 @@ export interface ExplorerService { export interface BfetchExplorerSetupPlugins { bfetch: BfetchPublicSetup; + developerExamples: DeveloperExamplesSetup; } export interface BfetchExplorerStartPlugins { @@ -36,9 +38,9 @@ export interface BfetchExplorerStartPlugins { export class BfetchExplorerPlugin implements Plugin { public setup( core: CoreSetup, - plugins: BfetchExplorerSetupPlugins + { bfetch, developerExamples }: BfetchExplorerSetupPlugins ) { - const double = plugins.bfetch.batchedFunction<{ num: number }, { num: number }>({ + const double = bfetch.batchedFunction<{ num: number }, { num: number }>({ url: '/bfetch_explorer/double', }); @@ -49,8 +51,25 @@ export class BfetchExplorerPlugin implements Plugin { core.application.register({ id: 'bfetch-explorer', title: 'bfetch explorer', + navLinkStatus: AppNavLinkStatus.hidden, mount: mount(core, explorer), }); + + developerExamples.register({ + appId: 'bfetch-explorer', + title: 'bfetch', + description: + 'bfetch is a service that allows to batch HTTP requests and streams responses back.', + links: [ + { + label: 'README', + href: 'https://github.com/elastic/kibana/blob/master/src/plugins/bfetch/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); } public start() {} diff --git a/examples/developer_examples/README.md b/examples/developer_examples/README.md new file mode 100644 index 0000000000000..1a57838c43d24 --- /dev/null +++ b/examples/developer_examples/README.md @@ -0,0 +1,36 @@ +## Developer examples + +Owner: Kibana application architecture team + +The developer examples app is a landing page where developers go to search for working, tested examples of various developer +services. Add your a link to your example using the developerExamples `register` function offered on the `setup` contract: + +```ts + setup(core, { developerExamples }) { + developerExamples.register({ + appId: 'myFooExampleApp', + title: 'Foo services', + description: `Foo services let you do bar and zed.`, + links: [ + { + label: 'README', + href: 'https://github.com/elastic/kibana/tree/master/src/plugins/foo/README.md', + iconType: 'logoGithub', + target: '_blank', + size: 's', + }, + ], + image: img, + }); + } +``` + +Run Kibana with developer examples via: + +``` +yarn start --run-examples +``` + +Then navigate to "Developer examples": + + diff --git a/examples/developer_examples/kibana.json b/examples/developer_examples/kibana.json new file mode 100644 index 0000000000000..8c9ec2e4dcbfc --- /dev/null +++ b/examples/developer_examples/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "developerExamples", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": false, + "ui": true, + "requiredPlugins": [], + "optionalPlugins": [] +} diff --git a/examples/developer_examples/navigation.png b/examples/developer_examples/navigation.png new file mode 100644 index 0000000000000000000000000000000000000000..ac72f697e5db08e792b6ab5308e087de037cad47 GIT binary patch literal 28522 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 void; + getUrlForApp: (appId: string) => string; +} + +function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) { + const [search, setSearch] = useState(''); + + const lcSearch = search.toLowerCase(); + const filteredExamples = !lcSearch + ? examples + : examples.filter((def) => { + if (def.description.toLowerCase().indexOf(lcSearch) >= 0) return true; + if (def.title.toLowerCase().indexOf(lcSearch) >= 0) return true; + return false; + }); + + return ( + + + +

Developer examples

+

+ The following examples showcase services and APIs that are available to developers. + setSearch(e.target.value)} + isClearable={true} + aria-label="Search developer examples" + /> +

+
+
+ + {filteredExamples.map((def) => ( + + + {def.description} + + } + title={ + + { + navigateToApp(def.appId); + }} + > + + {def.title} + + + + window.open(getUrlForApp(def.appId), '_blank', 'noopener, noreferrer') + } + > + Open in new tab + + + } + image={def.image} + footer={def.links ? : undefined} + /> + + ))} + +
+ ); +} + +export const renderApp = (props: Props, element: AppMountParameters['element']) => { + ReactDOM.render(, element); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts b/examples/developer_examples/public/index.ts similarity index 82% rename from test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts rename to examples/developer_examples/public/index.ts index 547dfe2aa38d2..616fb011e3705 100644 --- a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts +++ b/examples/developer_examples/public/index.ts @@ -17,4 +17,8 @@ * under the License. */ -export * from '../../../../../examples/bfetch_explorer/public'; +import { DeveloperExamplesPlugin } from './plugin'; + +export const plugin = () => new DeveloperExamplesPlugin(); + +export { DeveloperExamplesSetup } from './plugin'; diff --git a/examples/developer_examples/public/plugin.ts b/examples/developer_examples/public/plugin.ts new file mode 100644 index 0000000000000..a0aa601660f37 --- /dev/null +++ b/examples/developer_examples/public/plugin.ts @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + CoreSetup, + Plugin, + AppMountParameters, + DEFAULT_APP_CATEGORIES, +} from '../../../src/core/public'; + +import { ExampleDefinition } from './types'; + +export interface DeveloperExamplesSetup { + register: (def: ExampleDefinition) => void; +} + +export class DeveloperExamplesPlugin implements Plugin { + private examplesRegistry: ExampleDefinition[] = []; + + public setup(core: CoreSetup) { + const examples = this.examplesRegistry; + core.application.register({ + id: 'developerExamples', + title: 'Developer examples', + order: -2000, + category: DEFAULT_APP_CATEGORIES.kibana, + async mount(params: AppMountParameters) { + const { renderApp } = await import('./app'); + const [coreStart] = await core.getStartServices(); + return renderApp( + { + examples, + navigateToApp: (appId: string) => coreStart.application.navigateToApp(appId), + getUrlForApp: (appId: string) => coreStart.application.getUrlForApp(appId), + }, + params.element + ); + }, + }); + + const api: DeveloperExamplesSetup = { + register: (def) => { + this.examplesRegistry.push(def); + }, + }; + return api; + } + + public start() {} + + public stop() {} +} diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts b/examples/developer_examples/public/types.ts similarity index 68% rename from test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts rename to examples/developer_examples/public/types.ts index b4370eb53311e..0ef359ef64a23 100644 --- a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts +++ b/examples/developer_examples/public/types.ts @@ -17,4 +17,18 @@ * under the License. */ -export * from '../../../../../examples/bfetch_explorer/server'; +import { EuiListGroupItemProps } from '@elastic/eui'; + +export interface ExampleDefinition { + /** + * The application id that is the landing page for the example. + */ + appId: string; + title: string; + description: string; + image?: string; + /** + * Any additional links you want to show, for example to the github README. + */ + links?: EuiListGroupItemProps[]; +} diff --git a/examples/developer_examples/tsconfig.json b/examples/developer_examples/tsconfig.json new file mode 100644 index 0000000000000..d508076b33199 --- /dev/null +++ b/examples/developer_examples/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*", + ], + "exclude": [] +} diff --git a/examples/embeddable_explorer/kibana.json b/examples/embeddable_explorer/kibana.json index 3e4b074fb6b12..aa14549d0460f 100644 --- a/examples/embeddable_explorer/kibana.json +++ b/examples/embeddable_explorer/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": false, "ui": true, - "requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples"], + "requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples", "developerExamples"], "optionalPlugins": [] } diff --git a/examples/embeddable_explorer/public/embeddables.png b/examples/embeddable_explorer/public/embeddables.png new file mode 100644 index 0000000000000000000000000000000000000000..90b287962d24823c6717265bee595c9b746ba0e1 GIT binary patch literal 88399 zcmZ^~19T?M);1h_Vw)38Y~HbL+Y>vP*tTukwsXgriEZ2X=Q-#6=Y7}uzOUEnUe&wy zRkdr^?(Vv(s>2oJ#J|De!hwK*e3O(AQ33%0jr*G(gMs>c9l>bb1pz@2vlJFqkQ5ds zR&ca4v$Qq^0g(t#R)^M59>>hlR+6;vhmnBo4kwjJMHPx9CRVLONe~bUCm{)2P8Nmy zLr-cTtS*YK3s3BNZmM|xksmfO(VIL)*2%@8={H%oAz>5V)~E^gP}kor##}XfOq+OW)L|);zYPGkU2S-{~*) zIW(99FOWEmumwm*ANnC^kiN(lB~1W`h+SE0P6~tSZa6aDm_Y!tbTX%MbYwE8ZuAco zc321eFgH+vk{kC18IY#Tyd2fOnIPU(H)7Xhm?2aU=P_jHYf)a%ArxdJ;8b?S&@3Wn zk?&~Y3&-@mpd2BC6LO`MA}p5(ZR+85$ye^h^HxG;A$FJ{G_)*vWSH`Agf~}8&!0u8 z3&fWxMdG6ZRY*7D^#Z0yOi~!sq^L`eWz_o9{(RsvVY-lAaS~y&B#4RoBI1_!Cpf!| z^h5T+dBMA0wgDq<@d|{vwL4WQveA(^eK^dB+I!kFiRHUA7xF(E&{l9ic}Pjz{JwPK zsYo=lS;Xj&DMf{=zA-815D=BO^jk-WWF3RzrIX5~_kJ7d5t4MT%OJ|kB7u)wSOz*C zfIk>TiWu28L8%``qLQ-mc0Ti}eu^P;z*3wKffD9>c`XXiff-7ab-12aC?u4XV_Bsh zLXTQ{ZAmy8u|0CCrlHj=Y71J^FogYi-UWTJ7p!=>v9R>94&avO<%{I_u9l6Y2U7q7 z9Rz}@&=-$b52`hSDZh5Ytq?_bQJ2&wYQ7Bb2#M)XYoG0+@U3`y8rv(8cJq zA!R-MGD;OnUEQ7rnkx=Z#ZX}7arsY-Zz7S(2Z`T2hQ7i^7lu?^l`gf8Ru5i9`BE8Z zReFd(%`2QcWU{Cy%)^!F}==@lGiRE4@apt7!6>}i0XPUesIp)Xr>60{%rPs4{f0G+t2|5 zRM_A~0$>uLiH517_I$uTRz|c*aEd$xT4*Z)ie;DIs8$C;hD7rxiO3-Ogii=+%t?a%w`DDh#_2r`8!wQyCf4i zA$6{bke2wWD7RdP*eSU;xm7&fnC(8JBaNm|jyyf(6*&=^J{dpdlcFb&2|GYUisTR$x=H7#E(q8CuDrj{yZS1Vog z&gy?tW&ozbNAhKhs4b)|*DNV5+$`-*oI42k!g8c$N*Rj(%*)Ra&GG+aX$rM+YKB~O zYN~9)YpS*EozFjBKCN^4eujRMbawuyyi~iaB7?JISMkWI*KJq4!pp>8}tt`FmKYJ zRj^VpHos?wYLI+TXpm;GFyule%5rp{*(nW}_LkO~mK&P(%@7F%QIogGXRMioC@KUO z5(hR~D*6$7gZ0&Vk{OY0nqALu-Qs-~g2RCs$DDb}Byb!j@iHGu_#@;tGzD>k<35M_ z=cx&JxlotccZe|#-Yp|@g42m(&6Z2CoMK6D5g>566PqKk2y}m zauvQBt{fRbVRO2bma%)}3;LEW3n6Dn5{0Z ztTz@f|5}T%{m$mIfvdjVd4 zy&k?@-hC|03^T0Rm2O4OJr)KRw#Hmux85GT*ge`I89)|6TEkf&H6iRn{)Pa+HXx!x z*uejqb~Q1tcdOTj?1TaaAVK+VKRcQ^$ORdY?2r_bROi3uhvz5f#|~5tFb|#%{2JgJ zFb~QNBNh=6s|stIV%xiIZ(h$~(la)T<|2@cjEb#_@jv$`-Db-iV;pH z$t{VN_%jhe9!g#-V@t$u^j>w7swAT{L@irhv{4eV<(U#6GaoR5nwWEVmEU+BRRd2C_OC;+!=A^S+{S@jJ zlH55skz2r$_tisJy9Xk8LHUXSWq|2-W`LaGoMsPpW*%=>ApRNGuoL%D$jk;)EH{Ja zrrn+HQEk?jt>A6Ip;6;*`!E52@Ry2ufiCHf_U>7`oe|Mz(wl z`YogBl<%eTGSa-*9MOD%p@<=SX8oH^|EX9>#iNm??WcUR<-_hN(i!AgtqshoLhqcl z@yf_d=1}IPHR9H~g0Z@CPjFkY0kE-JQhVlO_e1)EJ<-N=t;aUi+HMP_bSV3`g(9JR)esd`=`6}3e#!*uJOlcWXw}eT+T5d z-J_w~|mOjREG!v4|=Mv+6`Rws^CrYrcEoV-9|g#jq4jhe>Rqz z(`8SOx_#}{=Q?x{bTN_(Q3^k^x5^#EsPBd4lQsV<=l;zB<&FHM+cl3KAesMTUh_Tb zv+BKl_-+-;mLbMD*hTlld*CXYp_t*VL;7>}t?)H@uoB!C>cgYm+)Mt*YUS*cLwXap zcjMD`u`^|Jc~kt#Y3s?G>cisQVU+Sp{s=(n>$Aym0e>PrtUg*hwME*?cRX`(m3nK! zyQBsZ2tG9ivOVPra!d`vSTpc32=TxrPZ9QNT_Lp1Z}_08K#>EoIGE|!In4#yr^k67 zK+J(ZQT3>~3Ri>%{HONBS=f?!W1O#0;dw z|59o|}Qe&CQM8 zjfLLM(VT&ai;Ihak(q&+neML!os);Hv!OektrOY53;F-c5ixZ#cC@s2wzRV){-<0+ zBRdyoK2p+uD*Es7Z#zxhE&rz`Tc`gt>u&=Y{>forqGx3Izrma>&Hf*-e{%i}`&YaE zT^;W~g>frbx|>>Sidfp1+B*F;4Zy_A%Fg?*dj5~B|FQIcK{fsd%FN8p`oE$7E9-wj z|FMNz-qF(ZubcjH27rl|;s22RN1vDBA5Z-+&;7ew{w4j}F90}RhX3|40FI9~@h=M% z0Fe|CRB;DA?{-Vd7I!6b?|gHa<|+X1h-Z-qfR!063tuFU*rkeQC6i7$Z`6BvK0mE$ z_B?lbj9tI<$ zIGX8!e)L>*x}2#kRabac+7>sJkC&S&w$ge@4(9SvMC<3kGs2R%W*9+Vh2TLw4zzi*CL>-5VV>WqtWe z?bZigx4h&AA9F91Qo(@O@txy*qia@5vw*N5ajgcSSrThjj{N--$^H|jTW&0d7_j12`)abhccw-(1-UEv0246VKpU|O|@Fg6GhyBr^fA99q z&c=zTcnXBlPESVC(uEh5j^EBV6qy%Ei^-)7onfiUv9C%X2gY8_3e1QWEL(!C9uJSQ zDV-7>v-V{EX4Sb@1+pEo& z@&Wj}0WpQAKFMx`xvVwBUq7w`nx5@@v;nByquZ}u5i^aWP9Zl7hWBU(f(t}wl1*8( z_}v$}9@4K9fqg!?3#TEC^-()4b5?PVfM;PwgU8g|t^Z9Ut zZ@zleImh?RwX}Kh-f>{}qTNg$pP*sC=Z4jc^mjphiv77AzCsRV2uTep5e7|wG=4(@ zM8&?!^dWu2yN(jvU9kEz%+WD<|Casg(UeNui8jZ?tM4OfIBuG9;Eyu0z_uwXFW-qW zwtJtI^%iMv+G4ITPkNHTp8I{O_p5GyDK!)$Er@nI{!;Fxu2i~#g$x3?J`k{ol|0s; zqBbret*+6KTbJi&0L#+RqwnQD7#;eyWn%QJBhr^m3&B`s2yfGy=F+?Q`o>Z+$cppE zXM8&a?5_1{k>#IGR>BWE0y&rvPsTVap4;5|ai!bicQc%DZex)^x73`G6^X?V@e>3! z;|eE!IEEyhC_Qn6|5GOFRzpW80rK6{T-1b6Bg|GRW zQbOjBMyET}a1)6T%1LptQF3_qmlqGFuD{r%s3-(YKh(dI7eWz&Fn}=e5Jkbs{zuGy z7)!*PG_wO19vFMdg?u}}B z)6>&Gwcz03jNUOZF+p<^b*`xo{u9Lyjs~u_pv zmEwn^BgIC_mFU}eVG+W?iJ+i%ws2srALkz9j3y+4%n z)hok|mRX}r4i67mZE4b^Q#(y&E-w<0b^u6_3CZbXlyX^diKWv5^``!=Ev)S96GKBm z`-cYteMbW0CS`Tgenz@o>qL z%gf(a5fN~RZb{S2%V`KmNRku=DnSSwG5454<1(_c(y}(uDynK0IkN3INXj$+1ljZO zA;`f2Go)u<$lj=Y1?_61dFuP%9fJhje&__Al-<}a#hBrElvWI<`P`qa((FIVr88r; z@;lh()B{+^NYL=`&+eD^4mjQJk~FCcx5_p2=f^i31Q*vNrNW) z(?!enqf4Is#ghdExP#kCmKnkFhw=Nxp!2O>{pprgpfp48_ukv()PojnyX3DU`LAf+ z>%LMyUnzRs^Ca0!4r6q-SXOySbMshqEUXEr*~`<*_<{n`lQw(F5GZJqt)30X`(uZE zCEc{#!7y6=2x+N<~*5YpuO0+Hsh+SOBF0kgP zD%nAjowliZ6r$+ND~VWo&#E=G=U zaHb+`N9$cI9!x1CBTydiH??K$QRyWEWfc{_^9j-y*4N366$q0Rl+JG$0yq7^Il!GX zJ-<5LE{Jh-S+AkLp555ij{r}aI0QJVvH(ubBFmowfb#Ail@&$JE`|HV7r7tjrO7^! z^IT1)*iuy@P*6}yBCXzQ=t&CYF#{bP9mV*Zv~2|iLg5D3Y4JHEWD1Td1^B<{4aPLU z0V@7J6UCfy|9Cc>Q{Y94%iOFqWA&%U?RaUh8MT&PMP(&hnlj_}z``Vp93$~8G8`>^ zDl{~nQXxmO^oGI;H6JZ)iZ%F1jECptu=8ORQT8gHCaDwVO;eaBB*U}UxnS3EqgaB8 z?eGgv6K&P#iU85oifDdgT_Hvr=aM-B=Zh_?Y)bx-Gs(4B%7>hP?C00UPa7~Q zUC&y6(RGb0=Ut9J=&Bk=k|R?vJ2Ej3VVvH0>_3>Y@wfW+*%72RXP|-UL@lOZ-7VK#&0CAEhXnF)3upSogR{qCqw*mp;qv%zcAL zh3q45c+ASlg%V+hU|j?QA?>&U@xqEO@!i@bD=;sNy?VTkXk5|39YO}wJ&#R4y1!@s z`XuNVk5_(xgULr-$=GorANV$>H+p4K!HXMfRLYyy%UP*au^n8(KdtF4!_Na7_Xpcq z2hScS+Y9}gQiP%$EEhoC(Zcr3Kv0GZ7=P_rFJsmUFeeft94a3WAOY9rJEqrZBSt~f zSV_aiDrJZ=dnS+BK^apN(^JztCi)t9$LWcrc_oN_Q|zIO`H>$_6@}DzKVnfq3`R6V z^qHnY6sV3_ z$%>T%i}Ke04%RBSK};FNF~2VxOLF>8>Q5N$I&N|W@CQ;1aHQ?Dabm}6yGZ-e$1{5n zCEuNN2vFPn{Pla-I%zi-M$l+)FHr%p|2yAy+K5yVw>fHqM}&Joc(1T%A+=0YBiK*s z-T}r5T6p6uZ9!hh{|pTQT=?4&N^JV+=UNn&a@+bZX2Kzb1B@&y5maF5(iH5R%akZ` zQMCu0x7(;%a`P6eH-nQ3f6__oA!kRv|Ml^ZaM(V|1xIeIFklo1FagdV{Tctrg^Ar=!Rx&`80W4r z#q4||cco{~P%u%BzsBhwf`jaUbImCgB`#*m$qHhM77K3li~i-~b<+3epxxc^s3~QiA?){ zy}iWM$7OH|vjHunqHlQ3BNm1RBCh;jx zQ7I{xt`Lo-10yFQKq#}Weq1v+T7FtmR}|Eh+lYYa{e{=TM<6B}FK)F$Xn1Q*hoo>Q z)F{s=Ut?w_T*8usolA)2IxLAA|MPhjI<+2}TsNZ5D6fK-NIJCrL$40OMiS1E1*5d2 zOCT9%C+e#F&uTH$ja>ypJX#nFm?&p7nWC+r4_v(5AyW=k#>=AoKOcdjzwa!+Qz$$T zk>!&T`vXlC0s^du@o&F)%?8y;BfxjKS-I(gJyvc<>ghH69BZFeLyLPz6*;dgX;I7fc&W|X$Ef?* zinN(S(g;ku#tQG4jN9>c>B1rNi(|tp0Kf+qB<%9L9E+3}+QPbNy2loLiYSXYm8!}9 zJ*w5n2Jgw*=cYC&9XHKl8#b0Ss~;`=IOkfa_g;$thb$Md-PEG1jy`Za9y3jgnLpT` z{z=^*7KEq6{s%)6XR{ocog>L$%27m4cL?z^f%gP2XAwoj!;l^ErXW3{eqb?p+36-b zGm*rUf@@w!nU)V6tpX;432ddcTg5vN#Kjqal6?j+#7wI7pq0q~U6JyH?NGggR zx6Tj3fJjBuS~9Yvzs~LnJ!1?YH-`*ckOajOic?{G8>kqbkm(^HIHN=9gt!!b!;0C# z7`Y$btM+5skSUb6qe}y_+*uiNY0dAY2#UYtq3`+ZHsg1L`?Tj9_}RHT^cw@z2Q`7Q zL1Vbv)LT)MRE;LQh`@k;4a`4Dqp;7guWI^r`|=C8w_z`Z@VCeV1gwvLj)K4Bs-CZw z<1T@t^=_Wu2c8Tocd8GM7PdWkZ42yn+fS!kHO!wMqaIxJ$KPQ}?=10Zo)FGZ47B*; zEciAA&sqcPql+{%p(HECBg|bG%e2D{d^{WLtOj*9=4l}us~dqe?<^7DA2n^a&mK%(T-;ncR5jiigLis6#bP6+UqcR-zg`HE@^W_{C}dBJY79XW z|3n6If;NDlouq|ojNGoa1C3v0fYWhRP+5;KuTH5TgA8( zPReS+@qi=lQ4`Cb{%4_}KZe5lP!yNRYYvCWJ=ORO(wcJ5a8rwKxNPIy>bhjouZHn~ zV^xm4jWr6)j0}Z!J2=C@DmweRPst*W)tx1Di6Tz%L+DZyLjx%mqVkE<$$6c0@lDLv z?*s_&aLm9v6GLaHRA*UYc?mg>8WTf@elSeTNjEeoG%b|F0NlvrEBG*ua@3oZN!)@C z_=D!{i2&js7@kkLn7o9c_PcX)41g#kCkczmv`DRifv{lu{p5F%mm=d!s~S90ro$U- zC;2uEYL4$Xjd_v5Ni~=Icb>%iQ_X6iry4fYK`d zyY4V7cbtQ^tKy2jo3!^JAl1~|ujU{O_ekS5Yy=m-rKMX@e9u-dv_qc5^y%w&w?Ive zUEC=5)A?l-|Ci(k)EVbRPb=mSEBReKx}GG@$deu7i1 zzTK0#nXD_UFI!I7+_1C@ci109SifdtG-782eVsoFYcI1B@R_V~jlo7dGK;jTL@?t4G*(CwPigqTPvfM0CUSS*Dq{-kt9gNgk zzKHY_-ZS7d2NX{XWm&;sb5sOSvR;=zPEpeFa1-Y*qF2Eg)|vz{1&!Of(VFZ#i62dp zTG%WYT%ONOHHU7uqpDje>8^;016XVYT%NQ-SAUh%ossP7UN5@LV_QtZEt#Z6M8 zvId1f&eP-vNn$AIToN?neiR;cnz*7v^BXNHtn2((;Z*Xu7mA%ki1}GeYg-YYHBXAu zx%gw@gDgT_L_;$_O8-**V7v;0O-3 zby;QHi9bs5xLUTlDoyaWWM&ptdgHK6ve+`^)ms1XA$nt-XqfmqK||{!sQAtEqN~Yq?${No5ZI?zCg#D6Stz*rxr_aV@;ETqlvK> z+jIgPH(sc%#>l%uUNH(9+wuo@-YC{0Ns{MuB%c_U$8i+~gRsK9d?~n}6D9>#AYOzJ zFpSp?$i9hvIKAto?)&C*Q21*^*J%$`$fhXcQ)1lT6~uvk#ykZ_rwv{d`6>01)M%;2 zx?(TRd@nWUpIbe&nxYz$^+HAZuuErwRyMd*-8(S{ID(yo;&>hRd#%N4N^-E-n)i`5_2=YNTY^_x=SojYX+QmwSd}GFZLK_A=Cd&mQxP1|( zf5oW2)?~qD+AmARWrM=Z(=8Jn!Q9GzuAITH{I2!t2l9r-$tJrQ!6!RRHia_!w;>*b zn%H6%2Noc#pd3inerNBKBB9-TS6h7#N9$GblZNhTp#4lyLSA3`@5$@;S3cSibDV4< z%!Wq`V5ZIxrpG$1`{?^+#1GxRb6c90Z~W(Rzh~AvXyjLx^REvEq_2SCUk7guZ!@`Z z9pG!WoNt&V=B8lZ>LB2D5|63;%CfYvV*+R495#(&Eu284&yxc+aw(%)jo|+hs@R z!00F!(59;j`&4}m#QabvgzZ>D2i3BVKn$ZnK&f$-C<6Ki7p=pN2C*4oh;}JL;~_jL zqqKx^rd)z3R7Z(J5U52K#J}8s0iq1CWef!%<>%`~(%;jvg^V+X6<;*~He|gZ%0xj9 zrQWzPriXRhbZERwdMFct?;<(2VZxuwgXV>9g~iLfFQ)zI^{qG^zgccMCL@0k zW33O|{wO*bmCa-mlh~&`JtAdKo8OSwwD7n+1#3Ygw{dW#SMw*{2$$oUeYg4BB(a+Sdk^A$xW=A8jlQaNQn=phisKp?g-Dp;rbKqvc;3Ey1;mw3y8I@S4b#V} zBB+LJJT^g5}H?G)Bb_othi^=bPvu zdMdk{6YH6dMSFDx@>bQ2KaYb64icUmr$DBa!x?OnWY-CxVoY`zMlG_1GyeLJL4ePX z$|Z?U#cq{aMrM%FmY}op%%C2#lCK82e>} z1492o@5BC%H}|YLz5j=H5+46k-P$Tf)(UKsLPniePk_TfJjskV=OyT!10-hx{7IHY zRL?Zr=NR=`hb5zwxnqaXyYk*jSb*_!YV;ugBaSj-E=sICUPyg$XhfQ92{Ip zP3;gf1Y$dgHySFu9KMUd3B6UXrN#E1aovia9;2eFD$I`W$8`RfoMvni7i5{zAe9}n(M_n^~J@7QovbU?#pxGKw^?q#5fXGR}y3bqn?R4jrQM-5YrdVob=8w0ht3;x&XG>KT-uom<$*8EP zvA3(Te6xG4Is0s?F%K{)yB%@5r>+;P z_CwgpT&>o~KF}TGuy>SrCr}mYvr7D3TH*&_lHs|P2H6-rw3F4+VQ~9rjHy7YmL{h! zjHSv1D-Hg8^&#!Iqg-$85CA7-=jYs!iiR>WLRK5`h3`i|lh;9>%9UjxaX6fBF%J0< zO?)#r;=+oPS?$a2f*tU3>#Zw;s|0G)x#9QjqWyQfgju7jw|(EQa_hILP;xx9BFt2V zx#%1u=_GSyy)3FjUWtwBG#QaD5?Ki5&D)dCWqI@6y!627+bOqip}a3Ew+>M~JFkWq zA-Q>cJ5vu8<4+Pjc+@z$yRXx?4wok!ocC&Qso-%qk}Kh$czHf4-3 zeM~7J%N#{C%Q1red=lE3YUf&=gfU4)kWqSx7b#UvPQgy-32RU2l?XdO(EeJs7F!D@ zD?Ob$JC%n5Epa3ii72J!l5p~+)lZ=B3ue%Bt#Eo)e5vm(zCosat4pV| zn+uRV3rtZ}cl>z2bL#_;Bv_-z|Lb(0+S1A@ag^}OWu@1xb^quHSPV=(*!o<_o%lQD zjPG;hT~5WIPQlE|8Z_0*(okNls{HnG^;P{oV)u@VK4Uo4EQ;9u*}?nuE`8hg@iZ<+ zMV~QQbx!N~=WJY*KE5lN>{8@L6Yuc-clp5XL-R( zJUI8bT;yo^-$$5Ezeo8D4Bpw+ODe|u%yPVvE^$h~=g-o&UKGEAgWLy;Uk2!g+QJ$I zO&>x78q|EDjdfYuxHve&Vgr$75;zH|xs-U?8GKDcwbnY$VuRiuy`Vx%J?t3!_w_l& zp~cn70mdlOBG8)vy1h^y)7MJl3W4P_zOTSPCbtr?CcG#JX~c|Tov%q|C?T0u#8npD zLVmNjXQ$#wr1G}`hE$$tc)l-ryG{Hq5+&TBCcwR?;6as|Z38R10V(QY$ehU-xl_2p zn@b;+2_ve$Fl(=SIaR`_(8|B8csCL)K$J0?QGap}on{k3ja+ej_WKNm@OgX%D@-wh zG-xTT(`VsA^>M@ww72lOJCRSP(*FA5;-qy{HV?{Uv!CV5_E`EVj~moJDmi1B^m*`8 zkF!ojuVd)0obiJuyI7vF!ahD^hDY96^&ayQ6Y<;EzPig2g2(idv$4jm*9) zziS$TT<5co?QKm|Gk_=Mm3Cw3L*w;fk+;W-cslKtwiisWEE^5Fwry^; zsJhirDmBCmN$>>n-Qg53#vKv^=n`8zr79lQA4_is&%STrSsy!<4R>d`DLX8wrpLAN zk_QCZSzv-Psd`+bA6q;$(O>trHkGiZDV1WYHSHA%aO4MSM*{A}7ozKS+S>Y**@c7i zYg-J#kdQwqb?Mru?bX0AEFprt>n&n4r1{jHFn{7oTP@`ENnKedHt`ago5B4hD89uJ*&fmro^OZS=oJzFhK96!G5ZxzC z_jr0f!*twt>h62oWg^_H<4$r6sW+7vY;t>91Ve{!SXi*k8fWZ$cxg@K>u~m>2$qJB5ojS+RL~s2>p$Zw=bv!8 zU(3lR%8l$@w5-{x1`92uWOMQbdf<+D59;LQHW3!LcXp^p^B^-~DIz&mMi#$wa7+wA zIzPs-(Nm>!{-MO{bxfsipRuc$=N9^vnEG0L`1A;T$Hsi?a}Z(v^`4-@+o2ngQGpR6 z)zlCGaRMBAr%!z>U0=ml7#~b@x8%6M$C-!i!(CRRq}+hYqJoVG)fj~mmIXwsitO|K z;4{2rkfrpatZP5G&7?Fz9+c&7wmi#xDvS_!rgK9?RHj|l2-X|znnW<(8cMgN-UM4yt@{JZDrZ z>frQ^{T>6xlQ#P4JUPbKUHtpZ6Y%rv;YY)r8$TJJJVF2Kl|wPdt5~SY`ucZ{ud7(A zSdZgt59&>w+HgXTr>=d$6>E=kKzWy2rcB|3Q4XFgqTs4U*gy^y2bx-7b6%=Y_LSoXv#YWuN zaX76wF;pCY3vGLEor-Q#rMNIGb5)kb9u)oP^=#Dfx$)kR z)PJ~^WBY?_3l?*AN(B;cPLHwA3xcutlRggE0TvUD?CB=t{5m|oFnd+lH8i^jVGzo; zn~fKJrBM}SivH#U$G%hs?V$862d@{531&#@^7&>?Y zVVPljH5UzYUAO%d9*5P4_;)c&l5WNC|vzA~MQp+-#Hl-RhW&mt?c(j78^3<)BJB@83O@x>^M<4g;^U6C3!Z;E0Cf z*aG%ext^GuPXqRwxk!dFL|$b5k&T2y!L<5lu(%(q?(8XH)HS~ma#0rZa8PCOqopbD z)KhU$1Xq+fW=u3S%=5A=0rGgNsdcbM%$-AB&}i;{>%SzS5jr4H@>v~ciClR-2nPtI z`H;Vi2YCcdguMxQrM8)PMg8yOl&-;5n}5V5d{m-rR;L=B3H}joSfHhbGWpU2eR`_oJ-|@Jk`1=kMkf*BFb=gL1UtPTV5c%#Tys z70l`AXrl!uIh$~r2+6cIdat9njChZE5nJ8xOH(6VcEGF0At?&??qipW&=!BqucG)b zg!duZJvthC)XzCCweK{W$?dyK4P6&kpP9G^)o6bSg^A+j#vfw4-R@dn8B8gu12cSv zJo+*b5)82*3(h0KgT8n$%KQZn5@u#d9(UD8@aozl}jM;JKV^ZA7# z1Y7S-a1$regG!#EvY3H{uEi(nE0wVhu>5?pPoe zZk@k3q)v4XF&IEn6uO~h|-dF4VES$#H=WI2#B~= zX)P*$bAe^BrWl?!&A}{ad)lXFy+j__*DUhNn4>AVI#t%}H@b(RT}9<)$QNaP2k3*O zl`{l+dYJiyRsL87%(%68^cSn^>Xw*#V|sI%i)epypO+D?hL^_ zD>4|ly;xgkn3Tl}+Bc(ybN`0G5Wf^vL9}!XSj@U>gebR1Zg0q#sNK4qTxLv0hBHcsF1v{0*rlogS9b2cY&S zvp1G!FFIvyI&995p0#5&TW?j5HS#`B9ymO66wsghR=DEtu{Q=#N9?l3k`C13>XJX% zI8djBk+HU^(qfK~faSh7{HA|;J!MYqwy5{g_I9dcvP3AQ&HEY!ueUPD_oxSTAU@tg z9XLr)h4^EzrR}M7>7NJJh_Do=smxTdhaokWIpXN}e>sV9ht4h6-+6nPY1*!!$7^Ti z2bEwIRl!P;Oa9Vyg|8EK!oY{*28JJ$9e@Gu)>+^J+&-_qNS53#`s&F#U3wy&^g5OY zD)F*njnd5nF=gQpGRK17AwE~ouy3p@l6kGCqsH-W$KXfYZ5;S(^m0by;izm?u|2R) zX79Pf)>zEzc0(gVB7*+VsT<^&L689@uYXgc(JVMqLua`=-@zy==|u!%itoSsdP>@U zHQHI^(CPYQd}C)~3irl7Vro_N8^6V=Lp!oGT-<7?*9?IQW%Zd>w%>xG?5?b@Qm+Qv z_AGeZ8@-YAZtZH6v$qX&@*BrLLs=sbuiS5dqfUZV=W8IWyQt@LG{NO2uJ_g=*wggj zjJz#@K7brM^~;oMhb>Yh9)>-oxf9LQg8=k`Oqq; z(R{A74u-G(7*y4lAcjmA9%4u^=1Hcbb@-OsRL(lUNM8gOxXm_Xy6WZ69-FMg7beG)5&G!-Xk6QI4I=Z$;s+(^KyS{-=bv);IqDJ!ldJ$pXUm1 z659sy8Y@EH4g=>78CNy)=5=<1%^9Zzxg7S{BTH*Fwkz#J$naPEZx6NtVhA|}V=QVX z>Z8&y=~oB{3Fn>y0-SLOaZ&W>WPKb+;EyQVCn9GuGH9aJ_Yl&E^r2D+2b!aJAztgV z_HtAFMXpU4LzuK~t{$f-v*!%F_Xh&T^A%5g-;&TjnIAj)SmM_W`F54uoie_qDand0 zFT6j%k`v{eRDa&@Fd23sS*7yU#rx{$>>Je7guX9gVa0hAvgln)mfH8!?%El$3S1lS zc&FXQ7J>Z=Nn}NYLYy~rP729ozXOTlxn`5j@s+{vJ7B&QIY7^-lbC^9$iWQtgvz@1 z$E`#n{`Mq0Y}6uyVN}LD?cwit-jfzd>_bcq`@S3&NJjFy;A|XRg+h}0EVw@nER6Ji^rF!Dg`bJ|(|i`-V#^TT~t6 zoJ5HG4FV>ZS{}K2d9&(7LPdano7*?uWWFxx?H*muH_nk05l9meD89FI!j=}fc;@TC z!wAuKXWSu-bv?{P<(F^-ii81%pHdk4EgSqEyV7o>;5$8B7&1qnlc7$xUz3Run>98D z@_-l0@f}(r@Fo$f*b7h2RScKbZfLjD`;4ayck-BvQ1Jj8u$5M~y|!%_0n#Ql8V~uT zO(zikca!F+6m>?^1XF@xR+mu+A@Gz-D8FFm?@tt&9CNX3K2hf3a~ib-NV^}JUi;B} zdmk^|+!rDwagTW*W$wnO@%e8LP;F`)8gQq)X%u}Ea%k{Hj8lZR3K6t7esPuhb9IwQ zjzeCBHI4x}zx^F2$Ac|l<_VG80-#Sw^&(&4ghujeGI!2I?q>^ZOufVJ13FM(V3Ehb z%uG!|d5#Ld6X(F}_gDGRxV9WKE6higbB4_bix<1-zn*!dk;0-LT<^jy4*bLqDZB9q zdY-~I79?yr!Z6#gGV!B`3?;+!brvoF)G%x8QShaLOg|vx4!t^4#aZo0B=2s_Awf|2 zKN}~}YmLlA9s7pj?E`n(K;92P5`51+?mkm!X@OI{E_hxNk=XusTc8mSk;D1(Wh+Q; z99flZpaf)C5+EO}9ey}dR2lsR?^(kzmbhpqC!HA*ypx$g{%dS%A05EUL>*j#Jct`i z$XVSnR$p?BkyYA{4-x{idh|cycMS1+IWf$%Tc&m!ZV2XH*E{2zY)Iq0fnAihW@;zr zCp|{K>ZJscR{ANAI_LFegZNn4{Q*ec%kokBAP9yxurR1+rCdXz%NqB2^g$?dMA)G7 z2#-wBIai91Lg=|MkeXjCQf?oHs0Jfu#GnEGzr&C7buB-;^5p#Muvj^ns~(3FOXZ}c zof4axSVPh3QktGi<*7F1djse9SKhM_jv#)>Vz7R2zCBl29UpJr2Fb->9$F%eB$kj* zTPlp%+i-XmvWD9?VlNR~oS)a%i(~dHVw-^3RO)DomW%V!HPE?#j@=1XkJ$BY%k|&)rZ*h#nWScMgE255xiP@yL=C04DO;@L3goW;ZJ{*FY3xT zbDRAzoHSQ}%po4J{gN^zgLrNL!%-d?|L_Ol&H}v~ibgVPv8y(9eNlB>xwfIpJ7DpUC*aI>S+vL_Q zn`K~-hrcaAz^%^2^(K%PHFdv!92237SiV;+)%!QA(CK{nh&~Ij7oZkBTMC4(SB}a8 zCeLzfN~IphW>BbiA3CbLN^>fUq~WgJ$`mfM;{{E$d+TOARLI_UPU}T86v`X!*&}Ia zguSq}&fs_vp5fF07%N==2kp3M23Pc=QW3_Brv^VfC*#<`N(EPc*DlG%{KOy*d>cnk zgp=-dNDFt3kKl5vkyDpZk<+CXn-+O76!$$0WAc_Qy!z(NQix4*V_3O0h%>!d;VbvF zVkJ_oUdYskqgv>y4s6!Ta5#BSXN9gGSNTJ;A*3_}r6a(~6v@cLf&##o&{!cHS+_Vr z9LdPmk!IR5R)%gI>o^-xZz!5CM-% zZr&t0;OE$TXLUtd8Wce3EjStjigE0{c3B$1o)Or=3iq`t|3}_GiyP*KB?qIBGUR^& zexu;g%%uSwQy-D0Pwka-ES0|S+6kG*Qc4bea`$YK#%=X7eDti|osy2c6z*xksR30o zbEZos&vrxbFG%6;X34HBmfS5hnA^aFdn6aO-8oeXmbVP#jdeXE4f_zF%nTmMh4Mie zr>9{7z}zfw$O|qYK%N25fWlau!>(p3EhywnOuBP*n=jz6rpVSR^R@gLr=|gg>I(`> z7sKe#5ot?BYDM%e`u~=zaGzE*u-uBTE|Z+pFz&=x7Jb#aIT8y4P~weo4#A zlD_wGbW0aDLZj~0q5X1xap)NEup*Pk5Y^tfLl?>n;#|F{jy}w8Elcg~+x7CXq0^UT z{7k1z;Z%SkY(Q*kYnFoQa!t7LkPU~q^LZQ1+4|q&d1Eq^^@?%%nuoHu+>JdLu(Pr;@`!`lTHGQR9CNVR zDlFDDn!Q$YSDT^LXnvf2%+Hk;PJDWm1`1mVu{#WBFjWLW06oqf0zQA0Rao2~;wXRy zM;yTytGt;ExymQ3jDXujJJbnlDPjw1hMujCaA`h1d;zD;oI=3vw$M2)jygDM01X}O zC@t#ObO>48tq`2M#O$zQY9k4RFYAp~)}S#k4AZEM3F*;1;oBzgls@R_ycT@bmX5HR zCZr3*6xTIE3eVU)xI81DOe_2>&q?9r1!zX`nG1qqCls$_6_%rLZGKtKEMqzoOe_Zz zLcTw7=tpewobQX^K_<$G&Umfmp%C{&JUY^}IGhjmPT}N=2*TGC;hZwakc%UOQe99a zg*k4%#_;&`!?Ju|JbOyUW@n_WtU`9xZHS^GI!!nEPRICzcc!EpM=cd(V;@NnN5{|r zM#>Yiuc0X>jdQ45j3*b16yn|J^pvL2_+bV{qD(T;VPZc0yciun9ws%OALj3;K?w(C60XAHVQ^D8zh@GMukqLe9>P}bWkLhF zD&}5eelf&H&sDvHGLP~8dH*2i-oY9t56Juhf)Tp8)iJpnEX7fC|eT!nUqFc630(l){Lf(FxW z&cT7d3`OEI2q-jbrRkEAC_Yh`XU#VQGx2nIwTi7KIKqP|I*X5R-R}ZFDwk!_Cke#i z$lwESLA^&~;um53qhMtmj&}^>0=sX*Nh6A{ffYpqe~drKrSwmXt3WkDNnTwX9+cy~ zUGm7*T`~ZFo^Q9kre1{!lS{$+^vR=GsTPq3HhU*QBkT-Td_8mem~5@6mMu8T>z#|| zWf%hYVF)&sjS3<>DeT^WALH9o+aNvTqxi$g6NSanR9Y?jsWi`s|(VhF`hezK-Jay8C@F)9vG(F50-5i#vXJtg>Xg998M&U8;b3^?~>oePS ztKvA?(Xyb;mG|Kzy^?yYA8jkcfI&7gf%8F3)gdxj>t4*<;DtJLyh#f`l`ug z4MWOV_gwmeY&cKy45QoQJva+)Lvu)*-91*o83_9!bliK89x7yW?ft#di+t>FXoNC! zD-mBkcSdRoi!`6)2}d2BNLOQVsl0>yJ+j4>)ptI4TXt5}#>7XuOkbGxAR?xiQyJq> z>ctq5Gw+rU%U6IJ^H38O5w|ootJ#Xa{B}Fh!4S5fB#sS>xH%X$rrh=80Qu}Y7O3xB zIdGn0UV4XMYC(~))L6NyN^^0v4EID#sz60?L4Yhvc`?kBIMD7}&&^3cmgvIzB*Rn7 z@`al+Zx^CDqy2M_WhpLxT6nIV|mW@e*# z(|sU_dxUwL3G#+`IpV7T+>Kl`nbDuvqKU$<;hQ!73+R16i=O;GD2Gxs`g4Q*D!fk* zVAnN-%`*_*3KXdAs{N&7WDL_Yb1hi)%luq_` ztMGmqXGLL1G6?p z6JOeOle_@<(9|T3Cc&xNP#Bw_Fn)aELoLtlu@M!ly%?1+ZnlA=-JNn~uuon`{QGM+ z$mh50l4DRjR3LwM;sente9q| z?Z?NDNLgNi!kCv2F#@6j;ppK=*CiRk$f5?k`iCU)P2M@qCW|wper@2!uj9 zO)qflX)a*(6sJHq32gg7xZi`ljfJHQIoH1=L+-1P zheG~Q2$gp)b;z;4ZrKAxal1EZTWBBe@A|43re{J6Z9Vsw(R15{6(NbP1KRI<&zIM~?@)kw|Po4Zw zzP9HUO)JeR9No}u#ghV)!k6?vfPiMc1}7$4pONS;WavQ#t76L5XpMG6-e!mEAYH-gRL5BK$|kK2VceHquLT9F?k$ zN-*NcM_4L|mpjhrXoVxJb5saWqPIcVQYmsI@#ckdn)f#mXC?Sdg-ss*0Q_V=S-+!a$QXQ2Fl2qj9TLnZvcrtPXEsK}`} zr!hkOEXw0mn7qzlbi(-FLU@)vN0i&4$Y1X`tNGtuw?P#xM`twY6(Q|ZUaz9QG?rFi zw9+BF5pEVEy%*4NxTT?4HXv^_FTMjDjwos7JODU59xNa2z_~2p-BTc%%lMy|#t?+^ za0np^1&I#w+1nx2t8ms3uQvRZL}_uwS3y>w002M$NklKu_Nh$kAfH_(y1zru)#}@Ktdb4gUf!eGfb|Jr<#d!vG;Z!nrR%z>gI! z0cZuzM*xsK)2Y!Q{=%#rxn)C(nx5)$WYFIF#*q5g_TQ@Sth4uT+NR;Y55dNfB}WtN zjh=w1i-LDAl*Twl3=}$aB{=r|QJ9HnLZZ<5EZ#X9nLa9y zEC|tq8#l{7^wcSk@87&l6%g?mmZQKj2z&j;59czN9;PK50`Y76Z&Modrx}c{2_qz$ zooH&wK_0A4l2%(eZe2t#oJ#Zp%w6AyqWR|j+clnIC=cS2e|Mq>&K@?E>rt4MMrWq9 z7f$-OG`8@ej;1OKbLQg^l*By<$Mn99(M&hYP4nR8r#5YZDRE4`2Xow4cJ9XFaddQ)rcIdd{>O*!%2#*nRbJ9>8#)T+n6CF= zHvGncgD@?Q$q^`9DiAv5pCRI@r|~)^iRc(3TT1X`TS~)PbS!l(8&W!PwY2da&g3$$Sw9t9nC6e2!44 zICyeE6yLo&S}rUDX&gWzT)*AJ(GKF)et!UG0LB~A(NH8r`cb&6bk38c4PWaP0Suz) z3*NPI66ok#nGx0A9Sy^GxUAds=U$jF4ED1 zFnZJ!oOCQ_3PF8m#bBBPA*n$8Fu&HW?tw5jN8vbpsY5E!8|K`EIn5Gius2RefldKM zXLO9mA#KFrIIU>Y+|1IrDI%F9t=yGt@yJ%vW~n7B|^nZ z$BFB2+;T9)H(Hh!i=UJ$pkO$1dLAQ+x+3fcG6jlw$fx`=xBAuR5fuaHbLa>!#%SsD zJ8|2mLL5xX!XYSBzb!9FZxfUfnYt5YO=a4IQ4wh|P1;uQ&M-GOHp~7EO-ds_ScyxM zB}bl?5B0=PgcIPp#y#wMx&qP_FVL=eG>#{6hzn+jMZIB-7vmf_aEs2JBtc&l%!zyE zH6OckueUpQ?z~1(Q&X)syVc+bdtU!pi*M%p^qF?N*T^U?@!?9MT%NIof85~J(Q!fZ zTv=Hm=g(s(*tkLOrLL{5x$3-Mi~KTuTzbm8S*+)x`#NYthQ^o$Ty6GRW;7Q;W2ECO ztnnob@v$@(#e8CHSw4qJb<{E(1A-O$L51QlJR=PP1O-kr&n!$s6sS7##3VT#SMPCu zh1)B^o{)k82R*c(DOi}@F@N*ha;Z2*KM|5n5ugeT9~R=8zh8uE)KFFHTJ{AS#w%eais6;Rrz zVQS;t0Udc|N>;z|!`xVUEd2@tE)@(H5hyOciR|KGpr9Nq^Ge6jBfjDVIQ;hWNBSYt z^i?5*#wb@R4?5B^3>UvsV(N(>EdxB+PS_;lU4(6uNf;d&L8Ej+h8w{0aNlxnu#DFt zh!4!~2@JPhef2fHf$hgXep({X?4`IAhYTGa#;LYg4$KQ|C~SFv9Bv>n z~A$jEC2j!)gUd63(*fBdqMY$jq73Fg5IP8$PDXy(;qaK3Cd#HK!C@<&vI1jsH zCCbjkM$5E$%4l^F^s=)v^Hmt91HOdwoODqH3Oyqo7e3{pSF;bRKcGaiu;1fB?DyE& zlmkC(q{5yEw^{~`2l$yVY0&1MnX#n#=0-lAZ4^Nrad0%11#ILb9QeH0h<5l)&|Svf z6o@xfIGpt6arB&ICwvJCh;@a+nMz{`$}VjgaBOI#W1Y>+a6&H~`#P2u6bu|r)aNT5 zYy-s}BiHCOtvCmZaqE5$C@^gpNE6S~Vp=1}!#p-RQt5C*n&u7lFf#)R72L z280B| zZ(s1NmnQL&Iltxl@5%5DPC?8GY%UDo+*Tr&EWauagUGZfkiSH@gc)U;i`b*5W0@wz z55%k~mn~S`#CaYap&%T?s32D=_zs_Vvk!w2H{d%0#>A(nfW^Cbh#z0PUwqj3?^XGN z#9G=I2iql`TlOAL2@1y2+J5#dmhr;wh#i={eZ6wip1o4AA!^TD8Pq}8v*WF5ZEdac zr+@lqa`50C^7l{uSOx0+4}40VdGYOC^iQ6ZoA&Qh1(K*Blci(5a56`akN_9^aCL88T@8B4^U@5Z#!(b+e%rSf zs~wJ?klSv(1tXO6(%QN)*j0^Cdx&CwCW4VMjb%94<|@V z)Z^N{aSuafUZIS`#KX30qs;`VNf@b>BRw^Y34nyy9}keGFDejDlCL*kiBh^&{)`vf zFp+E((O`+_`7rW5HytNx0BC8nr;nHHeZBD{3s3l!#OKplFJBmjofP78R%?R$&;t#o8S04?pSxnH{X8yZPE|nO=UtDX>4q1(O&*T58kiWcqWhWqxn#vba(ei zeM6np!R&PEG;Y3wX)1!9&^*A7cSf^iZrap_4V5!eT~#GCarN~N$SLHb1XqdPe%k>R zOuc=5vSVpma2TGmTYj|EB@Vsbio!Cc8H@j17;da{Ju03)%e)iLsz5kjni0 zJkWO0Gk0`YX+nJTL=wT-LE+AAnzJ~}HT*p5FU%YB9Hf=Oe7MV8kY|h}Xr3aS*t`XBq@VdF4i8ywm)s3M4FSFxlR3L;}_s2RjyGUZ?k26d zAA$qWWre$mWY!1W9|B>_G*LKnf|+pc>7bA%oI=&zQpvp`gfq$T(~N*V0OQk3*d>k8 z0{k#Pt%o;q0y{h6_LD?{#!1?CR1Rs8-)y#wi{G@-GOgr4=S3J7^R~2zgD>&Hys;`d zIMd?Ngooyhc_5zVf#L8?XL)1XBjd|RD>lr6&e$||e~&W1*hGmrSJJr=j`?FF=J6km zm;mMOaI-%%#pKD)y0biR{|9%4v-6U2A3q0J_tDHK^~Y&;x_Tf+@d z5`_<8z5q@1?!&@Bcrm!I%?;+`U5|H*$DXDX7?;8P_qF`5M;bN$^%7=#^V*!g+7HUr z+M`4BI39#?@Yu){h9Acd3TnQ)Xxuw79E@@E%L%jqPCp{R5nt;Yo-|pZgO3Sq2;smp zq#K4%5B{iQibGm4?_L~+j3;qzs)FyN9}=)vzzC*O`cRm`2k@N_&3YJU;oK;9%nSMw z(LfIShW+k@y}ly9P%Q-6OxVw%<0Lwl)^Zng%6hoCXhNJg4mW@^ z1|q;I9CHRrlVF9?o4EKx2LtCV8oItP4B-d{c#vv?0nT{wX_RzXAm>#u9RrUL>}UXy zVEU^k{5i7HxB*ecz%cj=(-gq@O~)t4EBqq9LNyJ5gD%n1y*%(+!-V0LjE1A~pwi<~ zM*Igs=XWE2$zN_3q^rX&@LcQ}_<3|(#>+WWz0njI^sC3uV}3vBd1AfeS!8taWr2fV zu@um?(v|rL0d*!=QmvT)(vLw3h?mk`{Q_8nv5Y#yNxjH$#fZ>y%W#!d85*; zf^W^p+=!8<_u8BcN01APScVG8$Frx*{6mvXK9=yxas=}kJRZX3iE!RRxV(%b7O=O1 zW*@CQDBpDM0|B$Rhk~XinvT?DrIH+`EZ}o)gmsC%6$)Ec0e8AHu4Dy(ywty?95s2J z8-bY&<|`aCrIMU}G_}oOi7l7%V7ry zc|)3L!eSl>r^$(q`$ppP#<;m}WR_Rgf)M?eu^Nqz$Ju{?`{DWBg=4qM@|ZII-FU}z zvi^Mz2Nc@jdN;a0UbkE$Uvyj=n}apkAJ1#M&Hj>2n#Z8jn!zIzkT1R zrz@4UW6OeVC4##yG~GaFHn{JCcU@$r^BgEB3jiVFF|nSj@GQ(ZaC0F0>4ayaS4;ft z2u=v4(g8ltsmjjey&@4kt#=;#5Yq7v;vaZ!uS8a68Ya=_Fb{z~Kgv8C_o^rzz%v9Z zYDgUwiWBk&2xDy zlpWz&nzsx&uxVEQBzqot=Y;>V)Rbi)y~q#J&2-#I$2!RRz~e>C-QXlxwAcJ;`Cxf* zqn*Do@0v%JBDii2uskrEsQCP~av~s9Ao0RdvDf%v$5krtQj za(_q(&VJ!$xQ+`Km25?MnRH?KZxwc+bJO7UNZ(rNPv*}8WqNWUg}Em}rL7`6VZ+zf zCLY`1DgnQ_kAWS8mJM859fh;U&Uxep?3GXiBGXZD0bav>Abc?wz^ifDmhP1Z;52?w zz?SJg5O>sudIk6mRoT*1gL90G4)A$|{PJ>!v^BY-D4ds<2kZk0)5OY$zzpAv%cY`l zhH`0b#6FM^uEZRzdRzh)1Gl_Y!90{8Z#!G_WLI0@rKUNK_WWj~#8sJafdI?mdO9ob zkE=s-J@ZD|%-4fBFRCm!qGctguXhSk^nDJ{44iZhgoq{#$FL)OY-}8-LfW}FYzS6- zi5tcE__+M=hfiWBdV?y7dW6l#UJ7o?JA3w=Jo3mxx^w#+cB1pBkPkliP@1vRoN;h> zdR<+u^!D~?8p_JbgaZApyYA8>O)Oo0W?t8ik@X4p1mM?wr^GNT! zyz|bx^6S6$tMbANFUvy@-H%=AMt8Cfmtrvk7O;T=^6e4BB2***#49r4U`Kz^y zU>*-roCi-JF^U=T4c+X z&1l5V-WyHB)xVJz1;Q-O&S#u#=K}+Ss?<(nGvVOiFa!l`;}CeQtt~3-s7#r5D#NbM zZtR>c!l-IiM_Fczzxoti+mOu9H1>h?4RareD=P}U+Ok~CF=WVJzKFXSa6AYngGXnV z<*T=5N=t1-zWwryjNtBbUL-Y&Jr0lT$&g#O;?4}*rD1}f3WIlMl;PfwLEM=!t#ci2 z4kQ=LlWMS6;+3Ow^5V%w96ynUV>YmB9D{<#@5#YPbQX7la36>p6;SxrSLMs;%Zu{E zcjmEloSO={Id56M2K=1N_jdni5A$4IiW}?l zah);tflz5OPQr@|+&++g>>DwqNjUeoa8C*I7IW7LmWfs3PLq-G8QgWkIV6_{D&DHH z{LoArmZT*j1f*WBmIK!IU1?t}l~lNE!2xy(+4)Z7)P%*tn!@=`8#ZmkaTvMMj}ZYU zKVw~D0^GShhs{lIzVVhk@xVvQfOK}^k`$bq_1nMmn=%d47k7r!nIMc!$JKoqEeigFcix6LyQR3eNN-`R z$0%qA6dsRL;h9+E0d3+OCB5+COAtWmvVF%kxp?ssOjwoIG+kGho5Z20{Ae~pCtDqb zth;+7O-Be^iUbRh?yR1fb9*|*af#GC6bln75$N z4|Jf8c5Dqg^Ftv{#~u;$OFYKU7y2+PM`14S%mYS5iqEvV^jO{qXSzZ-(;C3|WV}p2 zC+sc#K7M#WByj1NmSyB4`19NIb1@4rGq49>QBMWoJWTONnu= z;rbw=P}u&~Z~lh%#yD}#aBOfCz;rgD{=^f%s*Rcxt9x*M6#eNpk6!tqM zho-?xhejLF3myys7yIR4!7sgn1R0ir2bzheOr?3>5tg z-q;>E0so1uA1hLv0;D*wHV0UrDXSAXmGVSrsISF|rdWnayE509Loux7wA}&8l!XmD z{cl^1#V3UZam=4E9)>B zbP6+O#0hjK*m?g)AUyG4nLwwjz>jeKYT(%GphmGWR-s4%2>?fA3@2FDs&Kp#k1M87 zeUzStS9ml{6tKP{E)@+Y+$oUFU-yzQvBYOQfw0y(#E9s{)n4?{JW%u-Jc{qq>heHR zii1dWNbcv2LEu*AjZl|5<15q1q$P94uVnr)aL{t&xhT~58HJ?YQXKerIl%T}`#|DT z5{^I`>TTjYio&(OX(}lQV-t*Zu2Du|@s&URo0#!1UwFs+MJZp~xA=JCzb!T&&iDBI z8r~=2N#V$m-xN;%vpX|5S-e;jLAo<=%NdX9;LU1+JFbJtcI@pKo?OJieC|ryUcl!U zRATF%i@-9ZCL*W?79Qq+<}gpdW!i2IWEsm@F%AWMNjk8v1FLgocn0^n;A|^8nnJm} zm&!>M1b(nyOy^g+#(1?YCaGb`Nj2mg*(>72HK) zC;4V$*$*q=h9*$0Fae?qJprC0Yeoc9{G*r=i7?QyWxh#ha>{;R6C3G$i#Ag!q z-jBe?^2YG3js9L1muJEo>neavO3@4wo-A+Gnm5CDPvV!C73m*S#59o};UCrUg0+hm zFGGQ3V#)X<_7@eYdG|ucMO|*rjgC|@RE|5bh=YqhoN_@5qFIVf2GXS|^!;6u6_4po z8WtF#zZb>@w4CD$v_m}gbUzB?s~>j~|D@p=){onJPZUPU11Cg_4^M=^pRdXCPTDK{ z1ELB29C}DQq#GtAKnUVimiTEiJo8rsBZ}u*{)TGd`5jM#Fw&ueUkv{OxJ9$VA3-?k zUHCg$HCA&c|n3NS`qC z^YMdVSM*2}-e8IIensT3IdY!*%HieY-9_@Aat%X7qMTyt@7j;*4eI0G1- zeOtj;+7sa$4#NcRiWcB12=AJIA2(SXga{BakPTyVtGbXi<{x#G$?*zp7bz! z>Op{Wai0c`O#{Lbk1oUyU!rinasMl$qKioz;Bi4>{ejOm>$fi@68xxD*Z?L(?P+lL zwCJNy;!((SVHqx6Lqmf;2L=Xk^m&E8+fPU{jtuYE7{PV6Mc8A(i>YkV+VJhkO?~Ux z!EW)b_T-+EOxy>}``7z%QIu_F=@M=yg0Qky%ApN-0f(nC<`KZAD25$7@ z-Usc^!;5zRiV`dcz>R9(eq}~@od&MYz!BjBlU9b@u^szBaQ6lUKPR-QFt`t-9Q!eb zK%WAhLX>ct!FXrJ$T;o;ftknPjMLo*GK2d-cqh8+wX;EUAIQ0$1^MZbd6>XxQiG`t zCn|pNcHCWpV@Y}ju^NyQ`mEf@LpA8r^$t#{Ig0PBcNHZ$s*HLEyu2YzWkuQ8+u`my zL4QPri0%X7eISL|*eiqkKoF^=m3gbco{~iSK$y2t!CC=(} zHcVvoSiHhI&rP(q_EF;ifonH5}me2y`5U%%KyL z$i*m-)7S^m-`B6#wep(UmAYvqi3!{x{#SqXZS>ye^-5Y+bRJ{U&`_uLUG}odaDM@J zn6tOVy%hDBu;(f--tPCEzxl3A;HVSsWFN=Iy+fFG`<4l;izkxy-gHn3^^*7~#`|s7X@t$@z;D7%g{#+h-;8W7l z(yVYx`8m6pZ~Pxoyw0p=08bpy)ue@0tn)MyX?%RtuhQLa=jkcm_tk3R? z596Ia39vu)2@n+>@BXJFk>ocdveMroTkM~*(t!W!x%Wd! z`ozS9!uRairS|1J@4Q1^dg*2D=5g~N?;W3-n$o+`ckkY%yVXUyz| zKKq<(+}NT+`z+j}el^aN4SS9+zw(-JBOw*V+wUC4xmTZ(W7s9nqgzaQd{Raq%)nzR$+l0Y9F8F6&voIRN)(usZ(mu>|-Kym3beAZd_&a|atbmj6uDB#Eg zd9e(?5FP|&9yJbpry-n6f>TI-9+=0ZE})gvKZ!?teT)B25-bHyap1ahK--HpUTay= zC_@E80XvBeW{X$?SpY$zXP@91brdTr9ZghU`O25jvybQwb7Jh+z8${|tD;EmyYD`j zj^bm%a49ehh$7gQ)46I&J`+@t5r^`$NbTK6LUVEuG5yAI5&$LfO{@z)?zu(B$S4I6;hm| z%U`E4VGiq!`Y$a~n&x6+dJf0Ja9J*wx>6Z2eSo9P+%jQzN&)NH^8e4?n?UJxT=$)~ z_kG_RXaJ3UB?uDSDNzfx(v)P`k!43?#flt{oylhB#3wVEBtAJO$z-fEwoe=<<75&i zj-4aPvK4Knc8Q|66C?l<#11roMmM_A`@YWqck9*H@B8pI77{cm9u&~ud+%1&t-7_` zTid<09PR_@AJg3;y1PTGSz^{KU6r}{lZmMr-@GO)0>2485Y_m^B(p{))WKW{v?27M zInu@wCC79n7TpdPhkg+RTtQo^BfjW?KpV?v8QNSAq}(^Mx%(s1hWU1Apgrc&wyZsq zl{T)|Tkci~yv<4=aPKhSQt!-5ovu}uu+YDCt!>t<8Nsy9qc_#od0_%P5EMNc2mY-&w$IJ%5}*FCyAOA1;pIB8>X*zYw&DmIR)0% zSmt>nKhVgmpj4;hwsxZj5~!waBG{mTdp`m+o3{13(JLFZypaL=6Z;E(1yr^0$h z!jVDfB79o|w-jP2I6rN}U=VJ|=uUeleYV@$m!4YJM^9jXYzX zVjOTs*v(8Q;{U67Qome5JCL~#BphSq&I}m*CEbk?1dW!m#5@~oHQUtk(|~j$qM!g? zCtZnJT`fB(c(@Nl%RCY!+?j!{#L%ej%#c!|FaHQ$F4HD$x*KCycV}aX-`!$Bfw5OrvBErMq*L76cnZ9NFoyX>^PI#F`XuHLJ95w*1hAfWU>(^Wkx52M!JK z$O(3JNFxibaD+!_p^q-!t#Ft_;a-uDCnqAoMM|zyf`u&+hcJkAbFHk?yiyn*E9#(x zcAJEE+EO$_fE>6GU?Hy*YJl^iY=-=6e~=`yT<&+4=dF)hCGeA10^Z+4=ei>E>ZZZb znX#PeM%^;K;_AJDJ_W-a7#Q$B5@G`~{Y2AzSeHcskA5*j9N|U5kfXlfiT=4yF1v~_ z*wgEEC=Ze*r@qic#|;e)yKV@1BQHOZF2m$v?h`sv@D<7;`=$nU3M2_pxo; zHv9bNzi55^{kE~YTcf%~Khle^;pi^MF<}np&Ykn?Zujlm=f`a^mjZ*2j3XyItA4^P zCOvaN){?hmm?2hCzb7G(^WgAf$oCv}0M54hXW~4h%mL&KG%c zrx*4+u(1W6A{p`FiOUKPEJXt!WJTi?cf`r1jXaXjm9&L8A%4(hfi|a65du8W;duQ% zl@RCF@2wJeyO)6XZ8||7ms!F()!*k+c8+9@X>>j*(;ij2mUhMEN5HZJBYG4Fzwr4# zvx}Nhw6(SQUE`zDP-i&-7&;gC+ z-tN@h>|gleUq})1BX<7Wd7qiQ742bNi$W?H;W#NB3TA6KOoP-_>4>#BGP)IMIA+<= zl|VlPridWmm~Tmu&d{POA>)2=pCIUg=-h09?)?y)>u6!-zC0#8IvDMGAQMxvd8M>j z`YF*KNYtU2m<)H61i>%xvhD+sZ5{MF*sv#kU+!uSr?SqDE^0HKBd&{pcSVV?^cmC0M~Rtda~O90*w2a365W(3H{h2tYWaS4RP;^KmB5(5uw;O61#8|v*n?|HXt ztPdz&mu4Az_Uv}!jNkh9cVv2`-L`L+K7r2S$Js}GX6JGBuNa=^cfRv|Tdx@w_P(BZ z=6Sb&b@=cr`mS==>R8?!pVdo#B|p$rA>E5QS<{7q`>*gt$l7X?+j!iZIcCTicPX6A|H^ZB1Du5f;C;)&?J6N6!dvZmKh_ z{AC<0l$l6lLydGnYOa9S%BpOAZB+)&3&snpq`+BIm7d`yC#59N?%T6+cM05o-Z#;8 zN=v#dq=$<%L)_+d*?JuaJZQ+(_lPvKLmPrCg$Ei|rL8_cZJv=^KetNYZC(Nl%=pXa zToWCS7TJNS*1iPhLTV%w2p%>`W5oDeK|_aR$)YIHGA}L;jAG}`9d43@kc>8Q(FBQq zBtPsq@P~_9=4s7z;L$8R8sj&^v5bQauILaR2nLKLjL(Gwm`-R=P0w@ue%pjWlDH9_{orobrK&**R#FR7x_1sd?ae9bs;{TP5%|Dgm0b z?;qx&Dbow+NSOGDW}A2BGXZC&V)$&J9l(k zs}qumcpWdEFNO1Tg*W|->0Sq#znUN_ysOtj$Od%*bH6mC`#^}GKStFVJrL3d8_=(y>4@mh2xI$El2d}7u{Cv;7$m@ZWaKH!rIJ2Z-Is{ki&=!dYJc1gM{?6J_6 zsH^toFfi$tE=#N*W*Ct+bXjmtn`lPIg{3&)+TsW_h?csVKwD4$l$n(a%d|PZKMrt<0;xoMH@OU2$);$Rtdb_OMpJ-{V@-r837WRC}DV4_stsB0mn8L zXX)J@kvec>#O?rmK!d->PvTbKFagDh9;xR_n7|}bUjCZ)cg_P8hLRUsZtUy(j%nmR zuP^o+AIh0!EOU~B15+`pI5wJLq8?9Sybh@^)^zwz>Xk6)HxQn>VH8hT{t!A*4+N;g z^+OrCeg#>G;RRQ4UoL{7I~+d`4=$w<;a~!;e;GXx$CJPn&h<11uYYXKYdgd92cF=- zEub7Xta)3^rA>aGE^vj3Q}2)`r7gIU2s8^7{(L(eyq+hXiY^bwMV=fUXoF_o!^@`! zLgsF{TP5%|DuLMc^O2MhAo)DAVCqE0x^ugzTpNq(Krn;YnX|%O@ew-w72sQd>nedF z-G#EGTlyI57P1_u{eY`Em|m4w>>~^_7ONq`5*Hf&u5@ zR30&229MI?ShJUjTQYWnENpP|mXhiVpn|7wB)8p|HxR-07O|PrFRjOw7 z2)BH0mB3rO1d?u$H@o}IjyZ<9 z@h3PdblkF3*QhQ|E?C)?2CLlMYO|NdZ2Dlo3zX6gbym5p&E~cKJ@wK>*&LUSL64lHgJ zOyQO#FeVRNu*Gqm5^EG}uVCVklP~t#;;`1{8+5Ps-Zc_jDwl)3wlJW3x^*u_)jq)_ zw5ASUwE4bCE7Z}?s(qbSR8eY^uUxc+UR^M*J1(Y*C6r!g`B3Pj4`W-@CT>kzjTP5P zmm!Y;fi-hXcYGr3ACNTb`?Y=_h;$_chbi(G?#!t2qrB*cu-XH&#b99613{mJ(@zc; z9uu7f(gT^69>|o+6Cr{ z!O;U5(_P~cPT99K=$;U07@zV@dtl&dOzs28r45tw=$njBhI`8=Ts3ZU7+y#4a ze%hLJyq0?xoRNtkg^^z9PON$3;@UIOVN58D@eV_vgR+Kweqz%8{?5JjvA_Qd>W@0M zt0l5h&2SdA89XyO;rNSnCqo%939*@x@c^qRvoakGWwtUiJnp0aA{}8ZudnuX`kA3I z7Y0R|VU+7=DYJuVU}6eInmIX4>CX&}Iu2$I6^+5HIpLUjFvF;5N?=N3iA%0DD=Jqw z7}d;(OreN57N~HE%WPy?;R;k{BRUHGkN(B)T5sQFs}xOaLcb=b@?X`a)Nt zrm9Tgi=AoDKywycx)R!mF3}pM!-WU@MbhEGP+?(#mfBPf)N$0&s1bb0btp&|!4mX9 zNWZZ*Tm%K2Ho=KKW-g7QL4_>h!-LaVkkp{fZf{zMzvRa~f%mjLz9?a%7WU&hbYs z{h8G9$U2>Xp>@+1$7^bA$$ZGHW_l?)@N~Zw||^Ggl8p z5vZs3oVzi6$x!gtW|eDf)WSnp1OxYeG&hoBa1maw@@PnxlfqN~wNlQk)14q0r@Z*g zrJ~%bbM-(Z1d5AGrOT3i;|Rh*RcPJoq{~7>#Oa$E60@hy_Ne26XAodcTdVX#R+{a5`G$jqb)t%2(VkTax=Z+P)AdX0Y6 zvpbbfs)$yKH&qTEjx=z&@Bj_Jo{bO=oV*30?j9kk;(OYpn~O_ncrq!!Ouk8h@Q!|k z$$A8bufVeKdU|RkNm`L9M!Xr~J+mYdoa2c3$pH1@=|@1$6QXgcFsUpn55YT@iKmg| zi~P8^z9X8jL>`cN_F`Do9e#w%sz>=}=t}1!BBXE=9HDafOyYO~A%B@)@+96vn1~{x zjo-e6MF6z0pLza(?jUdQ>B`vHxNDUYf#`%OmXLV%g@R!4$JP< zh|c2A*q+I_wVYqT@OF# z426tP7!*uaA%IcwXegm52^gtW!*C~m{(Cri6EX?nFprXnogFS>AZ`qoFK&c+LS#XJ z3``Lfe@V+Hg_@DfiN>~bvv#aOBUOc2N^pAuui=N$K5O>QElD%Sc{bz?qviTo&sP$L z(K{pR@d*ha1%yh%mrEeCd^9dIBRE(-(Sk2=9SU?oC?67L|DZyH1#mFPh#A7WLNf}E7|&>C=kSCEW>%1O>Wub-$h;iI z-`$P|ykxa#OTeB+nUr(tzYWgPPR`@XGnkq^WlYQeo~? zA`~(0lXOxUA%y3Turas-$FyTUL*)fJBHG9cV*e$nm3*X9QG^JONld8!mDW{RsT#+; z+CT}{bmq7;UiXTTlW>a~C>N=n#{6WW=Y-3^(=Rz;{4)h(zm>h)=>hT0{voSb-|j0f zpqLjQWwoPB1G2HReO9}n!!Z**BY0KT3!-aL*GQM82Q~_mMOP}#fFuP-zh>2shYUXn z@e3NfvJwX1#1^|ovGcA2P{ zw`yq`x3xCQ7~zm1baw69YMVE&v+nM7Ha@0h9yP?KrUnV9N-+=CmKPm2O9q33Bi4EE zu8_Id2$fz|A!Kr`M&oTY=`apYlad4!$eUM=*A+NpxQIVS&r_HlHy^%yFz~31inLe5 z9!Pp~I47y={uJ5p3M*7S*i8OtWP4gme*>-u5;6w!Y|(y0smeO&O02*ewU<(*U3Z3WzpvQJMu`=HawQ-xCl1XIeFfu`bMm6|8}h|6x*c($2^Vd zcw?l;SJ{fO_C0;nYBsc4&Dvn@U{166IW1?GOV_Aav(_c;cXH@u{Om=$@Qr6}<6nQD zRuXEAa3yNb7k>DHjUT;WMaqAH_E#6iC#`04yUmYHS*cbW*1U7CGvdKRr-Vx@Dq6B8 z|FxUfI{om!u~WTv`S}x8xvtepw0vHn)sRJ9^*!A`EJj~pMN)oV)IiFXwaTKs)?zJ< zmPlBQoxEf<8#|oF@sk&y-8)=WOLeAJpb6!Rx4bbnjJZM-fFjWsqUrEbo7jeD{ro~kyp=oolf^& zwo-|{!zN{sDW3!$ zN)lWOuQ1Grd_WqadzlIBgAZ`Qd~x7$xQD0Q9V-7&&Xl7I7QsW<6kL8DS5XKLqt}Ga zbPKNMmCFf%#k9cX`Gn>KKdF(|d~gq+4BW|AD5GrJNZ;Xlfw9Cm0nWe+e^R-5E|z^h zjHG|*g%dV*yw3_GpbNO$zOlw8&kk6*t{@&0m&}cI#U8A5~&or=9xz<6_VnD9Ui@(!xnGoleCQ-o|Pxm(t{= zCth}8RI#SsN@cZTW?)oIblMuXR;XPRS-<=jHJdGMUgIO@#k2P6 zzk9*T)~;8@EjlwS)pB;REO%GPQa5dDi|>6Ns1 ziO~n^kr(!Y{DlmwQ~07M7Mv&@|ZA&pcw&mj?7V zXk$kXT1jJz)oy||9ke<5vW`cNTE(V2#e+0RVdj6PR|h)!?eY(vXT)vI55ChT&t0-3 zzyEnFRr!`_Ab0LBzN@4bZR5{>z@`Sqtgc(arc|bSbok`tpM2f=AAeRtr_nlp>R!A2 z+;Q9W8$V?|Uwhp8|MCY`wYkeSee!+UMwqta|LJSiaMw=T`M=#`0|yS-{P=`jc;S!^ z3&|owfzrNruLSm}4hqd%tpvls55BK~Pnk8}cbAQxxTv~1s=9AgTGiIHr`yI)pRmg> z9<{pdo2_}DCI&hv!=Cekrv3HXx^3jxIU75D(HeKC?$qFEhtc^GR{X$&O~$LaaxG(h z?W<4Ng}yN@b-*7rYMfityDD7`?XkxWNyC|AuR6a{i4|2b05*a(n&TwPS&bZS+jGvI ze)=W*+Tru|iLX3k%kO)@1<0!D(n*s%pZ?}E*7xnhR=uUwrZ0|I$Gi4emqry~TD(G7 zz!jK>zo)!n0nk}_kKbfE^Byxp7%`8C&!2D&2+VN`>DesO$YW5)ZHrDW+OjryZ=Rzd zuHuxX?5ri>Q+dTdKv&8JSagA^gf@9VQ;48I;PFowJq(_RNmvl*({TajFXdvoE1|<% zI&Fdj1+S-@#0M5hKxQ!$MR0g_RuCphW+&|UlQ8~>hzKnjJW}xR)I9Bkwh%4+dT>x@ z&?j(@_4S?{ZO$mJQ|PdsK#TMMnaMW%BaMr``b4>il1v$i`%HG>(psk2XLmfUNF zHMQckOPcBE;L*~o)pl&P${Go_nNfXTver8!cB5fNrRqcOK zQ)q3l)@Ta7pta76&%SDHipNr2&-V}bdSq=^o6Sy4S@ZVw8o~71z`>KkqpMZ-@9@)E zQTU{@9>>g!m$iR%Y1Yo`Zf=;}&2g*ONd~{tVnoj$ZdoSI!%G-h!*SuCF-tt70L{SN zU-c#cc!1+v+<1Kou7($8AV5O^eq-8He~jlblRN=;mpl>7#L2)D?|?tx^%;g=#HM#{tU3IJHp>~Kt1&;}Gl3L9zD7GXj`Xsc=3f(Q6>X!8<`#3Y%1osVP3PAY55 zGV0mwGkRu+*oDKms4qpzf+hCiip!O5U5U+Kdd?d6KVY37{_7ekPHGf8rjv>5tZwfk zT3ON3ikQdzs2FPdFKCokCdM81APaOz0fx6YGodA%IWgj9g}Y=G8(5duG3u!aD{h#w zhQ0TwoYa1`oLyX5>&rb$VnQts)rxtFfy%0Off#Q=VRrvrk{@v>`+23=bnk~Wnw^&r zYq6SLm6{3l`BGQSj(0obUYZ%x5{VA*R5nPsRq8l=qg8Fxaq`-FG5A`m-gKYh&uAud z+B)9#SDyo&9PJB*}XvXv-<$s;>wo%DRO`^Wl%Y;0Yb*}S)LQ$0t+b9he z7Bx*8TnsynUVK+$O=?1bvw7I#Y;UQkINDukr_L~rq|?i0wX9ELyx{9gd3ep zLA{&BzNV2$g_!WR?d`sS+k5Py&3saYq!C68l8WlDkx|cU)4u2PPui|u|A-3@ro65k zs8AQ5KWZob<9Dq4QxDo%t&0vndD8CxgI^WnzhE!@{XezM|LFbdgiH4FZ-3eD|J}c( zb;iS*uB~wa((}C+wBC8r+V9wE2R`>jEAObcq75bX?BD&*w(0LaY!y0n^wMvA-nRao z5BLVxyq4kO@!s6L#_y%kNvj*sl0mB&xM;pP?pk4GlP7v>5lM{9*8#j*yN7)ez=fD4@3=gTzQRf>KQ!{(ju>Z>{}S1VNq*W#cI_3 z!oN5l6#dRjXj!B{$H`SJ9Owv3Oo4_<^gu-06eri@?=(AY=x&tBD5DsG@NzK04MR%D zg}eqG7wHcP4sGaxXzb|sfdoB}KwGJ1)98nU9+*=vt8^O$zQV<}jZVz?#JCO-l)^3*3h+}rQswac7ct<|f2|fQYj#kerH%qI#u@Dq6qM)`qzcLN zZYfTxvXEJO>GbffNGf>kM=a@RJz?~Z_C?y-3f7;5bdiLOo&{Q+;2^~^Gn8VTj8pVk zF1y#hf;hJoG-%m`ylH7`c~tn6mx2xNa$IBDyWqR9ev3_MWW21Ua2Q-+-Fhpk-(V9O z02UNXi7t{QAIAWBy4!WKlBW)+Os3fp#i z9n&X@Hoa5mm1pu-B$WoQXO)EnddZFtcqTARN*-n?vtrFAw9M&zn>0m3aLTtf zsW7fgUp@#b6*G0#y|y9gPxg1PyEL zuKwYTw&DGE*~G=mHuyucX#i}ne40KbX!@pQc zOqx*I@Bch|A;N{Hel~P++N#iLb|IIcE$DkRDX-zg7&L1WJ<)N2=8TR@LK|h&Ci0p>9k_6b#?}QECftdRoEEE z@IonT$5Mz?1n(QvoA1-Xgk`>ki_j`=??Wym0z}X>n}|!Y z8kVRHctz+JNB*HHy6_$G(GFDCnY@LVF{jW{SOo5s(>@`8p)EjT0fI~Zv^%Bd{|mHd z5pa+$ZvjsJ3)LT@^Z1lSlE0X@ut%sk4p-r39JoS!XbN>j9vDPGcPs~4g!XbEqJ)zt z&kykv$GhhpA&gg2b`A?+mb8zQ!6SeE6)-@ej+7>G0)6SaNbu`Xm^%}cFy=&Ph!fht zN;@H5%s=l5PESLhN-vWaeJ+>Q2o#R7Q-=Yk9&(x7Fj8bQoDt8oMxSkMtv-bg!EWv^ z1+pkrK4UNc$8Xrs;WM`9vyV8F^adh2(oR+G#aq-?Z~g1UP%r7OcQMMFHw#Ip zw(=etC5)xmI{bc**m!B|oLbQdCv+mbgAot7ny5TFrGw*{cBn?XF5q=HMx%6OoIY@c z2233EAsRI+3%FL`byDm^Un0Uulu>+*7$0|nq{|CtUnK!nuhUX7r;4aiYB)N(o)b7J zk2!{?a<^>_ik<$Up9S6W~f>!cJGBX~!Na!vy%U|kR9{NFK(wq0T~P4%$8bOIE^{z%m-M=-<)4@ z9b@O_E!~60M#nTG)cU(<;fiZ4Vhr^4`&AHJlMRfE$I>T6!MlQE}6o2F|SK1qIO-I))9;C_+R|c)@mj&b!pguu`jVOsM8ucGtG>m_uoEccYpC$v=%yL z6Nhz<`j^AHh%@7ZeSzxR@QR3V;&#JHoy=fq zi)sNMOI5C?fs(0~S|S`>3AEH1>CZ6TcT5ue3c&4#1EWiJ`~u5J8lh@H$qD7%-O{0v z5IKG_6<+?EJBy{CqT4Hn<1|{xly;D^_>C-*BKuyOuN3;=JR<7%#Px@&}1PFo% zX0dJC7B4p#PCgg$^@+J7;ILY8Nhg-b^YP=SoY|rS2GfC2@7lFp_*f2QnxpNo%q$YKD=+G;olMCo-#ZVV)hc@65R51^^*dm<0qYD=NA2Ew_h&KQTzv8S1zp8mrQ^F?!QIi(?k0MsBs6#J+TmArvv)S2vVdVD z3}QZF{M_e7(TY&wo+AX^0}nLXi!UC^)F1KD$qRLnzwBZfw1eK>&`x@L`@D=0azFaf zQ!1;F7f!%#+qT&!2CLOY#7Elz&#Z(80(Z;S&9vz*w&}M%preb8I^V8}{^;#iiMF{aqe`RN*4sCG5}cQ=zrWko zynC;kNdegX$h~&ri$An;-+$5C9@wExJn5~7v0OfM+Ik*)-j~mI|GGBu#OY!|=jK;z zMyD_+T9Nr?Z>w$2w(-^uET8D+{ z0LMlZaO`7^VIxcMSQJ$ig?8xFvVH7JOZKoP1C#Ue(-P5t`&(wi<4g8`Kek}46_h6H z(#!UbAJx)x4CFEJ1>c_Z9iaRes11#mFES3k{SdWx8q8 z25+N>4j-|W=4PMmfaYrX!>?X>LYOy6IKWg9P|T2cHtXmu^>Q6WmdSCPEs!T>A*e*~ z9vwn&{ra$ya<#k>9)JFzxuz!U$56-2n3x$c5JC9HndPKLeKQ>E=_j67dVgD+Ug{9B)8xYq!T488S!UPY_}~>m*1c<= z@t1a`QY6~`^Y6`owteE=;hhdC-I3e>_Qx{cR2*hf_Ws&OGvVXQmXE*F)6NvS;%DAt zpq%Hj_Djm9WK2=a%6G_e{9oJWO5<8BK4lb6DTS~wSe9(`Khwxml>SXvmUJ|kvTB`0 zr;RWwM+O2X9KwKSaD->=Im{x|R)yR@y?kgFg!edncsd|sy*I$g2kC}Wddv2ZRsz~9 zwZW>r!WsF29{(oIvb9a2a586 zP3RRRMO%jE02jIp4RK#9rpxnYyGSF$6+Mvs96p!3d_~<7vao)5M?NL z%aIpuS>3)}M}M`1#?{>{F@cNp_$x23aLXMqa9qqsF7DWF)~xC9nF`E$X{p1R{oQxp zp?CI(!%Y5KbwL{70|!@sM_Op}#Zsbt$2;zG!4cBD{<4FH&6{ zGgcPw-gSxNC!T5wX|~MzzY!Kam%=*y_#1CWP)AGE;0q+BV1ZjI@#DqBTV06w5U!FjEN1=--^pt%@l25R}9C&;Sc|E-50$J7kLeT?iGH7 zESI*B*MtT}rJ(Y-j*-_K+Jrn)vH|TI^&9FaU?<| zuLoZ`4-tRF$9GI84U6Gcd&W5U$9VA>{K6n=wfMPk;t)ULjkpL$i=d&VVSw_vSd9@6 zVbbv;JPpf-C&CC9Uh=t!0Iu*FoHGW+<;=4t zfnp$GROOr`7vD;s2|oUv1H4`s7;>UI(whX!RFF&xRFCP*y7mqHZ2W5}Cg)KahG$$X z^b8>D1xAkDF?1`U0AW+TzAET}Oz6Z1#FIxhC99=)RH}`SS?zg^Olb-(IX5z`G?uj- zqT_>Xy2blIIJHx&{ewck4<9U#MJS<;X)6nTI)(gicLan}hO z+USi0+5)_~oDpJ)>$oVG3k38%N^~EH&bc~mbDH+BpW=ETvXK?_Kqk2lMDPr4t_Kon zOWy~w`ZBMO-87eJQ#lw{gXaXx`-3r3{2w0Bs7&*cU_A1s&04KLNo~ zEEq?3=Vk0`Sqxg-#!7Tw2bVj|59&@3DHM4)!7w^Fe_7#lK@i-d7?@7eEDTATR3~s$ z+Je>iIq`*InH|s!h!QE1Ir1V|?p%=Zwk4hHahR-P7HM|0DEnKBy1>d|I8JHNPpTyvdQO;S^MCO9VpR}KW!|t=1TecNIIuHVlic&?jJ@n1zh5iX_R=Z z?$}qrIATYWW^$fT2wUZP8ZrvlsQi)-fthIm#{~ahyN{DnE6Q`t^%4|f*sTiB5mE9T zaO<#x=GV;)x_ql*PQ@DF71{t~UAVEXLYwAcMnU+f2U0JaL&b$FBRAHK3$;GWd4@mv zo7^{|cBJ-FD!rL{**dDy=J{`ZOg4RzHpHx^L*^+8rTEp}Bmt0WThPmrOs6gmL%@VG z#%LnPT8pez#veJM65-XVTh@jti&@bYX{gll19)0QTeaE|aLSO)YS!}`pp8>?3EXK@ z_?XAlYvqmciAAt#iy*+No~!2xnj?)oaS^t9tkry0&jjV>*l!?@F(2`{TB@tUy|R>G zo;1BY{U_DkRz;NU@tX6Kjpirkiy}U+jzghL!F&Q7!29i4JSf|c>Yh+n_L3Vp)q{QbisrgG-)ab^)QjJ^}bwXxrjl$_! zeNpGBC+4NRCs?PLhK_62^a(aDn1s)Gr_4I*IBRwPl$DQ)IcY{P-XR95Q&LsBX|Q5= z&X>a`+I8-kTL;DbD~EI(R>V!VmfN(H>MDl>8w>(-s#&Ah#&U;M4Je!nby`;bCY!Ym zs-vcLO5?(|wbnA%qRXHptX@MQu}n9ASvoQ@HxXQ_)^hOCC-7xWx`lf@QZ622ZBE^N z;!HwZn}1z0wig|BtEzQ@(WuQ)I$P`@Jo&5FDVzGbz+j!p2!_C@k=4>#3Dy{%455Hn zrNazZVs4v|<>ZX;?aJjGr$|^yBHp zb=_qKy(_UvFBiXmCvBZ~90Q@Zg`;{!NGyUZ-o^-((N`>#yaem0-NN*wm95)k?eBaf zX&`Bod1K`Tk_pnhz&gW5*6V@jmt9r4zD<@=9ZmBXk7Hq-JKZ(eurnA?QM2Vm;25fR|||hwg;mlaaIy z;AoR`cgM6w>*#*4WClJ?wJ<9{_k+9Kfg@as)Mn7xKr4J&X2HV@34%(s%HUIQp@Vh{?_k zIs^!RPREVLB@6B{FPU!|GfZM=G+e+$2-f)xyYnPr9J(nm!8uFTXXiax8G%LvY2{laM&*b8O zvfX#zeSYlq{Q2{WQz$)+I@e2~Ep>Kw3P)HrrOYrYIQ;CFZSwfzR)628tY*`G8+Z6|36RVLLzlh|O!cbkg?<3sp{IN^3#Oo>gl9 zMWRfe<)Z8ii;5;0HgDPECvST~63T4?ScD4jiyM@4VB? zXJBB!>E(plh!j96Gx8pfAm6Mu@uuL(Y229^$dnqxq-@{Apa(Aw*rtu$nHveO%qYti zj3g3lJVOujN?*OB5#D=Z1|^je3^T@@$=_e&cM>$Fm5HgByx|BC-zB2lr*fDDvQPHY3duM;_ys% zLOvL|yG%pC$S)n<8GIsmLK$#WGK?%8zo(PN!;FOl5?b{h@CP_v_>%@q2|p6`I0+nB z6d)8mVMF*F+6Yg1D@>ZU0zcg){7TF7$_vc_M?$1|qmF1Qq+H>1qiFJl7i!Pz;R`+X z-;W)(a;3Abq0t`Qz0ZXO+Ui{N#`VFiQoO@d5XV7>N2H*p)ksFsqBZ z#4z{o+v$2CFk|xlz3+X`KK}8KyAav1VT1bx4fEr=Mf5qJ1)$UjvqmVqzB~3w zaA>fpH0Fjb*zgbjcbO8OwIP|}sL`DnGhB3bpDkX&9$tF@ZjLZWeGYMGD7`y z&jp{2o;}mh^9>GSdcDF09r=%8Da*58 zdBQGz?g=l}j{Ejl&7O^R{(?-mt9)7{Si#3KC_If>8vA3^K4nHb>rvj>5A@T78oX@V zyx#o~2+WEQXoN)t;`s67vN+!3`MP`=&GeuQ`skyNxpo zeuEvIJ|@eb+;_epZA+PAmWJhZxvajH^^p_(wfg0txz10RWQx{2v98PykPzoezdQ&M zP;S`V?-(<};}dxLJ%kI8G(7ztalQVhGvpjJ8Cc|48Ab|&raYrZh)DPlCk0}sun3A-get<_R>o)$yCM;R~m$Y zqCAL;LbFJw-%C5+XEP^%-zwJKiH}Txld|)6N z(17NE!?etYkPah$1l_uI>!iR~Z~OM`llhMq9lm?_Zr4d+sf#qg7b;qNsoT1Apk0LZPavc^SUvX(`X@K7jL1=S2DKjdbKE+g%CC_P^1uWb`gtyzeB@5xD75I^e*}<)bq3vgDp#6+-NE zbX58(TH_(SKSFp#06(lbmO7n~G-A5$qn~&Oh=X z3Nw}n2T{Pe0ta=#=ZLasBPGxTY6$n%a6SuDR<^XX+i$%~xAkdlenV5RObx@!b;01} z3KWI19oxE9791{EIcUK*0wf9%>Jvdh`7SAq?c29|9A+C8(zfpC=4$51_(OgQ!M5Z;u!ZuPh99_~+UDO>0m69Se;{06B(xA;?JTJ{?<12}az5G_`F@R@E8~4NdtJbNmaXp*0 zHeW7j!_@ks%#vW+$(P!Mqgl8}=dI$dFE50GaqG?y=}oAJRNoSYl7{6a8bwMMg7x#D z`w{dhilz5aC==%xjhl`~eI->fhFGX0uQOU=@VyR|4dujsMnMzDf9Gsedk*M+0Qb8= znsu3wmPto-9J*Nb7t?Rkv182pkLu2X2&XN!xLJDXgvj8WkNT8XM%YcY+OQO}7~>ub z5fEvp(a0B*+1!WXl%{Co?u$ShBh#S!Q7H5C(1zvdDeb{9XkgE!#_v5@IhpoWspE7$ zx*BAy_y%2MTs&i6k55qXj_^ps;s`zZW1fUR!qAo|`z!}QekHZKOI&}!g|J!SR{02c zgJ;YyU-3OYfe{v=!E7Xcr~MsB{**k&`kNDL-3|}nYYq|ueJkY^?nt>|y*L_jlSw8PxmMi4m)lKq%96{x|OGw00R(f`R?`_@I67q;6zWgZr7C&35~G zO=E@l_{DMi(xFQ-rBPtpTPp04J*{q~c2vgy{^G@pc3#JTIdSyMceUHbRv9T%UJjod zu`j(mAVDKW_68LS>D-7T{I&8D>lYn{&Xy`4IR%(xhelcGM|23z8hH%MG6$vZtM;&z2l=X$nHbU|JarqnKn*$`O1o2V1WiaIp-r8{F+QLDR^HL^m4rZvW#hv?zLPD8}k#t zuZWklDYsss{KRnApQ{zxufQ!pOh5X8|Mk{S#PybZs^Bt9&&ps%fRNcMiNBFzzWLdT^T!42{{B zb=~fV_jTFde6Y(t^Z7&eoA20YcWKl{-!tJM{?AHGL7qiIC`g;$1bt)|2aNoT(K z)Tc3~>Orruf$GKA)|REH=h;XF%k=z+Ch#1uV@}~@9QeeD`K!YaTnYy;?zgT*2ckfK z2@c|NaaXkj48kXq7THl81Wcv=(hlZ~@IpB+E4<((V(=L~l_U+qBPg1ly(rj7PPegbV_t=^*$p1d;S5N$QNv?)%y95VTh>G4eRLmDwpp%U~>e1fF$ zD4aXIcu(SI(#icMa2l5To}^2#D;!~C8u3XW-f%EPgn_D64chuYrgoUz{YO z$ID7A!IO?l*!azRK77D%FY@d^v#ZLEoE@-FeEoDSUM#WtPrm$;9UGn1-=h7SPuy+~ zG!R)|*ycB!DI)Ky-XjIU=7o)1})$z`Fv0DaA{*im_t6 zAT9l5N$7g>5V6Jf5$BlR5i&^FGe}YooIoEd%NB)#W$=iL`nzDEK`CNRTv_eRU{0qK z;b|CckUZtWK(HDN?4ClBP7DX0*rp>4JP0RT8WzKOc|@91`Bq?$BZT4Uzp4-I)36;| zq*)V%R?Jf__bLPj#;Eb>K>g%?15d+Ndynzc&looM8N)<){N@>9@tMzWwQ%|3q~qtl z=Z3%5cZ`2M;c~;I^BnQ4221;=-}B*I4M%R+-0z6@df+ike8#v}d*+7C{mzZMnty~} z@r(eqZw>si1W3Nll$+jPW`m1W&iT$?y@ug@V$N#gA*Qq`fQJ_ zt1R}-^ekTT1{V%j>lSO>e&B`UHm?h_W_0JXD>|`@y;85v=Q80>reASnM z)zK)!WtC|#80y*4&uSHdpt_2mdsA4@q2r~{0R5U#g=klcAJbm#dA)Jd;d0->oz8#C zUwNj!)B7u4?$ibKNL!ZU3TYKq7L0PIeR%yO;qt;LXZ^)?0X}cL4#SjA(sp%7z}vX$ zCbn_zWd~M}1ykE)X*Ug{Jp)rdvfB7{!PIsYj&@A{0tfAaev?&fWIW?y8S=zYz7ght zP@7%lI{{8(M?Dv0k*8bT~?Xutg$6XwX% znX4LUbj~#O0o>c7o|CWiI|IeGde!bWtLSR7iJr>=F2t2P+N^3rvus%DW|)x3%lm$T1r~aKT|^>*}p$N2kqR9=EX{pY`K#WozrKcJ~^a zlZ~;_C(d}d(zV*C-J{bj(m)@5_N;87>55Zrz}M~V@{T?F+*w1usSV2(oaTT=#fn`8FAEXwC)atqrty;PIl8|kWq&K>h`a9Ml^Qtyv_Gd z$TW}kX!ftOBJHt^A3ATdz2g!tQgXd>oi>CkY~)C<&79Rfn##Xc^WaG}N_qJPETb)gw)J>PVy18;|qw=$|6zE#(UW&;NRRLpE zogv7A)#(*K>dF1RK2?_rzbFm+nKMIHzP{dyb>}-n1#bwh5w8Ggr+C^a4-ncy!VEG& zuH{c$^)pXczrscqGA{j{e$NDO`qXEX=V|2`%MAq@c+~Fi(&?=-8&e;gOUk|G&Mqs_ zp4qs{eYQtsta7ipW389_#L+&RIj!rrRqi$WI;~VI0~4?6Mn39CWmmmdC$8FRZSuq= zn?7;b+kYkPSp&wY(*rhrY|!~f)lThsu4}d_wehJVmmF3h-chx&#ilO|+tkbbE|kkR zHCok{Hk;Ko*pr9)T!_)0t9R&T!odlfc&XQ!f9aZ9tJ&3Q^CP+q^~DPs;4GTMc6Qi; zE?lE8EXi1BsRppsyF0xcCSFq8m3gMJtr`S(*16#G`ct8WK_4Y~rAwD@Iz+zuNQX^h zS`8k5kO2iBBX*doR^;prJy-sVIFyv{dH zlYFqb9^rt7B|+9mtGZho-v8#$p0!-o?XeR&Twi71?rgC= z-K{qEqaO8MaoT7}_$HQ3x)G$&7AT+d15?)ise5hfqYv7w&i12_RiUGdQ6LZ=p^*M& zw48T2C5>F~XTSNJHSXS^B^cdRAOn(&Y#HIsYUvPHDtlLPWSxO2T(Jx@GGd&S5lS4l zN0w+)8=*Ec%uQ>8DNKpN;^t+%a)FVvM)2SaFkL^Z(|>dx?xBY>O<$HVP1P~86Nf3_ zw3Zt=Udl3Fxr|>DZtUD8&nx*Ym+?#7?8t-*QRwz?g3SQajDpc-MY6XtD}$H|QfNaM zSIFolj@vAOQHCB)d72~7nyHdMVEARhlouEpS1B)G*i+%kMF*o(M$MqKafpuL6Bq2ofAXic`yYJN>bg2Km=Jx+TZrl6B!k7{ z3ik@ul{b>i@>jV#*GTe`_D_NQ6)etda=A=SK-wiYi4L9U(M_LcZT)-iv<1~irEV6* zt(3d&>u6Nnrplf2q8=&Zd0kaJKQ-h2)Hn5+1*;QGBYUp6p3^KHNBf~2Eofjer~OZd z>GTFyX@@orOv~lk6XTj}Z_l)Ql|!aIYi2S-8yAc=PamO;)7EIG#hQhdO6Wl+?Vec$ z{e?D;|BO~bfEB5KmH9woS$%8D#(M`mUhUe}Ods$J>c`79e%!=orp|5jdF;K*pL_DU|uRK_Kl zf7p(gg^2QgfUT(+N{LqC28N0NSw|H+IWp!=#<<>Ct9!wS=!WzO_+4t$iau zJFfvkpA^M7zKIrXyGHpnZQ%$a_?>x+DOa{+>kYuDuYjpua$$7rGz^@hJ$-ihl{2>H z{yiDt0VBTf+hWZ-Hu&-ojGrruGfrhqX-6g>;g=4dwBGL@wA=sAPdmsNtKu?> zKmC;_ZR^jyE5Jp26fQ9?9UaD^-Lw!Usn1e%?oyd?kB)MHS@0L#sxUE&C=>}xIGEms z_wUz7h|M=HGVB1o^7pF#5rQo7gKYjV47< zAR#V7NdL$`2;`FNhmXv!go12dX;CIKa0xXfTuD0xhdck485vzUT_IhRP|m^2%kw(R zoxurz(GfQvj9CqT(Gd>ez+ZI0LVI3mYYwA5r`jJdgwho-1|z9(6rvA2lEDb+5SOyZ zYP*6VAie!DVDfNUb`!qra?bwpfH&bM*6EF+S~-EG)~WLF83+VUi}B2Bgf+>f@$GHi zXc+Am=#2lH=}4VUP_HTM$pMWZXJw5y^bs#;2zd6`B|FhK<~kkdHT=Q%Pua(IH`z~V z$}1A=t4|)aZ)*vmP9u)y`iu6l{cF9meD$dl_MKw`y1}=~{`5zu?4fN<_SuIvT2KFo z{n3w3*ax+2_J>cLv!B`9W)JUOqm&Blsh7{%A8B8tOUoLIY5;FW=}=!dgvQ05b2c(8 z`$96~fZd&;p&>u3fA(yTJ^JV)uFTYTvCjr1Br2uNi}vh+0|)JspZr-@!urc^d{c3Q zk-GOh@D4Wx;hMbArauZQHeqPZg^`b3*h)BKR*c&5$k*;114!d@&{Y#fh0|?qny7ey0 z_#uLTNN7%Df68aJ4%2d%TnJC%xFo-V&df~tfQ4%SBt?|1u*ZO1Eb>?={Tz3m??o$1 zZVC+hL7^6&iXkf1KvswAy6X$^33@O`<4V(q~SI5d)+W~6#b*gp;7Kj1-j3rvV4_)#B$H9Y#?3>^GmhIlX%N~62K^G*8Vr+y%Nfd{+_2}rBVK@*waO*d$ zb3L2-`Z`_MrDZ&YMWGHsHasjFd$a>F{)XlzyL{@L%qh=Xy9N+hjNiGEqxT;#-!zOS zsp5#Ut(B1Z>p~So@D)eBy5I;B0jT>R)!3VHO3_WsE0dOVEQRbuqW77bA z^sM$1SsiE(ZI86GGfv`fkju%^HB+HnB5zRkGk2nQZ`|M`SeQ(_=Qv*nxn=@yPyp(! zN-8YSzryUhpNGIb;gqXz=ekLMs36|ENb~el}wx^HWKxGquljkMk_e&u#C!IuFJg} z8o?4>d|xh>S#_-$zafsw6H(LBvssWu-g+Hsm`nCm&OP?LwcNec`hR@HYSy&a_=N#` z^VC+UF>-Cx7<$?e_ohDbL%%fBt#f{A=&?nG5?Mv)XSM*T!r6hi|uyAG+6` z|5tx%L$bVz0O|k1%hvJmZB9=A{@&lyEcUW7$Ur#!yMOlw z_K}Z##4cUBWKTc+jBVStMP^SL?S&T(xY>@WsYy2ycu4q$h6c4Xy4H;ePHLd{E5Gtt z?@zz?d;iu2SBZGb&V76B*jJyhkALK&%CTmkFr!ua5;H>v9lrOWeh87wAlYAt`w~$I zP|<0DgW&c;ab_3d;!hi-jkWCT4l@YFL$D!~(zwV|?AC=PvsBV?gcBAjB0G|~cJdD^i4fA~gga-wLz*6ly zdWCRHYOTAjdZoYYhy@-5KXOjZt*o(hu%~g14U!f%_-A~nYvA#exj=H z`J?__)hW{)qN7av2j?F@Xf2QIvikKM{#_+20Cnrzt?$KSR;?3pYwq0@HYTOOIB{{% zkLGUr@O@!f3Ntqn9x$CbT~yYzZM|<&&h?Jj=1)B2Yu%h2soSj6V4499KYd!KV&=6O z_)TlpzE|hnI}_4FTS=A9Yky73v8tm{R&3%+Oe<1R5}QXJ(`t%<$r#~nLEpw z%oM7ss~nYq?#7XHK&Kel(j`@MRvb2H|HgF2Zc(>7cu zd{l-35kAahZohr6ojrS2I8OO8*XGTeWtn%}UVizo+s%6BnP&|{jJMx@n}p7Q8*$vS zb&KuUv&Z({zR%6ybZ+m~X4c9eW`zj7rI^pFfASrB=GXr}9exN0F$j0&+t1nKkN%Dr zc#lVe$y1(Qrh1P|+h-aXzm@t#D2<+w2S3+MoQHvMs!RLIQk5^-}$a)8S7-R_cjTpdM%B$+k4*qZrizQhaEq5!Zyer)or)!HfA-S z_{7h8+T;%djR>fH5<29K_i>qlAJibVVNZ7`f}Hjnn;x^vnLgbkf^9XubMkHUT)$oV zz7D-SxW+1ygB_CtBX;S<<350ByJM@BYG6S% z%Z0}gog$jKFyexsZQmAO((KdnDYM(QJGNPcPJMBLk(p^(eU0O4y={}jTJPB6C$WZ4 zocAY#8BURP-m}X$^f_AJuYAo)NR@FiP=vMW9%uI2oVh?ZeM}X|2}J?~b1R`(-paqR zj>uz{00D3{hJ^o{>F6^UQ+!yaMY?pQERFxt_^h; z|EDiaNKvrJ9zQi|cgplhnNDlfiZS)ce%8EZ9#aatRcEv3wZGAGdCF%W3!3IsV`x!{ z9+jTJ`B9}Ip+w=m(T;ZYdfh8WWSU=$q1;W1=oFhqr({0XZ)UJSLTbz$p{cZjD4_>jZkKW@X*UM_ag|9uYVcwGU ze&a=}yM3L*n>0#0^1ENPV|72&)Nt{INr$8-n8!ewnBiy3j8v|>Y}^*(XPnGwQ-eU?~0j`Sw{MEb`r4cVEq=fzBP z?Jt8Ll}8jVoPrDY>#QV*U-XYp;t&=Clk3)WSX(;=3x!AZ#nNkCWsU8>O9H}2v7x1I z+qTWaPyoc=xG^wNQi^%t9R~x$u;tF3yZkSVhL`Q_PE{g>pyGLnz0=GXJ53AkKS%&I)VA}zy7k7|6hOIYP;I( z$p7lv;2nf=~orUysurO$oI8t>X9fwE{X{hKe^UH|-3E=*qey{}1M zq)BGsJN*dm@&Ecwt6JY;4V%{3$uIuM7NtA3_Ct5r`LDm=%UhcteZXG*@88zww!j*9 zthZNx`>VG7A3kEmI{4u(p*b#{4IVk;1Ax|D8+8am%B(QDScF-H^X7cS!Ag>OT2dw> zSpwvW%FG1IeF+4n8%kC)$b4j8auM9{chauJ>}5?>iewFI#qh=SW@p9xH`UvSV6gYQ zWN@)Tj0EekgSs)VylBbpmB3*v{^FTSc1FrTKXcDId*MXCb=FtfS{Vu)(hYiC%~~tX z^XgKa1J_zDjkx#nr0w4#%c5HBIwZx!4vlD7KD&7?L6*YlWvbqmr9^gv34(*mdUoyH zs+hWcO`DpvI=)N&a-aZa<(K1#86ku*LO7w()0sbi+!k-&rX$>`QPK^jof}+i&_Rsk zwte<}R=cLvHG1#<;;&fkx;AG-+dlh#tJZOAT=xg={{Pu~4UE;uIHWLKktm5KmNyl0u@6r;|uZlxRR62om1y zE_ShgXLr2qnQ8C6z286UtM|V5`t?lrOpjZ#X8L{g)mN33HPWC@1hnIHlgkN@~;I-I&}# zO`bY;Z8z|ckw@)4iaA6lqn2U{We^=r1f+Va_ABT8YWH=wsNA8*N#BVhZ5$CDy~pGF zGpYYvf5;`K31C7R|00-t4C6btU`UhxO`;`9NYB4?K1tRB7C(>FOE^@AB;N44p@Uv!fi=VrYy6f5h_-$+d*kRlG@h5GHGefWZ zy{g7lnza??F3Lb=!n;#z!A_<%aiu>l;4l*!MF( z<|~*NzIM{42ExI_MGmCy{nGO=pmzT@b!pIce&*TW=dj{l7}6$IED~!);y?7#d3)r4 zg7H$ca?Yi7Ji=I{{ZywJI;*1Mbbe}@{l~fki)g}z^GD&!?f-+1|njjMpkFe6fzq(@g<#v&R&8&saPqFE|n8H9Z|j0+^h&H;@X{J)7Pw? zjr4bVCargb17#ct9ESMsR4pb$$GV!xA%+)?OuItKK)yx`hHU56=hYq>cPq zM50;&mDDlOK|H3iO`Pemw$HP|quSd)a>y?H{tujS?fGjT4d*-Pb2)T1X&zm2RoS#? zRrHYxom+W;Rv>B)T{*2QhIQVKBILM?8n1|`Ho$bUdE3@4wtF`xX89+Amg8VB#So*} zp?(`Y^Cpc{&9bgC{FPy9n+<50O)rv8ctJH+FvwsLv5C)|oKi0V2gTB6xQKnv*t`?O zQgqBOUF6OPaD9!{g&XH1y7ow90;Q-J880fh;-s||nEEFEOV`j_Uq*!0JBKOvU{6YW zCTz*G!e9m`;zC+kiT#69$YEJ;*K(qr-I1g=)`qY5B&F~{k*Y@}*NGf(T85}=^K*%% z>*72o%P|VMgsSA?)Hr_=Z~`^jt@2A%RpM0|&0wpMh40wD)iKwM*BOi22Xw?NPV8=R zZKshlr>$`}vRa6f(&lvZmEdnH#AlnT1~LzR)s3n_`Gs)x4KJ?;glr-%9`1Fac1>n; zo-FtkTX8*J^iyU5&xBKzKI4t?V$}+jRGxyh5iY`PL1X9e<|eD*(Afe^L+w&qTjLwn zny~52#g@&T^tm!$A-1=-`aXqLl3Ecf&OCiY%td3gkfS?6dqP&=|tv z_twumY0Z1LW)9QFz7TP*pMNeS1cneb^PiBC`U~;i3v=Jne0* zu2rG9kzD4a15!x^mjey<*|q=tYqm_M(D{mD)C7we6eXOGkVU&e2{4PoR(yEkFXD^| z#+_JUEF#yPu%6e?u<^!9YId5_#FJs{su_MH9#BjpBqZ5Kg_DGta-NUKxa9{bhfB6yH z@_}P+f~5JGJ%~HQYV**4^GUn>M=#r{Ghbu%aNM>(|ETYmG#=y1+e9R(mEWchAN4F` zN=JJVavhKFw+rm0T>j%%Z2#w;vwf^?&i}y+_U^y^s@3n=WIGV0s*S2TxN`T%PEVsB zVY})1pdK!fW9J9hGsH;+zLPzdG7jZldx1OayBe`0*Jszi{)Tn_*b&?IT)0s&r@Tq| zrO-iUz8{z?d2rD8`6ua7;2KJw+qW|3K%~mo*4k=w5S@6Iz#G`BEL8>?yab|FQ3cno zK`Ta`ni;fPv83Ubr_r7m9<_N)p;tfMHOBhD2DgT<3)5{&vPvXaYjO;i6Kr%h~^Vbljy%Tq= z7y@GubKtZnjKOCN?2j1YEvP;Wf@FS;V5=G2ZcS<|`%$u+Z2cI11#ziVY`yut8 z{H*o7ddXU-9Bs;a1+O9B!q}`cl(te|11XA~&D6sHC0tee`WE_RKu@{kgb9R_Cop7_ zN&`~+DVcQY7wLSIw-;8>f=lBN!*2KK`GQaXI~SN-Rai5kyI`9vlr#20nc|5!dR~0+ z7!Ve3s}Sg6y5wvQan-}-R6dzhFl~06M;_UjwQ|5ZN>y-}5ua(wU=h$=u!Ag5K^ zScS-~S`I#h&K&^yESZi`!mQbsWiWy=t|YB)Y;!rYG<`t zGVv1&uJ1GcMG*@PDlwV(7Z^>J*=QvbnGT{et!8{h!>nBxI+9)#eq!O1z~t5OqS2TG zdLxQCXqCX6B4G#&)W{f$#2nPX`eS1g@tCr6#^6tw9gILT6FrNHWhy)}Ky~^&mwFBm@}u9@yYN%~(!oE&#^=sYKW#PbO%yW}tPp-NP0TP7(O8a%x4vh> zV$;&MRPWv2f6F&IcXMJrKTfDq@>kvDc9fXq#ynL<@|Z#;wNeHGaq05%DdW=qOkfH( zjR(2H@p4V>M;1dMuOktc%)ECNkzpp zUI|HG%n9Rz-W^f`H~RVD;a=&ntr8ge5aGo1VrsHteG#fMI&aD&u9XZAtopUhTtm(Rtv>q z4q7dUVTdVc)qv(!U<}j%gh&h_!xZ?CWej2tVp=Hzopg{ulrp8v6g||fUWL2L1>+Ko z*ikbv*WeL&@)JXT@6;JPede4UIds4tee{Iaj~I$J(zO>7+js?3U=+i^<~bx*j)se` zJ`^b#d@JJLPJqe=Je}2$c1kpGs4}a5^^}gkG<3vR46W) zk28>nYb2EM{cb)()saKu35^~@wSvexZ`$NX^&c8TjVT5bnT=L6@}n)a$g0Nu$!f>b zYDFLoC$jXX7(vi@@>K#Ws3OE>>bUXGJC38oPxzlj>Xtsw^cJyVd2cgXD*4ten@xYZ z$6QWj)2dQ!>hBDK)tu_S7&X0mPGZN)aaN0aC*fl{_;;K;9i;g}L#p0o7XH+K_y+c@ zDqX){^5reh9I@^f-nOg1_cE%D+?#+{wG)Zv$50HT@$9`*#kC6h)qKlmJsh;sqI{JX^2j$FFm=DADdk^la4&QMPN z`d8iXqD-sI^o-ixpZ};c5XbK(b+*=eP&>$Ndg6c+CkpPi$JUxY9j}XOOetG}9NX2pa-PfaLfZFpz$KR|nA0(o-5Mddldc8fV$#hdwFYdZm{OFhZjW zj+~(S#=F1W$DaZOB7+Uz#3;JD!bxt0i;=3VKD*~VGLcN00v@rM8BC4X9#kX~rc)r= z`h%$ZI4H)>UiS@32ck9t$20%&TQIE>+yD9J?X`dWpWH^4?(I1HfBmsN@_&5E8h326 z@Bi(8!C^5D$l-?Hxy)WdIdbuzbS;?+fBZx69=4r-|MRZ)`T9TnEw|)5iMpkXF3N;< z|H+Fs{-bU?^k4miRsKYsz3^ZE3tzox)icJWP#^f^Uo^of!)RkGo2(fE;yK>okJ^sz zq!Z9;52=>ksf+a~IPQT6{0Kw3>Boc);R{tm5z zwWpv}imVN2^{6xrJ2&~MZQcLkchAs&WadT_lJV;UsQO}WjLNRx)*h@EpsGK`Np~RKj!wT{W~{s4#(`PSH4#m_IG^%B zGZFQwq!GQg5)qdCf#@coJkp&s8~Wh*R1^{aDXBJUV8RThFySyGaZQ51zE`6bDdsUM z)mT<5Rj3(PBi50dqp>EC`D=k%hx)3Fy)DqdBba{BhI}<~t4JT%lPB4*pY#A<*(`b4 zq$B5jQemapbNxEEqJeWI>XGg3X!9`Zaq$@OBSXngs&}OkgV7bB6R(`MMLK(lh#cV> zcz5cYegenMh+G=5Egub=i6RIZkmILsSRE&P-}{ZPTPsEtcYd7fgTMKzH9WQ5nTPNw zYp8Z2BN)>c2RTsqd1oHNO_qGO{e@?JH6bEWy{FX=oQ+^&qW;(xM}H6o()qbZd~k~( zm2z{T%v`9BqzZcRYp+=U4^G>jFFoh+H}Xd-WUXj>WsR4EgwYg6cDB9t+$6%1>U9{a zN!tLMfdG))fxUL9Ym2F_Fw1!W(S`B?=K_ag)qc@h$_JjCSxQ{s{Rfl}g8AiHFRg_s6#T1QloWpQ~sKgF( zC&5wtZ5WQzszHBqbfj}w2pylBwU6%DV|5VMD_9zR4;5s&O}Zeeb2HX?7wq-x-8KY_ z2DG4_-m=505$RkY?U})Tn27dOHrA!#rTy#5MG2`4og?Y#>9tW*f;%zMEe4{~=3*>j z9{Rhf1BRPAQN`--F1X2lRs2n|QxWn&WadQ)T8G7rowLi1p!$;q_YFj`3 zM2K_#npJWhLmEAky@OWGzK?7ro%p5CGHVSt$K8#wAm*oA4&_wWMXvGZuWSBMeMx*J zEv|d-*d=6=5e}L%9-J|*v$H_(y$tLHb#ih$I*GLeuN4cq(J_LZ5X&HD)~PQqSks=Z z{ujcv7m7LhotynH9ag`!6LDFvP9O%Wcy(Ap&|f-UE$#27)IBi7Qy;5t4f}BlR$A@1 zRblHYI)&-h$!LT##?y*u)8hwYG*2r-p_`sKlnE1)k#=AKw@6 z(6FC!75*YZVOH~C6-d}|g)zd-g@b=9q=_ zqK)>GhmPAHzxyT!6t7!@4ib{@M!9sY%70ss6Cm0sKYH}A8>M%f5@I#zihdH4ErcMeka8smAsLyfBW7o${*w!CAZe2h1 zsGa-G?^q3YQ)~a_;FmsT9gpp^cmB`cw|D;SA90v+)|#H&>0&OOFxEjZ8BdgxD58io z+GN*Eaq@hh?fSw8eP%m-e!v>P_`Hi47KUdsL}T>SHETMu&C~b1c*^hG)>Xlih%mN% z@Hm5-d&QmMs?LbUkC!(VISEVGO`&*V#G%fWz}y0HG^wHkssXrvTJuk=cO3=|8|iCK zx*-w0oS7L$DA#QPrY=Q%ql>FC4~zAzRTUZLhpA3;!gyaTOhh`3HMmWL!4?g^H zdzZV+yZifGH1+?!@NN5_Prq$1efH;VZ)>}?aNto);+tpQwJuCpYd>TwVY7&|e(ihT zw10Btg1zyDU$8@Mo%Xe}oJQrKCc0Xgsrh5;ZMIBexRyjHByHvP(9ckvRO*fp;7Vee-?WV0AClqQvCcycmP z^3*^6E6lVppTMPFk{`?MK)jVhpXB#zq4Ng{3lp-3Z>#o^xi)N5}ak615hoLAJda!?1%%Vw=%p|7TMC=9Pqyux8g z`ZP$PmorGh#}(yUYrHhRY*eGLuV2)AqJ4reBdFx6#dySEZy`5p*jyx~82*_Kg5tY@=Qn}OO$ zl$BeiQ%+nP*dNip#qil~7o$jJv5~m4mO8}hKr&x3fhIQMW0+LBCHrsLzs=>{x`;>n zEmEm%!T{nTYP#cWBuXqJi>OVyu@CFG1f&5I<>yQk$$YJ+BREnU5+3@6c$6Na-+SHUf zugW1a8QSQaBwR$Lk-JP(#)%t);4M)o8@X(VsxGu~KbDxtsDruHatDZqvB@e^-^~ZM zabHQR*Hb=GTua-FsYkW!wZz}(IO3aGm+ryY&_DD2DXIawfQzt1KsvYt=ABcgY#1@t z_QQv)v8lnAXDc}A15iI2NR@1E*CB3@Ojv|n8|E@5r#IWCYLMhlSdzMUH#vB z;XC%`bqup{2ZBV_8#!dsV=?VtSM@3(NJ&hjJr5Si!IE$WNPrwy6Pe(Ak0=9uV1P+P zjW!@%gd@HIT;654uL?#WCstMLH$`kzY=6yc~&NVX?kc*2uIyt(UFR zO0lgZoHyJ#$c^lsYr#;N-s?Qqg4dtLxf?Z5u%+n&84*@BB9Oy-_S(R}fOU0rI1Z2n zo&}TM4II}DQy4iR<0%s$T0%L)rE2lFC%UH|ZzI&6mxawCkZQTHvafC@NF}-O> zL-@^sU5WX0kE<|L6}z6yZX_yP1Ue@zVOm9ON~cvsCIy|3Wj9$BB-gfZy99Fb(%h8M{C-j8*lAz%1`|#K66uUNDs~h`DbqrYz>5-w-P<4EU}Z+ zU>#I{*$(qyb`mcPsA*GsaAKV6Xi+cZpY}1NX?6Nqw`*3(vTQqAS-K>ro;{4e{>+E5 zdRk%cu&VgOci*y~W5fJ^`{d8Kt^1eHp7sH#VJNZJFMi;8!Yk~xtC#G>?u%Ae!r`}- z_4h!8sJ&)TB9PWq84Ob9OhnlI4-B*Ixe{HKDYP;^tq>qgvY{0jre@H7qMf?*FbAtt zAz?P&28TKRd3nSz1q*3@gxd_-XI1QDX>(alSjfe--2{RR-1ZFXduJ(U?^A+w{p0Y{^h3-lC6E?*sCnv2LEv*XP zZ%mBYzx=_stO#+47>-P6iemrvl^@u)>RK+NnzZh*QEP_T{PXXA-AW{S@wmmV z3M_3Vz2B{RiDg=Ml_xDJamkx+y<`3T1GaO=b~|$9kP|m)>XepB^)ZNDR)eow7bPZg z)nAEbP$$$XCiV#(#wHqQ@R`YYeV4FYw2~5+(*g;)(fyu)P7po)UOoSW;6Pbn* zdZyX3L|DQvx^&tYmU${((sv5ROW{g4MLaL*q-8%WJQwzDIQc^dObr*>Ky@Et ztmfC(_)Sl_TcwHd6*u3n23M9F0xQ8K=HaU_kkbS*y7*1+Yw~-qij7iQY;sD|)Qa+-9Kja}%TYh+>l3j4n$ zR))!Uv5RSD%euc@o8}dg|MP!}_!<;tz|dy9octxF%(|$tSycdkUU?9nOilPVZ1u}l zmZ#eIpG;n>fxbn?B9dBpM5h&ph=C64$wej-zo(yGUIpOs9WPa?za~#g9hgdhsunU6YdLX^7amd0kA;zqw=L{fu@we9&fLuN{}(cZ$wx@M~7Fqa`tu~(D6t+v)KaY|ij#85;uwK3m2InG&-O-`J?ed(;# zaQ5U*OSbOxH%p*zG@C>XBV1pr6^P97tIV+tUHq1<&K#eTtGphUmzXB|3}Nv-C-HrJ zal9#bDF;SNx9!44QngbTg-0ixA2hrpZnyJPS=2vy;L;^cprwN3&5q{e`>h_pW@piw z(7-%;E4w0J^iY*+8}R6RCV^oHQUKO8avwBeGfz{ zi#GNX$$u;`J2O>CfB3@pc6!MX%t$?jY zOT}C9y-+}sW>uI;8pm0F@Sev+$Q%*tg)(};`}(KY-b2m*y+O|KKqsErwI`TKk{OybWq)7S!L1ADD-cC{ z_uW%A%B4hY9c}jJn{V6Wk3UN1;g0Ovar8uWM0N6F?H@4EUGqhQgmC|seiGA3a%u#G zZW;pAUNVGu4pSs^FpTk;G3&t;iTq7;c3q}4F*9wGtUh!->l8%4pVhb`w2j!hgiygoc+#X8SG zc)%)+p@Y_V3-h$3gQ?6CBl$BSDl&bLxpPAaQe{q{tbE)gPVCH^Hg(!3f8xho<}VyI zgOWy;#9Q*mc=^D+Jm6a@Q6BN6@*C2CC*j^!BvSZlF!EvEi}wuPWvWlamntjxt5sB8 z5pQiezh9gfh@9GD5yPSRE|y*4H|a9@$yU5ei6Y;HzM#+uIX?4aKHTpN9q;E7cc1F( zvl_;*q#ReF7j4c|AMWyP1or8c_j4fXn4=@Z)`%g#Vs!OI;3Nm#yLYAD{jCrXfz)BH zxv^<`_bWfJn$~(ZMFMM^shPJes zO0^j+MngKyfESn{&HC!gmy95;gj!1;+8n;n|+cuthPtN^$JCphtbZ6}^WG}PQp z34fX?NW`aFmy6+g#=r38!T8PKmy|J6)&#xy{lUO?W3b4UDfGa6sNUrHfw%FV%_lFh zvVGq>MmBq?(#*Ux(r=Z}fl27cg~5J%Wa~B`3mPMTDypJjTj%z@%t`cCL_&SNeb%J@Ad6QUK1n2h{Ej3c6+b;?c* zL87nN;3BM2NqrTjQ3kvYj4e6?%k+i-Ep!k3j7ZN-Pk@NL&JkDn7PG7`p<`8Y{cZW; ztW_^h+tm29y>$-eFgn5Cq|J`c*c=7~7sfFUKRJ&H4|PsfWzJwf zo53gq_il$}@Mba)@MZ$xQ;(2y#5>N~?>0X70=_nJU8USt2!-48ZsKw8bG)0SDGWQ% zkRt12^ZXir)l5iB85>tehi!_`M1dI57)vvYHa#|B(+i_EIWmns zl~McRFV$NM_nS}6arOcx!ntrxfLGZp{+W^*{_1SDxYA}z#9*-7#i{XyBFt8Z$%vgm z?bN~jkG#YRva6VYH9W%^hQwZ4F)YDhmPoh2pT`iMa$*krm2!tb1vea4EKXY`cem@W zYH^$==T29V9s)FO-O6ER-Mfjsyx zuCx}nhG;c4HEf{Qy7(!ARK3T)7?%Hx-^GL@PBBbzKFI_o@K#IWYn$=)p zqdXA@m2g>5SqV46wN+b36DqcIkao=~A=*nz6F&3Rrs2W@o5I|GKEG6AbIYZWY%!N= zm`t_J=np0{3mh?-xsq}iOrCDOkt4oUHo!03uX*Ao^s>n}DBbHfO+jW59fem;*SJmO(_5y_q6b zH^nd~Uxm@JR3~2kq!PkIvtQVg7C}_j5}P^85HjI>FWoFB?IQs#<)CC50ilwhyaJ`1 zZ-tP)$K_ao;2Ht~+BqdzL#l^8N`AxSzTZx5go%evqK{c$-+(L8^s|vH>4a_}%;O*r zdj+nOC`zwLHA45VP%k(XXN)7{AjcKH- zBDb5SzucxjQnmzeM9$|seDlvS4g{X9>f|rbNG;k$EJegM03>d4CZkPk#7#?!BXojr zZEdj_L^ria#UmDH1X4T1d}iX*Y5X3Sy<~%f0O(KI^wuqxLyGI$^=R;1E_{eeZP#!nCe|r zXMi(fvTT9e-|3Cw=jWj?<*)F9eg)nH|Fz&QMjo3XKmWC)Psb^UV}iTB6>^!+Ad$mB z3PrxxVHxDskg+N+GMXT9QED}{RxtQkf`%c*MmkKSm=#56S%r$I%<&r4Jhfbn&9a8f zAFwDL+#p$jytV=jH#vzrf(o&=mREPqI2AV$|9Cq5F_pe6EQ(K7hjeehWbl@f1kL8E z(!w(t9T~j&E~od0-NiOgyQ0{hEl$#HLkaJ+4`YQH@p3);Y0b?|m@(E~2WjIrcmeQL8)YuY$gH9cDVoO22{nk(euyM`YV!Exrvb@CJff3+4sm zteb&62uK$!NhVN?fx(4{s)&vEB43e=u=42O{)SQU6j2+VGwf${a)*R21=30ck=gAs zkOGyWpgSGPfC#Pt{LCi^qh{AtRaO3{aLt&UfU@5t7YUEQtnwpmH7CkWoWwM;nLdaN zBo-rd$#;5zu!DW^`$%nfA=0P-C|X`XAd_?vcE>J za@biu4EaoDwO+q4S#O%P#J?lBUcJjO3N)^Cy#uSR8P3~iD3nnrR|p?wBVE^9vj)lF zpT_Hs@?T?}sEJcmpTXmH^1fLbeY=%gHv_p9!2A0hVj4?5m= zQg13-rmYe{IprlRO;~O>FEJ2lVG7#Hljye#^}$Z3{-nBDRnYB zKWlwxW9?{e^{}=6#CBV2{D&qURkLme63EMgTf}8lY#Vm}MXPM)wE6j$t*GInoITlM zXTJUStcmd2uIIUym1|JZ>Avv&FWSxz{iYQ+J!NBkFIauk0SY$H-#Uk3fl3jC0>O8! z;-)!4fA0K6+r(i|XCQ0}1@k1FT1IBB!BNPGFGzVnaik6_voA~7GSR*BUIw0;iOQ`8 zqY&J?9VWbsz+3prJiiWY>co%2PCEt1_uPDp%}X>1Jj9kc8iP@6#$a z>vC5cAzXPC?o-)x50Q_$)du<$BC6{!3GD-FPf0F3!@l5kEK?|CttIz1qmjI2lt;S3 z9Qwoblh>~()VdkS16y3F>(2LE-PWJQF4UyWUHZP8I?;hYpP{1iE8~e(jXUk?8~=tG z#gc7)md?DW^zLMSoGee6utU)3b5=a zr`Id%cUt?-r)*(@3#R%{+1AJYu2nbh#Q5GMQ|@{zuc+Y;iPvpv_=+`jJr3ft3Gy3L z!i|Blw)zyYR7Y)Ss1J9c#F>sb0PbeQAbyMZCP|!m#I%YN<2k?w;U|_&gWE@cEI~l- zfx~wL>MT>H*ho1r@0@AmfGhBl%q5a9x%FKj`a=~IOVi6`TI1N%lUX9IJYu-Um^RY8 z$46|GJA|B3-=xaJ1gF@}q7MD5-~YD#)~heGe=_akG)`_FjtYu35V1|Y<9*;(AZn8o ziV^FZKeaQCHr3+%m<^o&mQ77fTTNT3-y2<2xdYo=)z*9R-&yP4pS9WPi`KI95e^k1C9Vx{H|bj&06hyLbktzVq`Uh%=C?a+$Bu0o5e`d- zj7n}cRo18UI@~F>UHN6w1k(VSGzyb#uf?L(#b8-z_s$pbUSM{oS)I-XfvT^fe8woe zJv=Rnxp(;8jhs{-t+;}Tn*1CzYSin(2uc&b{FrNbSN(>Os3*RJX(>F2bUgUM{fX$W z0Yi^U`RFlrnFEA=2soia(WH;cU&^XP*F+3rt)sJU^%Lp7l1Jn7{6N3`^B;c4#-=B1 z67_gl4*yF}JdJ&=yL-dw8j8G*D53pBg0w$`teb(X1hEb-m(D`~06+jqL_t(sZabcGgUvO^^PzO^%=Cbo;!G4fopg;E$|*$FtUT^cQSlZpwx(e$~p#>#e5l zXRP)3KSUHTW7kjqCMU4lfiJPZy4e?L?!VKf=RBqt=FuK;=hYR4dLRacadd(pqTqyq z8jgOi>KGpC{bq0qL%(~Wj!S~8EHwwbg}?Gl@J_zpJuL1m$`|$P!$#&sj?<}V7!${yvc48QwrX0CBV6Ljw||<^3G2di?iZGe#F%Kdg4;C+W(l~ z8ixzRgMNTekR+n2LuJ>mz=rrstTnedXXi1jczDz1m3Vp@ovSLbAuRa*<{PhI>3hPj zm}uZyiSJ*%Uv%PJI-Dzs%`zj7rhS;fzCo ztC%W`{-k{c6TwDS1r~oRt2f!g@Jm+8$@GTCb{jtRt2Vb(YcrEKIBAaf3L91f@BA`W zd26whJHV{2!D{Ood^4F$0(gy9I&*-}Grnh@n%U-7W=I%*W6E^c-SEemVQXuPedvSF zdKMa#+5^#khR>XswXWt=0^zFt0}Wk&GMMsBnWXm@R)cjf!(+{BKR_i2re)&km4J!r z;X%XuPU=Y44$Eo9$XsC+r`8N@x=Mtb2CKj5;NUd(%T#a+-^j>_sc*@;ffOs=>A7w@ z+uN-HtJY#H-q(Db+!SoVk4h`itB>CpBiXZz{n~>=kz$M$Pr%?}k67_}ML-Vh%-3S; z9UsG<-7qTz7$iU!pec`T+Q+$&QG31TyfYA;PJg%ehCRRcfYtdeh1xgO>O~|%%%hll ziWcm5-+tZB4fU89hpc}~O}>h9eD%yp+uzaozPVdub;^>d*j&%rn<+Ql?Y)s3jaXiP zfb$w7wlIB##!8IIaR^Xit0I{_S>~iU=Qx(JVO3nlMOgeR;v9%Jx|WwGQO~V`X>cuN z{c%jJOn^Acm_r&^PaisNN4hrq*oh*wNSI@DM82Z;e1Bjbx}JL0-t4~O z&n-T;igP78d!ou z1A1rEkGGfuk8l36uAe1tBjkz&tUJ#l($Tph*;$LOjtjcpV+Q`KKm4x!*tXq>WA@rM zu0@aw{Y<_vg5^^?WnAJ9djx1aaX-#)y!J!;ZudE>Wnw2S$;Y@p_W3;rZ3kzWG;3eo zg?&0rdZgtvjat4>97#aeb@7u-+zd=iB2F6jek{@3j+Qpt4s3~;#H6&ZqzTsm`x-A^ zy=3*^Py!=ZLQ1if@m^XnXZ2h!(O%hV!_b=&Ca`5i^Y+cNDCTg5wl1nuJ`=MG)-&F3 z-#T~3-r&mZI$+AuOSzbeF8_j4DbIRVa$nHHGmurGZqNr6 z)}2=xc4ug~$#HHUWLe$1>v=ZB>DZH;9%uDYT*Eq^crsB^-h7CAv6~S|jj~#qqD~OQ zR8-?f{58)-PCc(dV7O<5j#kEbk8(E7=O%k$N-dmFpMiO>WG}0N`LHUH4JrPK5mnZ0 zCk_ogKMAuz3^sPns++dcX-i>5r>v&76C75`GgS^%^u2VlrW`~9phRzwUw`U6s}WLl zB*}jjPaQ~pe+PRn0}+%y!o8Q}xmjKjZ;1@t&meRh^^!QfKWKPI+-~Qoc92v1g#9{y z(+@88I6cFgd)Dz2`-tkStEJ-s|>6 z*H#F06RVM+;E^tdN;o~gfW4|oG@>Tanwqn5Rx)$stD4cQUy};024s?b9wm@DEO6^% zQ+XAPZ;w5*W4CSL+U_#9rfv4(rE_*=eAt>1IZez;dQonjHJx^H@U*p*H{15Qt#*B? z2UXs3zhvs%P_O-KnCRj5F3K`%S6NwH!~mlvZMCwWC4|92N*)?lA_h~eZd9Hrw7mY{ z4$WTVw;&@<}a}lm)tjPc?!*{37Z~jn-qbRcWT_3UuWRU$AzV2F9 zT9gc5`HoFbvf8NH?2Kq;qSq>FT47R6R$bp|gJ-`-19E{3%w>kdhFW@-R@7S=E0LLr zA>d#(e&RY8VC@F>C0m{^vr0OM5BD=NkQHU}rcz60+w68r z>I9yC)4|o&>czpTf%-T^N#J#d4q1snHbS@-U?~6TkGwafP59eeDtZ9A-y^GKTQ zhfxrS6fX($D24dX+3&phn!VJ0(V8LXV++$X9pj$gclT z?DKr%;u%&NT6u6~ge};1Rv$7Jd1b7}rYmRdVDn*XLV7U0^&tmtVK763H=fC5{-NuGyZUu4Z7C zLoiF-QF4W=yf5)5Z7@HFLY*}ua@$(pWP4jXY&ZK=x|+KRyLfRdNW)TRSEiYSivb14 zwG-^&G|*PXtg4tbJMC2?N1T(0$s?926-uEZ%SwgbV{cNyQlG8h#=@ZeAtFdcB1&37 zOf-4n-yy2dCa+|d>c|yN9K}G!uMFDM!~herDO((ojVor(6-_XM5gQx2iYRJ`8w0DX z64~X_#Cuk@j7VqnG81|5=uqQ&ieATRRRFs7zF1cYiVu{vy-GF$L|I4 zP#n@KPW)56m?nnB_ku9)M__`tCLG=;5*#9)dcNOWEE|neuxMT-1;4C6Fz#yvRuTHR zpdc{x*?cX#iXw(0QcV!6Ud~7S-*3EZ-(v-F0MX202y+L7v6h)}C1N4=b^gfv`Nvs9UpfgoMJaY1X zBAgT4^>eU!n;mG{k0@!z4z}$fc9H$4=al{6@&)?__#&~jIw_ifpZby}Pn&Bt+3?&T zE0`JUpB}W`%s{u)Znn2(-i2sFOZX?Ptf~r;(7?D@dnL>xU>2AtqzRaUTMD5TeG>z& zMHKkjjVpGEn+~VTGLB{j=EFH__>%Kanbh!BTor>TQk@K|6)#l1w}C^cZ+Q4(0r zl5g@~g_(Wt>UlQyE3K=h-rBh|uR`_CKb>nC;bg3UNGYASLF5?%0tbnL# zR(=D6gXf0_5O1AkW?XHXYq@E(x*9PH_wz8DKH1-c=xKtu#q8&l+B2ITv8v))4(RnG zRbXs!k=r!oYpX)5ueaTv&njzccqdT;QOb+GrM6+<-LGtU@kt7*1s_D;`^UVvb8$sNpN zVCDv85u6KN|Dk@+SR?*CN<7M&^?M-f&Lp`0n($>9WDPd^}pCCysKU+sC%W;3ggPifSn)eqppI} z3N6BlHKi|oXqV~UlW$%+Yfo+7Y9C+)sr@W5{53fcS!2vBI3d50PRqekZV&u7Uz@kf z6Z6)_a&b?~vhC_9vK^a>ZF75xb+!~+OGB}BHnK#l(p9lEmTKD_8{Sl=Z#e0hbf_|k zo@kVy;t(-Ts#Nikg1CkiRqvNDk(Rb+AZAOcLv#yV61B)Ryo2LQcBy~SdIp#5S|9$N zWxK?7%;ixop6^z%>QCQe9F`VCxz9tgbccor zkZ_%zn}%UbKpaD9HDj)2&!L3zB@L&iH$CQ}n(yCu(MD#+z0XZ0`pNZWb#|n6kL_>T zXI+S5V*(XhRQW|vv-KDD_~sC4GYy2`mRM_by)~j(v$K91_>|d;H(s-D#MSj^ zYQElg-ut;T5%M~PC5rk|t$ySXF|oqxs@`l95c@u<_-oa`zboU{S(R*q8FkrNm{ApY zOmX7CBQ7#3E#ReBJmS1(Px3C)LDn9mLShM9g)tBa0o=Sa@ zG-5mwyJ-UO0$O9=xO~Arg#yFpPCN!4T8(AwV&iHNEf7@e%BSpC{#==LwWGRQO($)j@bzq-S0g8_g!B7FQDQ7% zVdsNQ6^A1bwwlUPYp5=^+NxqZQ5P7Rq+;B>yYbFj{pMg*Uadald=Err3*9AC#vBL<%)F{e zQqa*@Uwrou{*v*zC+6p$lJpsXNz@o8J|l#fUrzkIa%bX@D1$fo1b-IabpGiy zF|V6FWBm9m1S=o+beux43&P4NXtuu7={=vM4sv0nV`crShgfB4e>TSYeLjvnnFxy6 z8+|8jhJDCG&HL=bTb{LRlb7vf#6d-(1cU^fktDiO|vzL6OA+e4$ug2ykz;^(AHnB3fXjAOd>}>3^!!3u1 zN2w|Qj^drJWPs3{8Gku@9d$N2KgH(y6-4G!FtsLYK`Cfk{TADeSZV<+&_z}$TH#DA zOd@6)LR>cDD;W{@;M5DWLQ=ghgQHd?qH&RhYR{N`VOBQfthkyn(KIkU&dNX&!ZCB7rDutP)Mp-~5`Azq&h-FTMo4v#idb2p|{zd^r zcvT$1FQZYL%S)}b!R#RKb1Z%55p%V$!B)?6att|P_q2_XVu{05MchYT3Q;dZ>{VG` zW)*dTv6NR~U{6=nO30Zw>W>Y1RuwRWc~%v3FsR5V7Vs}mvH!t}v;@W@E-aB$4V7D6 z!^V75sXel%+_qI{>FWL72bJT_dp4Jt+r4>eAgL2vxpLLjT6gc-=}aW<4Tz!0c?Y7- zf)lpEuT`L6c-`m+7nzX4pbm>~P8`TPq)*@_@A?*TbD*pIc$@$%Bh@ zwHRMbN4@VtXv6-<0(t$VqmSE@ z+jpX2#3uV&uiFpVWFLVTe(mb_?O@Xmn9FYKM4{!>$Qjb35e0MFQP+uv)HYvnOrc3R zFfAf{5pmIojq?r}1!ssOVy!VYk|k=ZL*nsz-#Hf#?Qc4WitZ-6Fec`7)wWf!N5URx z_rwjyE%Y@Bol5M+^ffzPbHa8sa1gK(6<#*lyT@Cw|p#A{1yt-h?$I%=9&jf~mb zL#J(oRf!H3GQ}j1>oBZR_KX@lUOh($YEVR|D6O`J@>bhjzYCM`!*&h^)icSR?B4mq zK2r@EqIGDXj&LMll5&;t&(F(H$CEIkuG%I$+H%3H(|s#Ni4K~(#Xm%C^5xW44ot0pE7t-OiU{~pRw;3D}QywZb zVOx5Y`gIe$fSaBnJEi_~(aEY&Q%wUQ}iWud_nrDPfix)NGpQEui5h#^Yan#A2;4%ZzicB*UefQN_^6tq=Dq^P9 z-aLa+f4U8^1ZJzakvQf*Gg%k#DDML3<%W^Y$F}_3!bVI~flDUWWi}~rfVSNzL>gziHF~RwW z`g+8{5Izx~C%5gecSd^ATwg}T^#Yb_2keQCWA@yZr+syDew6c4E{0LR5(6qi9k$7K zH|*e4HhYK6++XW^+b$4K%tmp>nb7^><4@bu$l#?7RE5IJXAz_HV-@$(aKBX{l6i0F zqFsTgA7V57@eYW1{UCM0;b8VEhGvFYRlEU#AMiZ@5kRdPDiY?T*?B`{t^M@j19lY+ zrXQezrTvFjd(N_n&7JR!yX;8I5m(i%;u5;PseTSNPGdZ=+*ed0zfzIyhdGVTj@sib zPul20$n)A{5BabfBIYEbDfB*JD9OYsS=CggE~^tw>zf)O9(zb?L&`7aB+*|xE1$iM zyIE~LiMX!U250+WG$JzLgoR%@s-d@*b`s!pp;O(m>N7^Ls5|4D)Ps&ksW2`WI}P z3S_0r9mj~ipqOQF{t{LwBAzbxN_4Tk46(LSPanhy4tKMhw*y-pMED&(to2%82F2_N zL{lbl|Dq7=SVR{ksx#A(wGo79G!UdDf(|6+I}z<`17m)Os*?N28|!M=tD*zKSnjr? zxQ?KgjYgEpvhSy*vR0jVdG0nc_XxgPV0vcGt_@B=z~^n}&al}E?Q)k?$sJ=OqN0_@ z)vMR+#*G{H^wUo|Oc~ld(Aj0jdp6n2-Iq{nt%hjM*f+XguqWG(*pr=)SSzcHF;*f~ z%&M!{Tc}4RUe6JT-CF}Et!KL5MLv}f=Th2j5KW^m9C_59+OeAnfL1CvE`DpOsyP^0vg?>65}}=A=KaEr_kh6xKPH&kn3l*%w86(KDfKL8)%5d^ zJ#9xeZNZ@7EHm$AU)@LzS$MtF`#P(mW;@Wh$L~PDf##>gLZT`z2BKmC=`;FklijSP zW?-J@Tq&Ri!Y-31`d;M>2M^BkS6~Vks21?R2Rz-11-OOb1@UGAr+t>|Q@ysWc9ZRF z*lumrZLab>F+XZOgij*oYU5CITUCc`=lckZvm9;EYE~zwhELlWlns<`A6jEibR4%2 zZ+;q0ws%|{wWA)!0ka*P>9@1P7lny4jtV9QVzx6(0*7h0qOx{-e7TV@F_ByJC_b;7 zfrJ8k9X&WdAQjq54Wtcm7z76jtC1@SXtVOAQl zK0C}N`<3}?h-I#T-XcV-8vN&d;xUz0h_gu{yR@TM;ND0zJs zEg{{-QO>NUx~S5wqqeWrKq>FDAC94aaPKV;#EU&a4d1j3D0XL97jP zLNeR)Y>ZBX%0)V53BRQV6)gA?lj)OST2^Stn=Mu<;86e8=lqD=uBb zt3!t0?0yMlhr{-vtxqBboO5P#ZuGn}DKS~CCUqW1+_nO^i!kjm#`N}J^AL9_tpZuCtZaZNOkAKvP z=pYm4-m-~T|Byq2QlZMq%o>IFpyHZ2>g%dD{; z2Cj6Ty6{h60-+2s#;wx?ymjlM%I{A^of@3NF@(v89XThsN7xV3GtC@SuI75~v6;DK z&9$7DfC$W}ywrQXyH)+=$1n7*yzA?c)7RGcXVs}&*)MuZ7OTiS1X~|%uc)s*WtOHm@ave>kY&V94`5EH8k2scI~rY zd+kL|nPZ&-BCZ3-&V6+lD zHF6eF*%|6VwaP!3f>sRL9}zCnO8vq0w{6>a9})tzztAXS*Tl8h1WRa1MCT5YrxW&) z=16+89;bEM!{b(tlU&^CRYeS{#*hC^YyH^gY_WN>ja~U;tN+l?SWyew6Tkf{w%piZ zH6Q+xRX_T?ja<88m%jNsR{Q94Hs7?@>c!(LJyM684Zonv

^U2q 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 literal 0 HcmV?d00001 diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx index bba1b1748e207..2a5023d2dd63a 100644 --- a/examples/embeddable_explorer/public/plugin.tsx +++ b/examples/embeddable_explorer/public/plugin.tsx @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - -import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; import { EmbeddableExamplesStart } from 'examples/embeddable_examples/public/plugin'; +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; import { UiActionsService } from '../../../src/plugins/ui_actions/public'; import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import img from './embeddables.png'; interface StartDeps { uiActions: UiActionsService; @@ -30,11 +31,16 @@ interface StartDeps { embeddableExamples: EmbeddableExamplesStart; } +interface SetupDeps { + developerExamples: DeveloperExamplesSetup; +} + export class EmbeddableExplorerPlugin implements Plugin { - public setup(core: CoreSetup) { + public setup(core: CoreSetup, { developerExamples }: SetupDeps) { core.application.register({ id: 'embeddableExplorer', title: 'Embeddable explorer', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const [coreStart, depsStart] = await core.getStartServices(); const { renderApp } = await import('./app'); @@ -55,6 +61,25 @@ export class EmbeddableExplorerPlugin implements Plugin) { + public setup( + core: CoreSetup, + { developerExamples }: SetupDeps + ) { core.application.register({ id: 'searchExplorer', title: 'Search Explorer', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const [coreStart, depsStart] = await core.getStartServices(); const { renderApp } = await import('./application'); return renderApp(coreStart, depsStart, params); }, }); + + developerExamples.register({ + appId: 'searchExplorer', + title: 'Data search strategy services', + description: `Data search services can be used to query Elasticsearch in away that supports background search + and partial results, when available. It also automatically incorporates settings such as requestTimeout and includeFrozen. + Use the provided ES search strategy, or register your own. + `, + links: [ + { + label: 'README', + href: + 'https://github.com/elastic/kibana/blob/master/src/plugins/data/public/search/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); } public start() {} diff --git a/examples/state_containers_examples/kibana.json b/examples/state_containers_examples/kibana.json index 581b399e3ffba..66da207cb4e77 100644 --- a/examples/state_containers_examples/kibana.json +++ b/examples/state_containers_examples/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["navigation", "data"], + "requiredPlugins": ["navigation", "data", "developerExamples"], "optionalPlugins": [] } diff --git a/examples/state_containers_examples/public/plugin.ts b/examples/state_containers_examples/public/plugin.ts index 38ebf315789c0..a4cf7dfd798f3 100644 --- a/examples/state_containers_examples/public/plugin.ts +++ b/examples/state_containers_examples/public/plugin.ts @@ -17,15 +17,21 @@ * under the License. */ -import { AppMountParameters, CoreSetup, Plugin } from 'kibana/public'; +import { AppMountParameters, CoreSetup, Plugin, AppNavLinkStatus } from '../../../src/core/public'; import { AppPluginDependencies } from './with_data_services/types'; import { PLUGIN_ID, PLUGIN_NAME } from '../common'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; + +interface SetupDeps { + developerExamples: DeveloperExamplesSetup; +} export class StateContainersExamplesPlugin implements Plugin { - public setup(core: CoreSetup) { + public setup(core: CoreSetup, { developerExamples }: SetupDeps) { core.application.register({ id: 'stateContainersExampleBrowserHistory', title: 'State containers example - browser history routing', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const { renderApp, History } = await import('./todo/app'); return renderApp(params, { @@ -38,6 +44,7 @@ export class StateContainersExamplesPlugin implements Plugin { core.application.register({ id: 'stateContainersExampleHashHistory', title: 'State containers example - hash history routing', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const { renderApp, History } = await import('./todo/app'); return renderApp(params, { @@ -51,6 +58,7 @@ export class StateContainersExamplesPlugin implements Plugin { core.application.register({ id: PLUGIN_ID, title: PLUGIN_NAME, + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { // Load application bundle const { renderApp } = await import('./with_data_services/application'); @@ -60,6 +68,62 @@ export class StateContainersExamplesPlugin implements Plugin { return renderApp(coreStart, depsStart as AppPluginDependencies, params); }, }); + + developerExamples.register({ + appId: 'stateContainersExampleBrowserHistory', + title: 'State containers using browser history', + description: `An example todo app that uses browser history and state container utilities like createStateContainerReactHelpers, + createStateContainer, createKbnUrlStateStorage, createSessionStorageStateStorage, + syncStates and getStateFromKbnUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the + state should be preserved.`, + links: [ + { + label: 'README', + href: + 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_containers/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); + + developerExamples.register({ + appId: 'stateContainersExampleHashHistory', + title: 'State containers using hash history', + description: `An example todo app that uses hash history and state container utilities like createStateContainerReactHelpers, + createStateContainer, createKbnUrlStateStorage, createSessionStorageStateStorage, + syncStates and getStateFromKbnUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the + state should be preserved.`, + links: [ + { + label: 'README', + href: + 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_containers/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); + + developerExamples.register({ + appId: PLUGIN_ID, + title: 'Sync state from a query bar with the url', + description: `Shows how to use data.syncQueryStateWitUrl in combination with state container utilities from kibana_utils to + show a query bar that stores state in the url and is kept in sync. + `, + links: [ + { + label: 'README', + href: + 'https://github.com/elastic/kibana/blob/master/src/plugins/data/public/query/state_sync/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); } public start() {} diff --git a/examples/state_containers_examples/public/with_data_services/application.tsx b/examples/state_containers_examples/public/with_data_services/application.tsx index 67a2a8b791ae5..d09b77fbf25ee 100644 --- a/examples/state_containers_examples/public/with_data_services/application.tsx +++ b/examples/state_containers_examples/public/with_data_services/application.tsx @@ -27,7 +27,7 @@ import { createKbnUrlStateStorage } from '../../../../src/plugins/kibana_utils/p export const renderApp = ( { notifications, http }: CoreStart, { navigation, data }: AppPluginDependencies, - { appBasePath, element, history }: AppMountParameters + { element, history }: AppMountParameters ) => { const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history }); diff --git a/examples/state_containers_examples/public/with_data_services/components/app.tsx b/examples/state_containers_examples/public/with_data_services/components/app.tsx index baf627fd62a32..04bdb53efa502 100644 --- a/examples/state_containers_examples/public/with_data_services/components/app.tsx +++ b/examples/state_containers_examples/public/with_data_services/components/app.tsx @@ -78,14 +78,7 @@ const { useContainer: useAppStateContainer, } = createStateContainerReactHelpers>(); -const App = ({ - notifications, - http, - navigation, - data, - history, - kbnUrlStateStorage, -}: StateDemoAppDeps) => { +const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps) => { const appStateContainer = useAppStateContainer(); const appState = useAppState(); diff --git a/examples/ui_actions_explorer/kibana.json b/examples/ui_actions_explorer/kibana.json index e88739a9e44d6..f57072e89b06d 100644 --- a/examples/ui_actions_explorer/kibana.json +++ b/examples/ui_actions_explorer/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": false, "ui": true, - "requiredPlugins": ["uiActions", "uiActionsExamples"], + "requiredPlugins": ["uiActions", "uiActionsExamples", "developerExamples"], "optionalPlugins": [] } diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx index de86b51aee3a8..670138b43b9c4 100644 --- a/examples/ui_actions_explorer/public/plugin.tsx +++ b/examples/ui_actions_explorer/public/plugin.tsx @@ -17,8 +17,8 @@ * under the License. */ -import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; -import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; +import { UiActionsStart, UiActionsSetup } from '../../../src/plugins/ui_actions/public'; +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; import { PHONE_TRIGGER, USER_TRIGGER, @@ -39,6 +39,8 @@ import { ACTION_VIEW_IN_MAPS, ACTION_PHONE_USER, } from './actions/actions'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import image from './ui_actions.png'; interface StartDeps { uiActions: UiActionsStart; @@ -46,6 +48,7 @@ interface StartDeps { interface SetupDeps { uiActions: UiActionsSetup; + developerExamples: DeveloperExamplesSetup; } declare module '../../../src/plugins/ui_actions/public' { @@ -66,7 +69,7 @@ declare module '../../../src/plugins/ui_actions/public' { } export class UiActionsExplorerPlugin implements Plugin { - public setup(core: CoreSetup<{ uiActions: UiActionsStart }>, deps: SetupDeps) { + public setup(core: CoreSetup, deps: SetupDeps) { deps.uiActions.registerTrigger({ id: COUNTRY_TRIGGER, }); @@ -98,6 +101,7 @@ export class UiActionsExplorerPlugin implements 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%0A1oI=Hc7h?x?s1Q}vv~W7j#?*+P{x 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 literal 0 HcmV?d00001 diff --git a/examples/url_generators_examples/public/plugin.tsx b/examples/url_generators_examples/public/plugin.tsx index 016494037ec05..77f7d66ccf103 100644 --- a/examples/url_generators_examples/public/plugin.tsx +++ b/examples/url_generators_examples/public/plugin.tsx @@ -18,7 +18,7 @@ */ import { SharePluginStart, SharePluginSetup } from '../../../src/plugins/share/public'; -import { Plugin, CoreSetup, AppMountParameters } from '../../../src/core/public'; +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; import { HelloLinkGeneratorState, createHelloPageLinkGenerator, @@ -58,6 +58,7 @@ export class AccessLinksExamplesPlugin implements Plugin { - public setup(core: CoreSetup) { +interface SetupDeps { + developerExamples: DeveloperExamplesSetup; +} + +export class AccessLinksExplorerPlugin implements Plugin { + public setup(core: CoreSetup, { developerExamples }: SetupDeps) { core.application.register({ id: 'urlGeneratorsExplorer', title: 'Access links explorer', + navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const depsStart = (await core.getStartServices())[1]; const { renderApp } = await import('./app'); @@ -40,6 +45,26 @@ export class AccessLinksExplorerPlugin implements Plugin { beforeEach(async () => { - await appsMenu.clickLink('bfetch explorer'); await testSubjects.click('count-until'); await testSubjects.click('double-integers'); }); diff --git a/test/plugin_functional/test_suites/bfetch_explorer/index.ts b/test/examples/bfetch_explorer/index.ts similarity index 83% rename from test/plugin_functional/test_suites/bfetch_explorer/index.ts rename to test/examples/bfetch_explorer/index.ts index a68a5090b9bed..1f039a2ea415c 100644 --- a/test/plugin_functional/test_suites/bfetch_explorer/index.ts +++ b/test/examples/bfetch_explorer/index.ts @@ -17,18 +17,17 @@ * under the License. */ -import { FtrProviderContext } from '../../../functional/ftr_provider_context'; +import { FtrProviderContext } from '../../functional/ftr_provider_context'; +// eslint-disable-next-line import/no-default-export export default function ({ getService, getPageObjects, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); - const appsMenu = getService('appsMenu'); const PageObjects = getPageObjects(['common', 'header']); describe('bfetch explorer', function () { before(async () => { await browser.setWindowSize(1300, 900); - await PageObjects.common.navigateToApp('settings'); - await appsMenu.clickLink('bfetch explorer'); + await PageObjects.common.navigateToApp('bfetch-explorer', { insertTimestamp: false }); }); loadTestFile(require.resolve('./batched_function')); diff --git a/test/examples/config.js b/test/examples/config.js index aedd06c31d413..228af71a1f5d0 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -27,6 +27,7 @@ export default async function ({ readConfigFile }) { testFiles: [ require.resolve('./search'), require.resolve('./embeddables'), + require.resolve('./bfetch_explorer'), require.resolve('./ui_actions'), require.resolve('./state_sync'), ], diff --git a/test/examples/embeddables/index.ts b/test/examples/embeddables/index.ts index 878b1b9277ba1..d0d7d4a01fbae 100644 --- a/test/examples/embeddables/index.ts +++ b/test/examples/embeddables/index.ts @@ -26,14 +26,12 @@ export default function ({ loadTestFile, }: PluginFunctionalProviderContext) { const browser = getService('browser'); - const appsMenu = getService('appsMenu'); const PageObjects = getPageObjects(['common', 'header']); describe('embeddable explorer', function () { before(async () => { await browser.setWindowSize(1300, 900); - await PageObjects.common.navigateToApp('settings'); - await appsMenu.clickLink('Embeddable explorer'); + await PageObjects.common.navigateToApp('embeddableExplorer'); }); loadTestFile(require.resolve('./hello_world_embeddable')); diff --git a/test/examples/search/index.ts b/test/examples/search/index.ts index 2fb37afd248e2..706e9659fc010 100644 --- a/test/examples/search/index.ts +++ b/test/examples/search/index.ts @@ -22,7 +22,6 @@ import { FtrProviderContext } from 'test/functional/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService, getPageObjects, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); - const appsMenu = getService('appsMenu'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'header']); @@ -36,8 +35,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid defaultIndex: 'logstash-*', }); await browser.setWindowSize(1300, 900); - await PageObjects.common.navigateToApp('settings'); - await appsMenu.clickLink('Search Explorer'); + await PageObjects.common.navigateToApp('searchExplorer'); }); after(async function () { diff --git a/test/examples/state_sync/index.ts b/test/examples/state_sync/index.ts index ea1c7692f64fe..297cd8f472b95 100644 --- a/test/examples/state_sync/index.ts +++ b/test/examples/state_sync/index.ts @@ -26,12 +26,10 @@ export default function ({ loadTestFile, }: PluginFunctionalProviderContext) { const browser = getService('browser'); - const PageObjects = getPageObjects(['common']); describe('state sync examples', function () { before(async () => { await browser.setWindowSize(1300, 900); - await PageObjects.common.navigateToApp('settings'); }); loadTestFile(require.resolve('./todo_app')); diff --git a/test/examples/state_sync/todo_app.ts b/test/examples/state_sync/todo_app.ts index 71b491a05ae2f..1ac5376b9ed8d 100644 --- a/test/examples/state_sync/todo_app.ts +++ b/test/examples/state_sync/todo_app.ts @@ -26,7 +26,6 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const testSubjects = getService('testSubjects'); const find = getService('find'); const retry = getService('retry'); - const appsMenu = getService('appsMenu'); const browser = getService('browser'); const PageObjects = getPageObjects(['common']); const log = getService('log'); @@ -38,7 +37,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide before(async () => { base = await PageObjects.common.getHostPort(); - await appsMenu.clickLink('State containers example - browser history routing'); + await PageObjects.common.navigateToApp(appId, { insertTimestamp: false }); }); it('links are rendered correctly and state is preserved in links', async () => { @@ -119,7 +118,9 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide describe('TODO app with hash history ', async () => { before(async () => { - await appsMenu.clickLink('State containers example - hash history routing'); + await PageObjects.common.navigateToApp('stateContainersExampleHashHistory', { + insertTimestamp: false, + }); }); it('Links are rendered correctly and state is preserved in links', async () => { diff --git a/test/examples/ui_actions/index.ts b/test/examples/ui_actions/index.ts index 55137e2acc2b7..957cd0c46e1d0 100644 --- a/test/examples/ui_actions/index.ts +++ b/test/examples/ui_actions/index.ts @@ -26,14 +26,12 @@ export default function ({ loadTestFile, }: PluginFunctionalProviderContext) { const browser = getService('browser'); - const appsMenu = getService('appsMenu'); const PageObjects = getPageObjects(['common', 'header']); describe('ui actions explorer', function () { before(async () => { await browser.setWindowSize(1300, 900); - await PageObjects.common.navigateToApp('settings'); - await appsMenu.clickLink('Ui Actions Explorer'); + await PageObjects.common.navigateToApp('uiActionsExplorer'); }); loadTestFile(require.resolve('./ui_actions')); diff --git a/test/plugin_functional/config.js b/test/plugin_functional/config.js index 9039f7e734a6e..74d52b842662b 100644 --- a/test/plugin_functional/config.js +++ b/test/plugin_functional/config.js @@ -37,7 +37,6 @@ export default async function ({ readConfigFile }) { require.resolve('./test_suites/embeddable_explorer'), require.resolve('./test_suites/core_plugins'), require.resolve('./test_suites/management'), - require.resolve('./test_suites/bfetch_explorer'), require.resolve('./test_suites/doc_views'), ], services: { diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json deleted file mode 100644 index 1acc7df871c94..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "kbn_tp_bfetch_explorer", - "version": "0.0.1", - "kibanaVersion": "kibana", - "configPath": ["kbn_tp_bfetch_explorer"], - "server": true, - "ui": true, - "requiredPlugins": ["bfetch"], - "optionalPlugins": [] -} diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json deleted file mode 100644 index e396489a1ffc4..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "kbn_tp_bfetch_explorer", - "version": "1.0.0", - "main": "target/examples/kbn_tp_bfetch_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json deleted file mode 100644 index 994f81e396763..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./target", - "skipLibCheck": true, - "types": [ - "node", - "jest", - "react" - ] - }, - "include": [ - "index.ts", - "public/**/*.ts", - "public/**/*.tsx", - "server/**/*.ts", - "server/**/*.tsx", - "../../../../typings/**/*", - ], - "exclude": [] -} From e66eaf74fd5b02a8466864d3df80a7457a4568c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez?= Date: Mon, 8 Jun 2020 15:49:26 +0200 Subject: [PATCH 18/27] [Logs UI] View in context tweaks (#67777) Co-authored-by: Elastic Machine --- .../log_entry_actions_column.tsx | 56 +++++++++---------- .../logs/stream/page_view_log_in_context.tsx | 54 ++++++++++++------ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx index 1ac2e00abca70..e27de7fd6b5a8 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_actions_column.tsx @@ -5,19 +5,11 @@ */ import React, { useCallback } from 'react'; -import { EuiButtonIcon } from '@elastic/eui'; +import { EuiButtonIcon, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { LogEntryColumnContent } from './log_entry_column'; -import { - euiStyled, - ActionMenu, - Section, - SectionTitle, - SectionLinks, - SectionLink, -} from '../../../../../observability/public'; +import { euiStyled } from '../../../../../observability/public'; interface LogEntryActionsColumnProps { isHovered: boolean; @@ -78,29 +70,32 @@ export const LogEntryActionsColumn: React.FC = ({ ); + const items = [ + + {LOG_DETAILS_LABEL} + , + ]; + + if (onViewLogInContext !== undefined) { + items.push( + + {LOG_VIEW_IN_CONTEXT_LABEL} + + ); + } + return ( {isHovered || isMenuOpen ? ( - -

- - - - - - {onViewLogInContext !== undefined ? ( - - ) : null} - -
- + + + ) : null} @@ -115,10 +110,11 @@ const ActionsColumnContent = euiStyled(LogEntryColumnContent)` const ButtonWrapper = euiStyled.div` background: ${(props) => props.theme.eui.euiColorPrimary}; border-radius: 50%; + padding: 4px; + transform: translateY(-6px); `; // this prevents the button from influencing the line height const AbsoluteWrapper = euiStyled.div` - overflow: hidden; position: absolute; `; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index cf3eae263ed59..3ef32c920e293 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -8,12 +8,13 @@ import { EuiFlexGroup, EuiFlexItem, EuiModal, - EuiModalBody, EuiOverlayMask, EuiText, EuiTextColor, EuiToolTip, + EuiSpacer, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { noop } from 'lodash'; import React, { useCallback, useContext, useMemo } from 'react'; import { LogEntry } from '../../../../common/http_api'; @@ -22,6 +23,7 @@ import { useLogSourceContext } from '../../../containers/logs/log_source'; import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; import { useViewportDimensions } from '../../../utils/use_viewport_dimensions'; +import { euiStyled } from '../../../../../observability/public'; const MODAL_MARGIN = 25; @@ -55,7 +57,7 @@ export const PageViewLogInContext: React.FC = () => { return ( - + { > + { /> - + ); }; +const LogInContextWrapper = euiStyled.div<{ width: number | string; height: number | string }>` + padding: 16px; + width: ${(props) => (typeof props.width === 'number' ? `${props.width}px` : props.width)}; + height: ${(props) => (typeof props.height === 'number' ? `${props.height}px` : props.height)}; +`; + const LogEntryContext: React.FC<{ context: LogEntry['context'] }> = ({ context }) => { + let text; if ('container.id' in context) { - return

Displayed logs are from container {context['container.id']}

; + text = ( + + ); } if ('host.name' in context) { @@ -105,21 +121,27 @@ const LogEntryContext: React.FC<{ context: LogEntry['context'] }> = ({ context } context['log.file.path'].length > 45 ? context['log.file.path'].slice(0, 20) + '...' + context['log.file.path'].slice(-25) : context['log.file.path']; - - return ( - -

- - Displayed logs are from file{' '} + text = ( + {shortenedFilePath} - {' '} - and host {context['host.name']} - -

-
+ + ), + host: context['host.name'], + }} + /> ); } - return null; + return ( + +

+ {text} +

+
+ ); }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9d3bc008106c6..aa45fd4bcb292 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7605,7 +7605,6 @@ "xpack.infra.logs.lastUpdate": "前回の更新 {timestamp}", "xpack.infra.logs.loadingNewEntriesText": "新しいエントリーを読み込み中", "xpack.infra.logs.logEntryActionsDetailsButton": "詳細を表示", - "xpack.infra.logs.logEntryActionsMenuTitle": "行詳細のログ", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlButtonLabel": "ML で分析", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlTooltipDescription": "ML アプリでこのカテゴリーを分析します。", "xpack.infra.logs.logEntryCategories.categoryColumnTitle": "カテゴリー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6992d4004e24e..56d373a6fcd2f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7609,7 +7609,6 @@ "xpack.infra.logs.lastUpdate": "上次更新时间 {timestamp}", "xpack.infra.logs.loadingNewEntriesText": "正在加载新条目", "xpack.infra.logs.logEntryActionsDetailsButton": "查看详情", - "xpack.infra.logs.logEntryActionsMenuTitle": "日志行详情", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlButtonLabel": "在 ML 中分析", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlTooltipDescription": "在 ML 应用中分析此类别。", "xpack.infra.logs.logEntryCategories.categoryColumnTitle": "类别", From fcab9745e0691af3878d7538681011b381ae2c71 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 8 Jun 2020 17:29:20 +0300 Subject: [PATCH 19/27] [Telemetry] collect number of visualization saved in the past 7, 30 and 90 days (#67865) * Update telemetry for visualizations to also count the vis from the past 30 and 90 days * Also add metrics for the saved visualizations for the past 7 days Co-authored-by: Elastic Machine --- .../server/lib/get_past_days.test.ts | 22 +++++ .../oss_telemetry/server/lib/get_past_days.ts | 11 +++ .../tasks/visualizations/task_runner.test.ts | 92 ++++++++++++++++--- .../lib/tasks/visualizations/task_runner.ts | 14 ++- .../oss_telemetry/server/test_utils/index.ts | 2 + 5 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts create mode 100644 x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts new file mode 100644 index 0000000000000..28909779343a5 --- /dev/null +++ b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts @@ -0,0 +1,22 @@ +/* + * 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 moment from 'moment'; +import { getPastDays } from './get_past_days'; + +describe('getPastDays', () => { + test('Returns 2 days that have passed from the current date', () => { + const pastDate = moment().subtract(2, 'days').startOf('day').toString(); + + expect(getPastDays(pastDate)).toEqual(2); + }); + + test('Returns 30 days that have passed from the current date', () => { + const pastDate = moment().subtract(30, 'days').startOf('day').toString(); + + expect(getPastDays(pastDate)).toEqual(30); + }); +}); diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts new file mode 100644 index 0000000000000..4f25ef147ad43 --- /dev/null +++ b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ +export const getPastDays = (dateString: string): number => { + const date = new Date(dateString); + const today = new Date(); + const diff = Math.abs(date.getTime() - today.getTime()); + return Math.trunc(diff / (1000 * 60 * 60 * 24)); +}; diff --git a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts index 6a47983a6f4d9..c064f39f4bc6a 100644 --- a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts +++ b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts @@ -13,6 +13,7 @@ import { import { visualizationsTaskRunner } from './task_runner'; import { TaskInstance } from '../../../../../task_manager/server'; import { getNextMidnight } from '../../get_next_midnight'; +import moment from 'moment'; describe('visualizationsTaskRunner', () => { let mockTaskInstance: TaskInstance; @@ -55,6 +56,9 @@ describe('visualizationsTaskRunner', () => { spaces_max: 1, spaces_min: 1, total: 1, + saved_7_days_total: 1, + saved_30_days_total: 1, + saved_90_days_total: 1, }, }, }, @@ -69,6 +73,7 @@ describe('visualizationsTaskRunner', () => { _source: { type: 'visualization', visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), }, }, { @@ -76,11 +81,16 @@ describe('visualizationsTaskRunner', () => { _source: { type: 'visualization', visualization: { visState: '{"type": "printing_press"}' }, + updated_at: moment().subtract(20, 'days').startOf('day').toString(), }, }, { _id: 'meat:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(2, 'months').startOf('day').toString(), + }, }, // meat space { @@ -88,38 +98,99 @@ describe('visualizationsTaskRunner', () => { _source: { type: 'visualization', visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(89, 'days').startOf('day').toString(), }, }, { _id: 'meat:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "cuneiform"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cuneiform"}' }, + updated_at: moment().subtract(5, 'months').startOf('day').toString(), + }, }, { _id: 'meat:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "cuneiform"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cuneiform"}' }, + updated_at: moment().subtract(2, 'days').startOf('day').toString(), + }, }, { _id: 'meat:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), + }, }, // cyber space { _id: 'cyber:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(7, 'months').startOf('day').toString(), + }, }, { _id: 'cyber:visualization:coolviz-789', - _source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } }, + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(3, 'days').startOf('day').toString(), + }, }, { _id: 'cyber:visualization:coolviz-123', _source: { type: 'visualization', visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(15, 'days').startOf('day').toString(), }, }, ]); + const expectedStats = { + cave_painting: { + total: 3, + spaces_min: 1, + spaces_max: 1, + spaces_avg: 1, + saved_7_days_total: 1, + saved_30_days_total: 2, + saved_90_days_total: 3, + }, + printing_press: { + total: 1, + spaces_min: 1, + spaces_max: 1, + spaces_avg: 1, + saved_7_days_total: 0, + saved_30_days_total: 1, + saved_90_days_total: 1, + }, + cuneiform: { + total: 2, + spaces_min: 2, + spaces_max: 2, + spaces_avg: 2, + saved_7_days_total: 1, + saved_30_days_total: 1, + saved_90_days_total: 1, + }, + floppy_disk: { + total: 4, + spaces_min: 2, + spaces_max: 2, + spaces_avg: 2, + saved_7_days_total: 2, + saved_30_days_total: 2, + saved_90_days_total: 3, + }, + }; + const runner = visualizationsTaskRunner( mockTaskInstance, getMockConfig(), @@ -131,13 +202,10 @@ describe('visualizationsTaskRunner', () => { error: undefined, state: { runs: 1, - stats: { - cave_painting: { total: 3, spaces_min: 1, spaces_max: 1, spaces_avg: 1 }, - printing_press: { total: 1, spaces_min: 1, spaces_max: 1, spaces_avg: 1 }, - cuneiform: { total: 2, spaces_min: 2, spaces_max: 2, spaces_avg: 2 }, - floppy_disk: { total: 4, spaces_min: 2, spaces_max: 2, spaces_avg: 2 }, - }, + stats: expectedStats, }, }); + + expect(result.state.stats).toMatchObject(expectedStats); }); }); diff --git a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts index b15ead36a75f6..5f7f70ee5d62a 100644 --- a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts +++ b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts @@ -10,12 +10,14 @@ import { first } from 'rxjs/operators'; import { APICaller, IClusterClient } from 'src/core/server'; import { getNextMidnight } from '../../get_next_midnight'; +import { getPastDays } from '../../get_past_days'; import { TaskInstance } from '../../../../../task_manager/server'; import { ESSearchHit } from '../../../../../apm/typings/elasticsearch'; interface VisSummary { type: string; space: string; + past_days: number; } /* @@ -26,7 +28,11 @@ async function getStats(callCluster: APICaller, index: string) { size: 10000, // elasticsearch index.max_result_window default value index, ignoreUnavailable: true, - filterPath: ['hits.hits._id', 'hits.hits._source.visualization'], + filterPath: [ + 'hits.hits._id', + 'hits.hits._source.visualization', + 'hits.hits._source.updated_at', + ], body: { query: { bool: { filter: { term: { type: 'visualization' } } }, @@ -43,13 +49,14 @@ async function getStats(callCluster: APICaller, index: string) { const visSummaries: VisSummary[] = esResponse.hits.hits.map( (hit: ESSearchHit<{ visState: string }>) => { const spacePhrases: string[] = hit._id.split(':'); + const lastUpdated: string = _.get(hit, '_source.updated_at'); const space = spacePhrases.length === 3 ? spacePhrases[0] : 'default'; // if in a custom space, the format of a saved object ID is space:type:id const visualization = _.get(hit, '_source.visualization', { visState: '{}' }); const visState: { type?: string } = JSON.parse(visualization.visState); - return { type: visState.type || '_na_', space, + past_days: getPastDays(lastUpdated), }; } ); @@ -68,6 +75,9 @@ async function getStats(callCluster: APICaller, index: string) { spaces_min: _.min(spaceCounts), spaces_max: _.max(spaceCounts), spaces_avg: total / spaceCounts.length, + saved_7_days_total: curr.filter((c) => c.past_days <= 7).length, + saved_30_days_total: curr.filter((c) => c.past_days <= 30).length, + saved_90_days_total: curr.filter((c) => c.past_days <= 90).length, }; }); } diff --git a/x-pack/plugins/oss_telemetry/server/test_utils/index.ts b/x-pack/plugins/oss_telemetry/server/test_utils/index.ts index 428909dc7feed..93bde69629fbd 100644 --- a/x-pack/plugins/oss_telemetry/server/test_utils/index.ts +++ b/x-pack/plugins/oss_telemetry/server/test_utils/index.ts @@ -7,6 +7,7 @@ import { APICaller } from 'kibana/server'; import { of } from 'rxjs'; +import moment from 'moment'; import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; import { ConcreteTaskInstance, @@ -38,6 +39,7 @@ const defaultMockSavedObjects = [ _source: { type: 'visualization', visualization: { visState: '{"type": "shell_beads"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), }, }, ]; From 2bedb59c8e0968eb05d2033d36e9426e556a40be Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Mon, 8 Jun 2020 07:30:14 -0700 Subject: [PATCH 20/27] Closes #66867 by adding missing, requried API params (#68465) Co-authored-by: Elastic Machine --- x-pack/plugins/apm/public/services/rest/watcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/services/rest/watcher.ts b/x-pack/plugins/apm/public/services/rest/watcher.ts index 3027164e2863e..246ed061eb20e 100644 --- a/x-pack/plugins/apm/public/services/rest/watcher.ts +++ b/x-pack/plugins/apm/public/services/rest/watcher.ts @@ -19,6 +19,6 @@ export async function createWatch({ return callApi(http, { method: 'PUT', pathname: `/api/watcher/watch/${id}`, - body: { type: 'json', id, watch }, + body: { type: 'json', id, watch, isNew: true, isActive: true }, }); } From bc3918671cb9bc7ba33a192fa1ffc7c0ee1af93c Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Mon, 8 Jun 2020 10:43:10 -0400 Subject: [PATCH 21/27] [security_solution] enable react-hooks/exhaustive-deps (#68470) * Enable `react-hooks/exhaustive-deps` rule for security_solution * Disable it anywhere that it would catch an issue --- .eslintrc.js | 4 --- .../alerts_histogram_panel/index.tsx | 6 ++++ .../alerts/components/alerts_table/index.tsx | 7 +++- .../components/rules/add_item_form/index.tsx | 1 + .../alerts/components/rules/mitre/index.tsx | 3 +- .../components/rules/pick_timeline/index.tsx | 2 ++ .../components/rules/query_bar/index.tsx | 8 +++-- .../rules/rule_actions_field/index.tsx | 3 ++ .../rules/rule_actions_overflow/index.tsx | 1 + .../components/rules/rule_switch/index.tsx | 3 ++ .../rules/schedule_item_form/index.tsx | 3 ++ .../rules/step_about_rule/index.tsx | 3 ++ .../rules/step_define_rule/index.tsx | 3 ++ .../rules/step_rule_actions/index.tsx | 4 +++ .../rules/step_schedule_rule/index.tsx | 3 ++ .../rules/throttle_select_field/index.tsx | 1 + .../alerts/components/user_info/index.tsx | 8 +++++ .../alerts/use_privilege_user.tsx | 1 + .../alerts/use_signal_index.tsx | 1 + .../rules/fetch_index_patterns.tsx | 1 + .../detection_engine/rules/persist_rule.tsx | 1 + .../rules/use_pre_packaged_rules.tsx | 1 + .../detection_engine/rules/use_rule.tsx | 1 + .../rules/use_rule_status.tsx | 2 ++ .../detection_engine/rules/use_rules.tsx | 6 ++-- .../detection_engine/rules/use_tags.tsx | 1 + .../detection_engine/rules/all/index.tsx | 2 ++ .../rules_table_filters.tsx | 1 + .../detection_engine/rules/create/index.tsx | 3 ++ .../detection_engine/rules/details/index.tsx | 2 ++ .../detection_engine/rules/edit/index.tsx | 6 ++++ .../cases/components/add_comment/index.tsx | 4 ++- .../cases/components/all_cases/index.tsx | 11 +++++++ .../components/all_cases/table_filters.tsx | 6 ++++ .../cases/components/case_view/actions.tsx | 1 + .../components/configure_cases/connectors.tsx | 1 + .../configure_cases/connectors_dropdown.tsx | 1 + .../configure_cases/field_mapping.tsx | 2 ++ .../components/configure_cases/index.tsx | 7 ++++ .../components/connector_selector/form.tsx | 1 + .../public/cases/components/create/index.tsx | 2 ++ .../components/open_closed_stats/index.tsx | 1 + .../components/use_push_to_service/index.tsx | 2 ++ .../components/user_action_tree/index.tsx | 4 +++ .../user_action_tree/user_action_markdown.tsx | 2 ++ .../cases/components/user_list/index.tsx | 1 + .../containers/configure/use_configure.tsx | 3 ++ .../containers/configure/use_connectors.tsx | 2 ++ .../cases/containers/use_bulk_update_case.tsx | 2 ++ .../cases/containers/use_delete_cases.tsx | 4 +++ .../containers/use_get_action_license.tsx | 2 ++ .../public/cases/containers/use_get_case.tsx | 2 ++ .../containers/use_get_case_user_actions.tsx | 2 ++ .../public/cases/containers/use_get_cases.tsx | 4 +++ .../cases/containers/use_get_cases_status.tsx | 2 ++ .../cases/containers/use_get_reporters.tsx | 2 ++ .../public/cases/containers/use_get_tags.tsx | 1 + .../public/cases/containers/use_post_case.tsx | 1 + .../cases/containers/use_post_comment.tsx | 1 + .../containers/use_post_push_to_service.tsx | 1 + .../cases/containers/use_update_case.tsx | 1 + .../cases/containers/use_update_comment.tsx | 1 + .../components/alerts_viewer/alerts_table.tsx | 1 + .../common/components/alerts_viewer/index.tsx | 1 + .../common/components/charts/common.tsx | 1 + .../drag_drop_context_wrapper.tsx | 1 + .../drag_and_drop/draggable_wrapper.tsx | 1 + .../draggable_wrapper_hover_content.tsx | 2 ++ .../events_viewer/events_viewer.tsx | 2 ++ .../common/components/events_viewer/index.tsx | 1 + .../exceptions/viewer/exception_entries.tsx | 1 + .../components/generic_downloader/index.tsx | 1 + .../components/header_page/editable_title.tsx | 1 + .../common/components/help_menu/index.tsx | 1 + .../components/import_data_modal/index.tsx | 1 + .../public/common/components/links/index.tsx | 1 + .../components/markdown_editor/index.tsx | 1 + .../components/matrix_histogram/index.tsx | 33 ++++++++----------- .../components/matrix_histogram/types.ts | 6 ---- .../ml/anomaly/use_anomalies_table_data.ts | 4 +++ .../permissions/ml_capabilities_provider.tsx | 1 + .../ml_popover/hooks/use_siem_jobs.tsx | 1 + .../filters/groups_filter_popover.tsx | 1 + .../common/components/navigation/index.tsx | 1 + .../common/components/search_bar/index.tsx | 6 ++++ .../common/components/stat_items/index.tsx | 1 + .../components/super_date_picker/index.tsx | 3 ++ .../common/components/toasters/index.test.tsx | 1 + .../public/common/components/top_n/index.tsx | 1 + .../components/url_state/use_url_state.tsx | 3 +- .../anomalies_query_tab_body/index.tsx | 1 + .../events/last_event_time/index.ts | 3 +- .../common/containers/global_time/index.tsx | 1 + .../containers/matrix_histogram/index.ts | 3 +- .../public/common/containers/source/index.tsx | 3 +- .../common/hooks/use_add_to_timeline.tsx | 1 + .../common/hooks/use_index_patterns.tsx | 1 + .../components/connector_flyout/index.tsx | 6 ++++ .../public/common/lib/kibana/hooks.ts | 2 ++ .../public/common/utils/route/spy_routes.tsx | 2 ++ .../utils/timeline/use_show_timeline.tsx | 1 + .../view/details/overview/index.tsx | 1 + .../containers/hosts/first_last_seen/index.ts | 1 + .../components/management_page_view.tsx | 1 + .../view/details/host_details.tsx | 1 + .../components/embeddables/embedded_map.tsx | 4 +++ .../embeddables/map_tool_tip/map_tool_tip.tsx | 4 +-- .../source_destination/country_flag.tsx | 1 + .../network/components/tls_table/index.tsx | 1 + .../network/components/users_table/index.tsx | 1 + .../components/alerts_by_category/index.tsx | 2 ++ .../components/recent_timelines/index.tsx | 1 + .../components/signals_by_category/index.tsx | 1 + .../fields_browser/category_columns.tsx | 1 + .../fields_browser/field_browser.tsx | 1 + .../components/fields_browser/index.tsx | 2 ++ .../components/flyout/pane/index.tsx | 4 ++- .../components/manage_timeline/index.tsx | 1 + .../open_timeline/edit_timeline_actions.tsx | 1 + .../edit_timeline_batch_actions.tsx | 1 + .../components/open_timeline/index.tsx | 5 +++ .../open_timeline/use_timeline_types.tsx | 2 ++ .../timeline/body/column_headers/index.tsx | 3 +- .../body/events/event_column_view.tsx | 2 ++ .../timeline/body/events/stateful_event.tsx | 1 + .../timeline/body/stateful_body.tsx | 9 +++++ .../data_providers/provider_item_badge.tsx | 3 ++ .../timeline/fetch_kql_timeline.tsx | 1 + .../components/timeline/footer/index.tsx | 1 + .../timelines/components/timeline/index.tsx | 7 ++++ .../insert_timeline_popover/index.tsx | 1 + .../use_insert_timeline.tsx | 2 ++ .../timeline/properties/helpers.tsx | 1 + .../components/timeline/properties/index.tsx | 1 + .../components/timeline/query_bar/index.tsx | 6 ++++ .../timeline/search_or_filter/index.tsx | 5 +++ .../timeline/selectable_timeline/index.tsx | 1 + .../components/timeline/timeline.tsx | 3 ++ .../public/timelines/containers/all/index.tsx | 3 +- 139 files changed, 313 insertions(+), 46 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f0b7d6864bef0..c33f4de15b919 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -758,10 +758,6 @@ module.exports = { 'react/jsx-no-target-blank': 'error', 'react/jsx-fragments': 'error', 'react/jsx-sort-default-props': 'error', - // might be introduced after the other warns are fixed - // 'react/jsx-sort-props': 'error', - // might be introduced after the other warns are fixed - 'react-hooks/exhaustive-deps': 'off', 'require-atomic-updates': 'error', 'symbol-description': 'error', 'vars-on-top': 'error', diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx index 7451ea6ec0ca1..ed98a37775576 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx @@ -131,6 +131,7 @@ export const AlertsHistogramPanel = memo( totalAlertsObj.value, totalAlertsObj.relation === 'gte' ? '>' : totalAlertsObj.relation === 'lte' ? '<' : '' ), + // eslint-disable-next-line react-hooks/exhaustive-deps [totalAlertsObj] ); @@ -138,6 +139,7 @@ export const AlertsHistogramPanel = memo( setSelectedStackByOption( stackByOptions?.find((co) => co.value === event.target.value) ?? defaultStackByOption ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const formattedAlertsData = useMemo(() => formatAlertsData(alertsData), [alertsData]); @@ -154,6 +156,7 @@ export const AlertsHistogramPanel = memo( value: bucket.key, })) : NO_LEGEND_DATA, + // eslint-disable-next-line react-hooks/exhaustive-deps [alertsData, selectedStackByOption.value] ); @@ -175,6 +178,7 @@ export const AlertsHistogramPanel = memo( deleteQuery({ id: uniqueQueryId }); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -189,6 +193,7 @@ export const AlertsHistogramPanel = memo( refetch, }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [setQuery, isLoadingAlerts, alertsData, response, request, refetch]); useEffect(() => { @@ -219,6 +224,7 @@ export const AlertsHistogramPanel = memo( !isEmpty(converted) ? [converted] : [] ) ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedStackByOption.value, from, to, query, filters]); const linkButton = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx index 685e66e73ced2..2be20a9d47f67 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx @@ -117,6 +117,7 @@ export const AlertsTableComponent: React.FC = ({ }); } return null; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [browserFields, globalFilters, globalQuery, indexPatterns, kibana, to, from]); // Callback for creating a new timeline -- utilized by row/batch actions @@ -143,6 +144,7 @@ export const AlertsTableComponent: React.FC = ({ ({ eventIds, isLoading }: SetEventsLoadingProps) => { setEventsLoading!({ id: ALERTS_TABLE_TIMELINE_ID, eventIds, isLoading }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [setEventsLoading, ALERTS_TABLE_TIMELINE_ID] ); @@ -150,6 +152,7 @@ export const AlertsTableComponent: React.FC = ({ ({ eventIds, isDeleted }: SetEventsDeletedProps) => { setEventsDeleted!({ id: ALERTS_TABLE_TIMELINE_ID, eventIds, isDeleted }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [setEventsDeleted, ALERTS_TABLE_TIMELINE_ID] ); @@ -210,7 +213,7 @@ export const AlertsTableComponent: React.FC = ({ }, [setSelectAll, setShowClearSelectionAction]); const updateAlertsStatusCallback: UpdateAlertsStatusCallback = useCallback( - async (refetchQuery: inputsModel.Refetch, { alertIds, status }: UpdateAlertsStatusProps) => { + async (refetchQuery: inputsModel.Refetch, { status }: UpdateAlertsStatusProps) => { await updateAlertStatusAction({ query: showClearSelectionAction ? getGlobalQuery()?.filterQuery : undefined, alertIds: Object.keys(selectedEventIds), @@ -314,6 +317,7 @@ export const AlertsTableComponent: React.FC = ({ title: i18n.ALERTS_TABLE_TITLE, selectAll: canUserCRUD ? selectAll : false, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { setTimelineRowActions({ @@ -321,6 +325,7 @@ export const AlertsTableComponent: React.FC = ({ queryFields: requiredFieldsForActions, timelineRowActions: additionalActions, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [additionalActions]); const headerFilterGroup = useMemo( () => , diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/add_item_form/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/add_item_form/index.tsx index c8eb6f69c95ba..b0098d62cc9e5 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/add_item_form/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/add_item_form/index.tsx @@ -126,6 +126,7 @@ export const AddItem = ({ inputsRef.current[haveBeenKeyboardDeleted].focus(); setHaveBeenKeyboardDeleted(-1); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [haveBeenKeyboardDeleted, inputsRef.current]); const values = field.value as string[]; diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/mitre/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/mitre/index.tsx index 88edd0e795531..e898a362c7771 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/mitre/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/mitre/index.tsx @@ -40,7 +40,7 @@ interface AddItemProps { isDisabled: boolean; } -export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddItemProps) => { +export const AddMitreThreat = ({ field, idAria, isDisabled }: AddItemProps) => { const [showValidation, setShowValidation] = useState(false); const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); @@ -101,6 +101,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI ...values.slice(index + 1), ]); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [field] ); diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/pick_timeline/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/pick_timeline/index.tsx index 0029e70e4edda..4b27daf3180ec 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/pick_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/pick_timeline/index.tsx @@ -39,6 +39,7 @@ export const PickTimeline = ({ setTimelineId(id); setTimelineTitle(title); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field.value]); const handleOnTimelineChange = useCallback( @@ -49,6 +50,7 @@ export const PickTimeline = ({ field.setValue({ id, title }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [field] ); diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/query_bar/index.tsx index 82206b6ba5e3d..c53a9ccc22d8b 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/query_bar/index.tsx @@ -16,7 +16,6 @@ import { Query, FilterManager, SavedQuery, - SavedQueryTimeFilter, } from '../../../../../../../../src/plugins/data/public'; import { BrowserFields } from '../../../../common/containers/source'; @@ -111,6 +110,7 @@ export const QueryBarDefineRule = ({ isSubscribed = false; subscriptions.unsubscribe(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field.value]); useEffect(() => { @@ -143,10 +143,11 @@ export const QueryBarDefineRule = ({ return () => { isSubscribed = false; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field.value]); const onSubmitQuery = useCallback( - (newQuery: Query, timefilter?: SavedQueryTimeFilter) => { + (newQuery: Query) => { const { query } = field.value as FieldValueQueryBar; if (!deepEqual(query, newQuery)) { field.setValue({ ...(field.value as FieldValueQueryBar), query: newQuery }); @@ -179,6 +180,7 @@ export const QueryBarDefineRule = ({ } } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [field.value] ); @@ -214,7 +216,7 @@ export const QueryBarDefineRule = ({ [browserFields, field, indexPattern] ); - const onMutation = (event: unknown, observer: unknown) => { + const onMutation = () => { if (resizeParentContainer != null) { const suggestionContainer = document.getElementById('kbnTypeahead__items'); if (suggestionContainer != null) { diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_field/index.tsx index b77de683d5f20..c6ff25f311d9c 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_field/index.tsx @@ -56,6 +56,7 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables updatedActions[index] = deepMerge(updatedActions[index], { id }); field.setValue(updatedActions); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [field.setValue, actions] ); @@ -71,6 +72,7 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables updatedActions[index].params[key] = value; field.setValue(updatedActions); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [field.setValue, actions] ); @@ -82,6 +84,7 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables ); setSupportedActionTypes(supportedTypes); })(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx index 5a5156fa2b9a3..66f04c9bc6add 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx @@ -101,6 +101,7 @@ const RuleActionsOverflowComponent = ({ , ] : [], + // eslint-disable-next-line react-hooks/exhaustive-deps [rule, userHasNoPermissions] ); diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_switch/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_switch/index.tsx index 79a16bfb386ec..c85676ce51052 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_switch/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_switch/index.tsx @@ -94,6 +94,7 @@ export const RuleSwitchComponent = ({ } setMyIsLoading(false); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [dispatch, id] ); @@ -101,12 +102,14 @@ export const RuleSwitchComponent = ({ if (myEnabled !== enabled) { setMyEnabled(enabled); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabled]); useEffect(() => { if (myIsLoading !== isLoading) { setMyIsLoading(isLoading ?? false); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading]); return ( diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/schedule_item_form/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/schedule_item_form/index.tsx index 9a4f9b25e01f4..bb33767f4f5d5 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/schedule_item_form/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/schedule_item_form/index.tsx @@ -78,6 +78,7 @@ export const ScheduleItem = ({ setTimeType(e.target.value); field.setValue(`${timeVal}${e.target.value}`); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [timeVal] ); @@ -87,6 +88,7 @@ export const ScheduleItem = ({ setTimeVal(sanitizedValue); field.setValue(`${sanitizedValue}${timeType}`); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [timeType] ); @@ -111,6 +113,7 @@ export const ScheduleItem = ({ setTimeType(filterTimeType[0]); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field.value]); // EUI missing some props diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/step_about_rule/index.tsx index e41416f2a0ccb..f23c51e019f24 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/step_about_rule/index.tsx @@ -101,6 +101,7 @@ const StepAboutRuleComponent: FC = ({ setMyStepData({ ...data, isNew: false } as AboutStepRule); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); useEffect(() => { @@ -113,12 +114,14 @@ const StepAboutRuleComponent: FC = ({ setMyStepData(myDefaultValues); setFieldValue(form, schema, myDefaultValues); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValues]); useEffect(() => { if (setForm != null) { setForm(RuleStep.aboutRule, form); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); return isReadOnlyView && myStepData.name != null ? ( diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/step_define_rule/index.tsx index fc875908bd4ef..b56e1794eef63 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/step_define_rule/index.tsx @@ -123,6 +123,7 @@ const StepDefineRuleComponent: FC = ({ setMyStepData({ ...data, isNew: false } as DefineStepRule); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); useEffect(() => { @@ -132,12 +133,14 @@ const StepDefineRuleComponent: FC = ({ setMyStepData(newValues); setFieldValue(form, schema, newValues); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValues, setMyStepData, setFieldValue]); useEffect(() => { if (setForm != null) { setForm(RuleStep.defineRule, form); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); const handleResetIndices = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx index 778c6bd92bc73..9334bd59bebf5 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx @@ -99,6 +99,7 @@ const StepRuleActionsComponent: FC = ({ } } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [form] ); @@ -112,12 +113,14 @@ const StepRuleActionsComponent: FC = ({ setMyStepData(myDefaultValues); setFieldValue(form, schema, myDefaultValues); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValues]); useEffect(() => { if (setForm != null) { setForm(RuleStep.ruleActions, form); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); const updateThrottle = useCallback((throttle) => setMyStepData({ ...myStepData, throttle }), [ @@ -142,6 +145,7 @@ const StepRuleActionsComponent: FC = ({ options: throttleOptions, }, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [isLoading, updateThrottle] ); diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/step_schedule_rule/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/step_schedule_rule/index.tsx index fa49637a0c830..60855bc5fa25f 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/step_schedule_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/step_schedule_rule/index.tsx @@ -62,6 +62,7 @@ const StepScheduleRuleComponent: FC = ({ setMyStepData({ ...data, isNew: false } as ScheduleStepRule); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); useEffect(() => { @@ -74,12 +75,14 @@ const StepScheduleRuleComponent: FC = ({ setMyStepData(myDefaultValues); setFieldValue(form, schema, myDefaultValues); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValues]); useEffect(() => { if (setForm != null) { setForm(RuleStep.scheduleRule, form); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); return isReadOnlyView && myStepData != null ? ( diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/throttle_select_field/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/throttle_select_field/index.tsx index 133f25ef3a03f..bf3498b28cd45 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/throttle_select_field/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/throttle_select_field/index.tsx @@ -31,6 +31,7 @@ export const ThrottleSelectField: ThrottleSelectField = (props) => { props.field.setValue(throttle); props.handleChange(throttle); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [props.field.setValue, props.handleChange] ); const newEuiFieldProps = { ...props.euiFieldProps, onChange }; diff --git a/x-pack/plugins/security_solution/public/alerts/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/user_info/index.tsx index 8753064751f76..fc3fec7c29c1f 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/user_info/index.tsx @@ -171,18 +171,21 @@ export const useUserInfo = (): State => { if (loading !== privilegeLoading || indexNameLoading) { dispatch({ type: 'updateLoading', loading: privilegeLoading || indexNameLoading }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, privilegeLoading, indexNameLoading]); useEffect(() => { if (!loading && hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) { dispatch({ type: 'updateHasIndexManage', hasIndexManage: hasApiIndexManage }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, hasIndexManage, hasApiIndexManage]); useEffect(() => { if (!loading && hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) { dispatch({ type: 'updateHasIndexWrite', hasIndexWrite: hasApiIndexWrite }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, hasIndexWrite, hasApiIndexWrite]); useEffect(() => { @@ -193,30 +196,35 @@ export const useUserInfo = (): State => { ) { dispatch({ type: 'updateIsSignalIndexExists', isSignalIndexExists: isApiSignalIndexExists }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, isSignalIndexExists, isApiSignalIndexExists]); useEffect(() => { if (!loading && isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) { dispatch({ type: 'updateIsAuthenticated', isAuthenticated: isApiAuthenticated }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, isAuthenticated, isApiAuthenticated]); useEffect(() => { if (!loading && hasEncryptionKey !== isApiEncryptionKey && isApiEncryptionKey != null) { dispatch({ type: 'updateHasEncryptionKey', hasEncryptionKey: isApiEncryptionKey }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, hasEncryptionKey, isApiEncryptionKey]); useEffect(() => { if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, canUserCRUD, capabilitiesCanUserCRUD]); useEffect(() => { if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, signalIndexName, apiSignalIndexName]); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_privilege_user.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_privilege_user.tsx index e67afd686a7ca..dda9b50239cde 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_privilege_user.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_privilege_user.tsx @@ -83,6 +83,7 @@ export const usePrivilegeUser = (): ReturnPrivilegeUser => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { loading, ...privilegeUser }; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_signal_index.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_signal_index.tsx index 6c428bd9354ee..65a2721013b5e 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_signal_index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/alerts/use_signal_index.tsx @@ -103,6 +103,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { loading, ...signalIndex }; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/fetch_index_patterns.tsx index 157f1490971f1..640d6f9a17fd1 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/fetch_index_patterns.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/fetch_index_patterns.tsx @@ -94,6 +94,7 @@ export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return => isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [indices]); return [{ browserFields, isLoading, indices, indicesExists, indexPatterns }, setIndices]; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/persist_rule.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/persist_rule.tsx index 03080bf68cbf5..fd139d59c0a27 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/persist_rule.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/persist_rule.tsx @@ -53,6 +53,7 @@ export const usePersistRule = (): ReturnPersistRule => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [rule]); return [{ isLoading, isSaved }, setRule]; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_pre_packaged_rules.tsx index de4037ce7134c..5f5ee53c29caf 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_pre_packaged_rules.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_pre_packaged_rules.tsx @@ -185,6 +185,7 @@ export const usePrePackagedRules = ({ isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [canUserCRUD, hasIndexWrite, isAuthenticated, hasEncryptionKey, isSignalIndexExists]); return { diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule.tsx index 6ae5da3e56ff6..3256273fb8425 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule.tsx @@ -55,6 +55,7 @@ export const useRule = (id: string | undefined): ReturnRule => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); return [loading, rule]; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule_status.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule_status.tsx index b9f5e1f152c21..ec1da29de4ba8 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule_status.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rule_status.tsx @@ -64,6 +64,7 @@ export const useRuleStatus = (id: string | undefined | null): ReturnRuleStatus = isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); return [loading, ruleStatus, fetchRuleStatus.current]; @@ -119,6 +120,7 @@ export const useRulesStatuses = (rules: Rules): ReturnRulesStatuses => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [rules]); return { loading, rulesStatuses }; diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rules.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rules.tsx index 3a074f2bc3785..1a1dbc6e2b368 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rules.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_rules.tsx @@ -46,7 +46,7 @@ export const useRules = ({ let isSubscribed = true; const abortCtrl = new AbortController(); - async function fetchData(forceReload: boolean = false) { + async function fetchData() { try { setLoading(true); const fetchRulesResult = await fetchRules({ @@ -80,7 +80,7 @@ export const useRules = ({ fetchData(); reFetchRules.current = (refreshPrePackagedRule: boolean = false) => { - fetchData(true); + fetchData(); if (refreshPrePackagedRule && refetchPrePackagedRulesStatus != null) { refetchPrePackagedRulesStatus(); } @@ -89,12 +89,14 @@ export const useRules = ({ isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ pagination.page, pagination.perPage, filterOptions.filter, filterOptions.sortField, filterOptions.sortOrder, + // eslint-disable-next-line react-hooks/exhaustive-deps filterOptions.tags?.sort().join(), filterOptions.showCustomRules, filterOptions.showElasticRules, diff --git a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_tags.tsx b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_tags.tsx index ebfe73f2f0863..038f974e1394e 100644 --- a/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_tags.tsx +++ b/x-pack/plugins/security_solution/public/alerts/containers/detection_engine/rules/use_tags.tsx @@ -53,6 +53,7 @@ export const useTags = (): ReturnTags => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return [loading, tags, reFetchTags.current]; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx index 9e61ec0eb3bcf..80ef5681189c5 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx @@ -235,6 +235,7 @@ export const AllRules = React.memo( : [], reFetchRules: reFetchRulesData, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ dispatch, dispatchToaster, @@ -313,6 +314,7 @@ export const AllRules = React.memo( ))} ), + // eslint-disable-next-line react-hooks/exhaustive-deps [allRulesTabs, allRulesTab, setAllRulesTab] ); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx index 2bc19ae45a078..c65271c3cc014 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx @@ -45,6 +45,7 @@ const RulesTableFiltersComponent = ({ useEffect(() => { reFetchTags(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [rulesCustomInstalled, rulesInstalled]); // Propagate filter changes to parent diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx index dd290ec1ae582..de7c99acee6e5 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx @@ -114,6 +114,7 @@ const CreateRulePageComponent: React.FC = () => { const actionMessageParams = useMemo( () => getActionMessageParams((stepsData.current['define-rule'].data as DefineStepRule).ruleType), + // eslint-disable-next-line react-hooks/exhaustive-deps [stepsData.current['define-rule'].data] ); @@ -155,6 +156,7 @@ const CreateRulePageComponent: React.FC = () => { } } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [isStepRuleInReadOnlyView, openAccordionId, stepsData.current, setRule] ); @@ -171,6 +173,7 @@ const CreateRulePageComponent: React.FC = () => { } return 'passive'; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [openAccordionId, stepsData.current] ); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx index ebd6ed118f937..43792e8bd19f4 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx @@ -191,6 +191,7 @@ export const RuleDetailsPageComponent: FC = ({ ))} ), + // eslint-disable-next-line react-hooks/exhaustive-deps [ruleDetailTabs, ruleDetailTab, setRuleDetailTab] ); const ruleError = useMemo( @@ -203,6 +204,7 @@ export const RuleDetailsPageComponent: FC = ({ date={rule?.last_failure_at} /> ) : null, + // eslint-disable-next-line react-hooks/exhaustive-deps [rule, ruleDetailTab] ); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx index 8fc646c01b17c..73e76165183c5 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx @@ -104,6 +104,7 @@ const EditRulePageComponent: FC = () => { }); const [{ isLoading, isSaved }, setRule] = usePersistRule(); const [tabHasError, setTabHasError] = useState([]); + // eslint-disable-next-line react-hooks/exhaustive-deps const actionMessageParams = useMemo(() => getActionMessageParams(rule?.type), [rule]); const setStepsForm = useCallback( (step: RuleStep, form: FormHook) => { @@ -113,6 +114,7 @@ const EditRulePageComponent: FC = () => { form.submit(); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [initForm, selectedTab] ); const tabs = useMemo( @@ -206,6 +208,7 @@ const EditRulePageComponent: FC = () => { ), }, ], + // eslint-disable-next-line react-hooks/exhaustive-deps [ rule, loading, @@ -265,6 +268,7 @@ const EditRulePageComponent: FC = () => { } else { setTabHasError(invalidForms); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ stepsForm, myAboutRuleForm, @@ -320,6 +324,7 @@ const EditRulePageComponent: FC = () => { setInitForm(true); setSelectedTab(tab); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedTab, stepsForm.current] ); @@ -338,6 +343,7 @@ const EditRulePageComponent: FC = () => { useEffect(() => { const tabIndex = rule?.immutable ? 3 : 0; setSelectedTab(tabs[tabIndex]); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [rule]); if (isSaved) { diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx index 277352c39df65..a57fae8081bea 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx @@ -59,6 +59,7 @@ export const AddComment = React.memo( `${comment}${comment.length > 0 ? '\n\n' : ''}${insertQuote}` ); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [insertQuote]); const onSubmit = useCallback(async () => { @@ -67,9 +68,10 @@ export const AddComment = React.memo( if (onCommentSaving != null) { onCommentSaving(); } - await postComment(data, onCommentPosted); + postComment(data, onCommentPosted); form.reset(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form, onCommentPosted, onCommentSaving]); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 12f0d02eb10f6..32a7c4078071e 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -134,6 +134,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { (refetchFilter: () => void) => { filterRefetch.current = refetchFilter; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [filterRefetch.current] ); const refreshCases = useCallback( @@ -146,6 +147,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { filterRefetch.current(); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [filterOptions, queryParams, filterRefetch.current] ); @@ -158,6 +160,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { refreshCases(); dispatchResetIsUpdated(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isDeleted, isUpdated]); const confirmDeleteModal = useMemo( () => ( @@ -172,12 +175,14 @@ export const AllCases = React.memo(({ userCanCrud }) => { )} /> ), + // eslint-disable-next-line react-hooks/exhaustive-deps [deleteBulk, deleteThisCase, isDisplayConfirmDeleteModal] ); const toggleDeleteModal = useCallback((deleteCase: Case) => { handleToggleModal(); setDeleteThisCase(deleteCase); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const toggleBulkDeleteModal = useCallback( @@ -192,6 +197,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { const convertToDeleteCases: DeleteCase[] = caseIds.map((id) => ({ id })); setDeleteBulk(convertToDeleteCases); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedCases] ); @@ -199,6 +205,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { (status: string) => { updateBulkStatus(selectedCases, status); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedCases] ); @@ -220,6 +227,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { })} /> ), + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedCaseIds, filterOptions.status, toggleBulkDeleteModal] ); const handleDispatchUpdate = useCallback( @@ -261,6 +269,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { setQueryParams(newQueryParams); refreshCases(false); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [queryParams] ); @@ -274,6 +283,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { setFilters(newFilterOptions); refreshCases(false); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [filterOptions, queryParams] ); @@ -296,6 +306,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { }; const euiBasicTableSelectionProps = useMemo>( () => ({ onSelectionChange: setSelectedCases }), + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedCases] ); const isCasesLoading = useMemo( diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index 42340b455c3af..63172bd6ad6bb 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -66,12 +66,14 @@ const CasesTableFiltersComponent = ({ const newReporters = selectedReporters.filter((r) => reporters.includes(r)); handleSelectedReporters(newReporters); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [reporters]); useEffect(() => { if (selectedTags.length) { const newTags = selectedTags.filter((t) => tags.includes(t)); handleSelectedTags(newTags); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [tags]); const handleSelectedReporters = useCallback( @@ -84,6 +86,7 @@ const CasesTableFiltersComponent = ({ onFilterChanged({ reporters: reportersObj }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedReporters, respReporters] ); @@ -94,6 +97,7 @@ const CasesTableFiltersComponent = ({ onFilterChanged({ tags: newTags }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedTags] ); const handleOnSearch = useCallback( @@ -104,6 +108,7 @@ const CasesTableFiltersComponent = ({ onFilterChanged({ search: trimSearch }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [search] ); const handleToggleFilter = useCallback( @@ -113,6 +118,7 @@ const CasesTableFiltersComponent = ({ onFilterChanged({ status: showOpen ? 'open' : 'closed' }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [showOpenCases] ); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx index cd9318a355e3c..df1082ec48f91 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx @@ -44,6 +44,7 @@ const CaseViewActionsComponent: React.FC = ({ onConfirm={handleOnDeleteConfirm.bind(null, [{ id: caseData.id, title: caseData.title }])} /> ), + // eslint-disable-next-line react-hooks/exhaustive-deps [isDisplayConfirmDeleteModal, caseData] ); const propertyActions = useMemo( diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx index 947d68fd04f7b..b8151cb6fe18c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx @@ -68,6 +68,7 @@ const ConnectorsComponent: React.FC = ({ ), + // eslint-disable-next-line react-hooks/exhaustive-deps [connectorsName, updateConnectorDisabled] ); diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx index c5481f592e750..018d3a0fdb4e0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx @@ -91,6 +91,7 @@ const ConnectorsDropdownComponent: React.FC = ({ } return connectorsFormatted; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [connectors]); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/field_mapping.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/field_mapping.tsx index 415faa96eeedd..fe5c0c19820a1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/field_mapping.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/field_mapping.tsx @@ -95,6 +95,7 @@ const FieldMappingComponent: React.FC = ({ const myMapping = mapping ?? defaultMapping; onChangeMapping(setActionTypeToMapping(caseField, newActionType, myMapping)); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [mapping] ); @@ -103,6 +104,7 @@ const FieldMappingComponent: React.FC = ({ const myMapping = mapping ?? defaultMapping; onChangeMapping(setThirdPartyToMapping(caseField, newThirdPartyField, myMapping)); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [mapping] ); diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx index 6e1ef293fd5c4..256c8893be941 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx @@ -77,6 +77,7 @@ const ConfigureCasesComponent: React.FC = ({ userC // ActionsConnectorsContextProvider reloadConnectors prop expects a Promise. // TODO: Fix it if reloadConnectors type change. + // eslint-disable-next-line react-hooks/exhaustive-deps const reloadConnectors = useCallback(async () => refetchConnectors(), []); const isLoadingAny = isLoadingConnectors || persistLoading || loadingCaseConfigure; const updateConnectorDisabled = isLoadingAny || !connectorIsValid || connectorId === 'none'; @@ -89,6 +90,7 @@ const ConfigureCasesComponent: React.FC = ({ userC (isVisible: boolean) => { setAddFlyoutVisibility(isVisible); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [currentConfiguration, connectorId, closureType] ); @@ -96,6 +98,7 @@ const ConfigureCasesComponent: React.FC = ({ userC (isVisible: boolean) => { setEditFlyoutVisibility(isVisible); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [currentConfiguration, connectorId, closureType] ); @@ -113,6 +116,7 @@ const ConfigureCasesComponent: React.FC = ({ userC closureType, }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [connectorId, closureType, version] ); @@ -125,6 +129,7 @@ const ConfigureCasesComponent: React.FC = ({ userC closureType: type, }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [connectorId, closureType, version] ); @@ -141,6 +146,7 @@ const ConfigureCasesComponent: React.FC = ({ userC ) { setConnectorIsValid(true); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [connectors, connectorId]); useEffect(() => { @@ -149,6 +155,7 @@ const ConfigureCasesComponent: React.FC = ({ userC connectors.find((c) => c.id === connectorId) as ActionConnectorTableItem ); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [connectors, connectorId]); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx b/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx index 9e058ee5cf09e..1706fa38bb8a0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx @@ -33,6 +33,7 @@ export const ConnectorSelector = ({ useEffect(() => { field.setValue(defaultValue); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValue]); const handleContentChange = useCallback( diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index ae0ffe498c391..1b65b2582c760 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -91,8 +91,10 @@ export const Create = React.memo(() => { const onSubmit = useCallback(async () => { const { isValid, data } = await form.submit(); if (isValid) { + // `postCase`'s type is incorrect, it actually returns a promise await postCase(data); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [form]); const handleSetIsCancel = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/cases/components/open_closed_stats/index.tsx b/x-pack/plugins/security_solution/public/cases/components/open_closed_stats/index.tsx index b9dab13090aca..e7d5299842494 100644 --- a/x-pack/plugins/security_solution/public/cases/components/open_closed_stats/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/open_closed_stats/index.tsx @@ -24,6 +24,7 @@ export const OpenClosedStats = React.memo( description: isLoading ? : caseCount ?? 'N/A', }, ], + // eslint-disable-next-line react-hooks/exhaustive-deps [caseCount, caseStatus, isLoading, dataTestSubj] ); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx index 2e9e3cce4306a..f18870787ded1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx @@ -141,6 +141,7 @@ export const usePushToService = ({ errors = [...errors, getKibanaConfigError()]; } return errors; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [actionLicense, caseStatus, connectors.length, caseConnectorId, loadingLicense, urlSearch]); const pushToServiceButton = useMemo(() => { @@ -160,6 +161,7 @@ export const usePushToService = ({ : i18n.PUSH_THIRD(caseConnectorName)} ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ caseConnectorId, caseConnectorName, diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx index da49014eb0204..52c2779a93fc0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx @@ -85,6 +85,7 @@ export const UserActionTree = React.memo( updateCase, }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [caseData, handleManageMarkdownEditId, patchComment, updateCase] ); @@ -109,6 +110,7 @@ export const UserActionTree = React.memo( window.clearTimeout(handlerTimeoutId.current); }, 2400); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [handlerTimeoutId.current] ); @@ -155,6 +157,7 @@ export const UserActionTree = React.memo( showLoading={false} /> ), + // eslint-disable-next-line react-hooks/exhaustive-deps [caseData.id, handleUpdate, insertQuote, userCanCrud] ); @@ -165,6 +168,7 @@ export const UserActionTree = React.memo( handleOutlineComment(commentId); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [commentId, initLoading, isLoadingUserActions, isLoadingIds]); return ( <> diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.tsx index 03dd599da88e5..b3a5f1e0158d8 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.tsx @@ -74,6 +74,7 @@ export const UserActionMarkdown = ({ updateTimeline: dispatchUpdateTimeline(dispatch), }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [apolloClient] ); @@ -114,6 +115,7 @@ export const UserActionMarkdown = ({ ); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [handleCancelAction, handleSaveAction] ); return isEditable ? ( diff --git a/x-pack/plugins/security_solution/public/cases/components/user_list/index.tsx b/x-pack/plugins/security_solution/public/cases/components/user_list/index.tsx index 0606da371d16a..86ff42561c409 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_list/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_list/index.tsx @@ -84,6 +84,7 @@ export const UserList = React.memo(({ email, headline, loading, users }: UserLis window.open(`mailto:${emailAddress}?subject=${email.subject}&body=${email.body}`, '_blank'); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [email.subject] ); return users.filter(({ username }) => username != null && username !== '').length > 0 ? ( diff --git a/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx b/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx index 5a85a3a0633bc..e89212036ec20 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx @@ -253,6 +253,7 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.firstLoad]); const persistCaseConfigure = useCallback( @@ -311,11 +312,13 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [state.version] ); useEffect(() => { refetchCaseConfigure(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { diff --git a/x-pack/plugins/security_solution/public/cases/containers/configure/use_connectors.tsx b/x-pack/plugins/security_solution/public/cases/containers/configure/use_connectors.tsx index 9cd755864d37b..812e580ad653f 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/configure/use_connectors.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/configure/use_connectors.tsx @@ -50,10 +50,12 @@ export const useConnectors = (): ReturnConnectors => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { refetchConnectors(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx index ef68f4f48b1a0..c333ff4207833 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx @@ -108,6 +108,7 @@ export const useUpdateCases = (): UseUpdateCases => { cancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const dispatchResetIsUpdated = useCallback(() => { @@ -121,6 +122,7 @@ export const useUpdateCases = (): UseUpdateCases => { version: theCase.version, })); dispatchUpdateCases(updateCasesStatus); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { ...state, updateBulkStatus, dispatchResetIsUpdated }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_delete_cases.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_delete_cases.tsx index 2e8c7dfae2313..f47e9e51f865a 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_delete_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_delete_cases.tsx @@ -110,6 +110,7 @@ export const useDeleteCases = (): UseDeleteCase => { abortCtrl.abort(); cancel = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const dispatchToggleDeleteModal = useCallback(() => { @@ -118,6 +119,7 @@ export const useDeleteCases = (): UseDeleteCase => { const dispatchResetIsDeleted = useCallback(() => { dispatch({ type: 'RESET_IS_DELETED' }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.isDisplayConfirmDeleteModal]); const handleOnDeleteConfirm = useCallback( @@ -125,10 +127,12 @@ export const useDeleteCases = (): UseDeleteCase => { dispatchDeleteCases(cases); dispatchToggleDeleteModal(); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [state.isDisplayConfirmDeleteModal] ); const handleToggleModal = useCallback(() => { dispatchToggleDeleteModal(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.isDisplayConfirmDeleteModal]); return { ...state, dispatchResetIsDeleted, handleOnDeleteConfirm, handleToggleModal }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_action_license.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_action_license.tsx index 563e2a4a58c70..e289a1973cf6e 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_action_license.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_action_license.tsx @@ -65,10 +65,12 @@ export const useGetActionLicense = (): ActionLicenseState => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [actionLicenseState]); useEffect(() => { fetchActionLicense(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { ...actionLicenseState }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_case.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_case.tsx index 01ada00ba9b72..ea4da41151993 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_case.tsx @@ -118,10 +118,12 @@ export const useGetCase = (caseId: string): UseGetCase => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [caseId]); useEffect(() => { callFetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [caseId]); return { ...state, fetchCase: callFetch, updateCase }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_case_user_actions.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_case_user_actions.tsx index 050b2815dc511..76d939de06a0a 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_case_user_actions.tsx @@ -210,6 +210,7 @@ export const useGetCaseUserActions = ( abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [caseUserActionsState, caseConnectorId] ); @@ -217,6 +218,7 @@ export const useGetCaseUserActions = ( if (!isEmpty(caseId)) { fetchCaseUserActions(caseId); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [caseId, caseConnectorId]); return { ...caseUserActionsState, fetchCaseUserActions }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx index 45b571a8fe7e2..fdf526a1e4d88 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx @@ -184,8 +184,10 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { abortCtrl.abort(); didCancel = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => fetchCases(state.filterOptions, state.queryParams), [ state.queryParams, state.filterOptions, @@ -224,11 +226,13 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { didCancel = true; }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [state.filterOptions, state.queryParams] ); const refetchCases = useCallback(() => { fetchCases(state.filterOptions, state.queryParams); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.filterOptions, state.queryParams]); return { diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx index 476462b7e4c28..5260b6d5cc283 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx @@ -69,10 +69,12 @@ export const useGetCasesStatus = (): UseGetCasesStatus => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [casesStatusState]); useEffect(() => { fetchCasesStatus(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_reporters.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_reporters.tsx index d723b8cc37c23..b4ca775660910 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_reporters.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_reporters.tsx @@ -79,10 +79,12 @@ export const useGetReporters = (): UseGetReporters => { didCancel = true; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [reportersState]); useEffect(() => { fetchReporters(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { ...reportersState, fetchReporters }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_tags.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_tags.tsx index 14f5e35bc4976..eded326719e52 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_tags.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_tags.tsx @@ -89,6 +89,7 @@ export const useGetTags = (): UseGetTags => { }; useEffect(() => { callFetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { ...state, fetchTags: callFetch }; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_post_case.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_post_case.tsx index 13cfc2738620f..0752fe9b2071c 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_post_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_post_case.tsx @@ -86,6 +86,7 @@ export const usePostCase = (): UsePostCase => { abortCtrl.abort(); cancel = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { ...state, postCase: postMyCase }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_post_comment.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_post_comment.tsx index 9a52eaaf0db6b..e6cb8a9c3d150 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_post_comment.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_post_comment.tsx @@ -79,6 +79,7 @@ export const usePostComment = (caseId: string): UsePostComment => { cancel = true; }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [caseId] ); diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_post_push_to_service.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_post_push_to_service.tsx index 4d25ac7fbf0db..0d8a4c04ca7cd 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_post_push_to_service.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_post_push_to_service.tsx @@ -146,6 +146,7 @@ export const usePostPushToService = (): UsePostPushToService => { abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_update_case.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_update_case.tsx index 77cf53165d914..18dd9f5278503 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_update_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_update_case.tsx @@ -118,6 +118,7 @@ export const useUpdateCase = ({ caseId }: { caseId: string }): UseUpdateCase => abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_update_comment.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_update_comment.tsx index 821fd5523a751..f896185dbccfb 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_update_comment.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_update_comment.tsx @@ -111,6 +111,7 @@ export const useUpdateComment = (): UseUpdateComment => { abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx index b19343a9f4a5c..bb6ba01821835 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx @@ -69,6 +69,7 @@ const AlertsTableComponent: React.FC = ({ endDate, startDate, pageFilters title: i18n.ALERTS_TABLE_TITLE, unit: i18n.UNIT, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( { const isDarkMode = useUiSetting(DEFAULT_DARK_MODE); const defaultTheme = isDarkMode ? DARK_THEME : LIGHT_THEME; + // eslint-disable-next-line react-hooks/exhaustive-deps const themeValue = useMemo(() => mergeWithDefaultTheme(theme, defaultTheme), []); return themeValue; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index c33677e41db0e..32f05e7c837a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -126,6 +126,7 @@ export const DragDropContextWrapperComponent = React.memo( () => () => { unRegisterProvider(); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx index 998d18291f638..4eeefdbe2fca0 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx @@ -63,6 +63,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ onFilterAdded(); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field, value, filterManager, onFilterAdded]); const filterOutValue = useCallback(() => { @@ -77,6 +78,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ onFilterAdded(); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [field, value, filterManager, onFilterAdded]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index d0bd87188e541..6b4baac0ff26c 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -103,9 +103,11 @@ const EventsViewerComponent: React.FC = ({ } = useManageTimeline(); useEffect(() => { setIsTimelineLoading({ id, isLoading: isQueryLoading }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isQueryLoading]); useEffect(() => { setTimelineFilterManager({ id, filterManager }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterManager]); const { queryFields, title, unit } = useMemo(() => getManageTimelineById(id), [ diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index ec726cd603579..9f1d71108e2f1 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -74,6 +74,7 @@ const StatefulEventsViewerComponent: React.FC = ({ return () => { deleteEventQuery({ id, inputId: 'global' }); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_entries.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_entries.tsx index d0236adc27c6c..fa21d61b06ebe 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_entries.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_entries.tsx @@ -109,6 +109,7 @@ const ExceptionEntriesComponent = ({ }, }, ], + // eslint-disable-next-line react-hooks/exhaustive-deps [entries] ); diff --git a/x-pack/plugins/security_solution/public/common/components/generic_downloader/index.tsx b/x-pack/plugins/security_solution/public/common/components/generic_downloader/index.tsx index 2f68da0c18727..2e8d5f77afc83 100644 --- a/x-pack/plugins/security_solution/public/common/components/generic_downloader/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/generic_downloader/index.tsx @@ -95,6 +95,7 @@ export const GenericDownloaderComponent = ({ isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ids]); return ; diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/editable_title.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/editable_title.tsx index 0c6f7258d09dc..6f6c94a0b1124 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/editable_title.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/editable_title.tsx @@ -57,6 +57,7 @@ const EditableTitleComponent: React.FC = ({ onSubmit(changedTitle); } setEditMode(false); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [changedTitle, title]); const handleOnChange = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx b/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx index ba9c136445983..56f0c825aa59e 100644 --- a/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx @@ -45,6 +45,7 @@ export const HelpMenu = React.memo(() => { }, ], }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return null; diff --git a/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx index c4e0a0de5ae81..a42628cecff8e 100644 --- a/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx @@ -109,6 +109,7 @@ export const ImportDataModalComponent = ({ errorToToaster({ title: errorMessage, error, dispatchToaster }); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFiles, overwrite]); const handleCloseModal = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index 8a41d4c36edbb..637f0d8d53057 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -242,6 +242,7 @@ const ReputationLinkComponent: React.FC<{ name: isDefaultReputationLink(name) ? defaultNameMapping[name] : name, url_template: url_template.replace(`{{ip}}`, encodeURIComponent(domain)), })), + // eslint-disable-next-line react-hooks/exhaustive-deps [ipReputationLinksSetting, domain, defaultNameMapping, allItemsLimit] ); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx index b0df2b6b5b60f..d92952992d997 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/index.tsx @@ -135,6 +135,7 @@ export const MarkdownEditor = React.memo<{ ), }, ], + // eslint-disable-next-line react-hooks/exhaustive-deps [content, isDisabled, placeholder] ); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 08edae208fefa..3e196c4b7bad4 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -19,12 +19,7 @@ import { MatrixLoader } from './matrix_loader'; import { Panel } from '../panel'; import { getBarchartConfigs, getCustomChartData } from './utils'; import { useQuery } from '../../containers/matrix_histogram'; -import { - MatrixHistogramProps, - MatrixHistogramOption, - HistogramAggregation, - MatrixHistogramQueryProps, -} from './types'; +import { MatrixHistogramProps, MatrixHistogramOption, MatrixHistogramQueryProps } from './types'; import { InspectButtonContainer } from '../inspect'; import { State, inputsSelectors } from '../../store'; @@ -125,6 +120,7 @@ export const MatrixHistogramComponent: React.FC< yTickFormatter, showLegend, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [ chartHeight, startDate, @@ -145,21 +141,20 @@ export const MatrixHistogramComponent: React.FC< stackByOptions.find((co) => co.value === event.target.value) ?? defaultStackByOption ); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); - const { data, loading, inspect, totalCount, refetch = noop } = useQuery<{}, HistogramAggregation>( - { - endDate, - errorMessage, - filterQuery, - histogramType, - indexToAdd, - startDate, - isInspected, - stackByField: selectedStackByOption.value, - } - ); + const { data, loading, inspect, totalCount, refetch = noop } = useQuery({ + endDate, + errorMessage, + filterQuery, + histogramType, + indexToAdd, + startDate, + isInspected, + stackByField: selectedStackByOption.value, + }); const titleWithStackByField = useMemo( () => (title != null && typeof title === 'function' ? title(selectedStackByOption) : title), @@ -260,7 +255,7 @@ export const MatrixHistogram = React.memo(MatrixHistogramComponent); const makeMapStateToProps = () => { const getQuery = inputsSelectors.globalQueryByIdSelector(); - const mapStateToProps = (state: State, { type, id }: OwnProps) => { + const mapStateToProps = (state: State, { id }: OwnProps) => { const { isInspected } = getQuery(state, id); return { isInspected, diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts index e30f1e9374c26..a9e6cdd19bb20 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts @@ -99,12 +99,6 @@ export interface GroupBucket { }; } -export interface HistogramAggregation { - histogramAgg: { - buckets: GroupBucket[]; - }; -} - export interface BarchartConfigs { series: { xScaleType: ScaleType; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_table_data.ts b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_table_data.ts index a76c4c850b8d9..8568c7e6b5575 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_table_data.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_table_data.ts @@ -121,13 +121,17 @@ export const useAnomaliesTableData = ({ isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ + // eslint-disable-next-line react-hooks/exhaustive-deps influencersOrCriteriaToString(influencers), + // eslint-disable-next-line react-hooks/exhaustive-deps influencersOrCriteriaToString(criteriaFields), startDate, endDate, skip, userPermissions, + // eslint-disable-next-line react-hooks/exhaustive-deps siemJobIds.sort().join(), ]); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/permissions/ml_capabilities_provider.tsx b/x-pack/plugins/security_solution/public/common/components/ml/permissions/ml_capabilities_provider.tsx index 1d5c1b36e22af..c83271a56be5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/permissions/ml_capabilities_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/permissions/ml_capabilities_provider.tsx @@ -60,6 +60,7 @@ export const MlCapabilitiesProvider = React.memo<{ children: JSX.Element }>(({ c isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs.tsx index a84d88782926c..7f0a8dea1913e 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_siem_jobs.tsx @@ -74,6 +74,7 @@ export const useSiemJobs = (refetchData: boolean): Return => { isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [refetchData, userPermissions]); return [loading, siemJobs]; diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx index dd6e2d3f47110..de4185c8644a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx @@ -46,6 +46,7 @@ export const GroupsFilterPopoverComponent = ({ useEffect(() => { onSelectedGroupsChanged(selectedGroups); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedGroups.sort().join()]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx index e0a24a5fc083c..0cbff9e70eff7 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx @@ -54,6 +54,7 @@ export const SiemNavigationComponent: React.FC< chrome ); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [chrome, pathName, search, navTabs, urlState, state]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx index b0b9309b44c68..a182102329f05 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx @@ -134,6 +134,7 @@ const SearchBarComponent = memo( window.setTimeout(() => updateSearch(updateSearchBar), 0); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id, end, filterQuery, fromStr, queries, start, toStr] ); @@ -153,6 +154,7 @@ const SearchBarComponent = memo( queries.forEach((q) => q.refetch && (q.refetch as inputsModel.Refetch)()); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id, queries, filterManager] ); @@ -160,6 +162,7 @@ const SearchBarComponent = memo( (newSavedQuery: SavedQuery) => { setSavedQuery({ id, savedQuery: newSavedQuery }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -196,6 +199,7 @@ const SearchBarComponent = memo( updateSearch(updateSearchBar); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id, end, fromStr, start, toStr] ); @@ -218,6 +222,7 @@ const SearchBarComponent = memo( filterManager, }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [id, end, filterManager, fromStr, start, toStr, savedQuery]); useEffect(() => { @@ -241,6 +246,7 @@ const SearchBarComponent = memo( isSubscribed = false; subscriptions.unsubscribe(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const indexPatterns = useMemo(() => [indexPattern], [indexPattern]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx index ec9cfc62ebada..dee730059b03a 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx @@ -183,6 +183,7 @@ export const useKpiMatrixStatus = ( }; }) ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); return statItemsProps; diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx index 6b979df653eb4..84ff1120f6496 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx @@ -117,6 +117,7 @@ export const SuperDatePickerComponent = React.memo( refetchQuery(queries); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [end, id, isQuickSelection, kqlQuery, start, timelineId] ); @@ -136,6 +137,7 @@ export const SuperDatePickerComponent = React.memo( refetchQuery(queries); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id, isQuickSelection, duration, policy, toStr] ); @@ -175,6 +177,7 @@ export const SuperDatePickerComponent = React.memo( setIsQuickSelection(newIsQuickSelection); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [recentlyUsedRanges, kqlQuery] ); diff --git a/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx index 944e8f6d1fb39..35036ef4b16b5 100644 --- a/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx @@ -69,6 +69,7 @@ describe('Toaster', () => { if (toasts.length === 0) { dispatch({ type: 'addToaster', toast: mockToast }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx index 3e83866c7cf62..c28f5ab8aa44f 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx @@ -109,6 +109,7 @@ const StatefulTopNComponent: React.FC = ({ isManagedTimeline(ACTIVE_TIMELINE_REDUX_ID) ? getManageTimelineById(ACTIVE_TIMELINE_REDUX_ID) : { documentType: null, id: null, indexToAdd: null }, + // eslint-disable-next-line react-hooks/exhaustive-deps [getManageTimelineById] ); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx index b3436a7da8297..ef60967e70ac3 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx @@ -46,7 +46,6 @@ export const useUrlStateHooks = ({ pathName, search, setInitialStateFromUrl, - tabName, updateTimeline, updateTimelineIsLoading, urlState, @@ -206,10 +205,12 @@ export const useUrlStateHooks = ({ } else if (pathName !== prevProps.pathName) { handleInitialize(type, pageName === SiemPageName.detections); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isInitializing, history, pathName, pageName, prevProps, urlState]); useEffect(() => { document.title = `${getTitle(pageName, detailName, navTabs)} - Kibana`; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [pageName]); return null; diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx index a5574bd2a57c7..0beecc500e722 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx @@ -35,6 +35,7 @@ export const AnomaliesQueryTabBody = ({ deleteQuery({ id: ID }); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const [, siemJobs] = useSiemJobs(true); diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index 260b13c23f39c..6050dafc0b191 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -33,7 +33,7 @@ export interface OwnProps extends QueryTemplateProps { indexKey: LastEventIndexKey; } -export function useLastEventTimeQuery( +export function useLastEventTimeQuery( indexKey: LastEventIndexKey, details: LastTimeDetails, sourceId: string @@ -84,6 +84,7 @@ export function useLastEventTimeQuery( const signal = abortCtrl.signal; fetchLastEventTime(signal); return () => abortCtrl.abort(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [apolloClient, indexKey, details.hostName, details.ip]); return { lastSeen, loading, errorMessage }; diff --git a/x-pack/plugins/security_solution/public/common/containers/global_time/index.tsx b/x-pack/plugins/security_solution/public/common/containers/global_time/index.tsx index 4632e9aee3fdd..9b9b5c5d815b9 100644 --- a/x-pack/plugins/security_solution/public/common/containers/global_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/global_time/index.tsx @@ -59,6 +59,7 @@ export const GlobalTimeComponent: React.FC = ({ return () => { deleteAllQuery({ id: 'global' }); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index c675c2745c66a..8c7acfc18ece6 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -17,7 +17,7 @@ import { inputsModel } from '../../store'; import { MatrixHistogramGqlQuery } from './index.gql_query'; import { GetMatrixHistogramQuery, MatrixOverTimeHistogramData } from '../../../graphql/types'; -export const useQuery = ({ +export const useQuery = ({ endDate, errorMessage, filterQuery, @@ -102,6 +102,7 @@ export const useQuery = ({ isSubscribed = false; abortCtrl.abort(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ defaultIndex, errorMessage, diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index 26fb14c6a9c1e..ad480ad2c496b 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -75,7 +75,7 @@ export const getIndexFields = memoizeOne( ); export const getBrowserFields = memoizeOne( - (title: string, fields: IndexField[]): BrowserFields => + (_title: string, fields: IndexField[]): BrowserFields => fields && fields.length > 0 ? fields.reduce( (accumulator: BrowserFields, field: IndexField) => @@ -173,6 +173,7 @@ export const useWithSource = (sourceId: string, indices: string[]) => { const signal = abortCtrl.signal; fetchSource(signal); return () => abortCtrl.abort(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [apolloClient, sourceId, indices]); return { indicesExist, browserFields, indexPattern, loading, errorMessage }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_add_to_timeline.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_add_to_timeline.tsx index b73c400bc0460..eba8d14d60538 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_add_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_add_to_timeline.tsx @@ -159,6 +159,7 @@ export const useAddToTimeline = ({ document.body.classList.remove(IS_DRAGGING_CLASS_NAME); // it was not possible to perform a drag and drop } }, 0); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [_sensorApiSingleton, draggableId]); return startDragToTimeline; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_index_patterns.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_index_patterns.tsx index 05b0521e35217..837d6d5f70b5d 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_index_patterns.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_index_patterns.tsx @@ -45,6 +45,7 @@ export const useIndexPatterns = (refreshToggle = false): Return => { return () => { isSubscribed = false; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [refreshToggle]); return [isLoading, indexPatterns]; diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx index cb227563b697a..30e2c650a70cc 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx @@ -53,6 +53,7 @@ export const withConnectorFlyout = ({ if (!isEmpty(apiUrl)) { secretKeys.forEach((key: string) => editActionSecrets(key, '')); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (isEmpty(mapping)) { @@ -64,6 +65,7 @@ export const withConnectorFlyout = ({ const handleOnChangeActionConfig = useCallback( (key: string, value: string) => editActionConfig(key, value), + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); @@ -73,11 +75,13 @@ export const withConnectorFlyout = ({ editActionConfig(key, ''); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [action.config] ); const handleOnChangeSecretConfig = useCallback( (key: string, value: string) => editActionSecrets(key, value), + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); @@ -87,6 +91,7 @@ export const withConnectorFlyout = ({ editActionSecrets(key, ''); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [action.secrets] ); @@ -96,6 +101,7 @@ export const withConnectorFlyout = ({ ...action.config.casesConfiguration, mapping: newMapping, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [action.config] ); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts index afd57cade5559..bc16e8334e81c 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts @@ -89,10 +89,12 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => { return () => { didCancel = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [security]); useEffect(() => { fetchUser(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return user; }; diff --git a/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx b/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx index 9030e2713548b..072bbcf31c900 100644 --- a/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx @@ -32,6 +32,7 @@ export const SpyRouteComponent = memo( }); setIsInitializing(false); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [search]); useEffect(() => { if (pageName && !deepEqual(route.pathName, pathname)) { @@ -81,6 +82,7 @@ export const SpyRouteComponent = memo( }); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname, search, pageName, detailName, tabName, flowTarget, state]); return null; } diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx index 78f22a86c1893..37c6a6583f0fb 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx @@ -25,6 +25,7 @@ export const useShowTimeline = () => { } else if (!showTimeline) { setShowTimeline(true); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentLocation.pathname]); return [showTimeline]; diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/overview/index.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/overview/index.tsx index 937e3727ca613..86c8e00c0a56f 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/overview/index.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/overview/index.tsx @@ -90,6 +90,7 @@ const AlertDetailsOverviewComponent = memo(() => { history.push(locationObject); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [alertDetailsTabId] ); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.ts b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.ts index 55a3a4b060dad..a4f8fca23e8aa 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.ts @@ -79,6 +79,7 @@ export function useFirstLastSeenHostQuery( const signal = abortCtrl.signal; fetchFirstLastSeenHost(signal); return () => abortCtrl.abort(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { firstSeen, lastSeen, loading, errorMessage }; diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index 5b140a53a363b..4fbbcfc8d948a 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -35,6 +35,7 @@ export const ManagementPageView = memo>((options) => href: getManagementUrl({ name: 'policyList' }), }, ]; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [tabName]); return ; }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index b05cdfb3be840..6c447df618791 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -137,6 +137,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { description: details.agent.version, }, ]; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ details.agent.version, details.endpoint.policy.id, diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx index 5e0aa76e1d139..39b253439aff0 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx @@ -149,6 +149,7 @@ export const EmbeddedMapComponent = ({ return () => { isSubscribed = false; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loadingKibanaIndexPatterns, kibanaIndexPatterns]); // queryExpression updated useEffect @@ -156,12 +157,14 @@ export const EmbeddedMapComponent = ({ if (embeddable != null) { embeddable.updateInput({ query }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]); useEffect(() => { if (embeddable != null) { embeddable.updateInput({ filters }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filters]); // DateRange updated useEffect @@ -173,6 +176,7 @@ export const EmbeddedMapComponent = ({ }; embeddable.updateInput({ timeRange }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [startDate, endDate]); return isError ? null : ( diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx index 3472cb629b9fe..d2b8322c8121e 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx @@ -20,10 +20,8 @@ import * as i18n from '../translations'; import { ITooltipProperty } from '../../../../../../maps/public'; export const MapToolTipComponent = ({ - addFilters, closeTooltip, features = [], - isLocked, getLayerName, loadFeatureProperties, loadFeatureGeometry, @@ -78,8 +76,10 @@ export const MapToolTipComponent = ({ }; fetchFeatureProps(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ featureIndex, + // eslint-disable-next-line react-hooks/exhaustive-deps features .map((f) => `${f.id}-${f.layerId}`) .sort() diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx index b9c01826e9e74..cb1af5513c846 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx @@ -31,6 +31,7 @@ export const CountryFlag = memo<{ if (displayCountryNameOnHover && isEmpty(countries.getNames('en'))) { countries.registerLocale(countryJson); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const flag = getFlag(countryCode); diff --git a/x-pack/plugins/security_solution/public/network/components/tls_table/index.tsx b/x-pack/plugins/security_solution/public/network/components/tls_table/index.tsx index 0fc4629c95470..d0e001466518d 100644 --- a/x-pack/plugins/security_solution/public/network/components/tls_table/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/tls_table/index.tsx @@ -108,6 +108,7 @@ const TlsTableComponent = React.memo( [sort, type, tableType, updateNetworkTable] ); + // eslint-disable-next-line react-hooks/exhaustive-deps const columns = useMemo(() => getTlsColumns(tlsTableId), [tlsTableId]); return ( diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/index.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/index.tsx index 51634a784d4da..af9d2b0ffefe3 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/index.tsx @@ -114,6 +114,7 @@ const UsersTableComponent = React.memo( [sort, type, updateNetworkTable] ); + // eslint-disable-next-line react-hooks/exhaustive-deps const columns = useMemo(() => getUsersColumns(flowTarget, usersTableId), [ flowTarget, usersTableId, diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 574260a819071..fe66ae2624c2a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -71,6 +71,7 @@ const AlertsByCategoryComponent: React.FC = ({ deleteQuery({ id: ID }); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const kibana = useKibana(); @@ -98,6 +99,7 @@ const AlertsByCategoryComponent: React.FC = ({ `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, legendPosition: Position.Right, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx index 75b157a282eeb..ab76219b3cc00 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx @@ -82,6 +82,7 @@ const StatefulRecentTimelinesComponent = React.memo( onlyUserFavorite: filterBy === 'favorites', timelineType: TimelineType.default, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterBy]); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx index f2ad45be93522..5010fd9c06eb7 100644 --- a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx @@ -61,6 +61,7 @@ const SignalsByCategoryComponent: React.FC = ({ const [min, max] = x; setAbsoluteRangeDatePicker({ id: setAbsoluteRangeDatePickerTarget, from: min, to: max }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [setAbsoluteRangeDatePicker] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category_columns.tsx index b0a8dc311228a..14c17b7262724 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category_columns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category_columns.tsx @@ -50,6 +50,7 @@ interface ToolTipProps { const ToolTip = React.memo( ({ categoryId, browserFields, onUpdateColumns, timelineId }) => { const { getManageTimelineById } = useManageTimeline(); + // eslint-disable-next-line react-hooks/exhaustive-deps const { isLoading } = useMemo(() => getManageTimelineById(timelineId) ?? { isLoading: false }, [ timelineId, ]); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_browser.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_browser.tsx index c255bd062bb4c..8f538e03835f8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_browser.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_browser.tsx @@ -180,6 +180,7 @@ const FieldsBrowserComponent: React.FC = ({ useEffect(() => { scrollViews(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedCategoryId, timelineId]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx index 11c44cce89956..a3e93ff3c90eb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx @@ -108,6 +108,7 @@ export const StatefulFieldsBrowserComponent = React.memo { onUpdateColumns(columns); // show the category columns in the timeline + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); /** Invoked when the field browser should be hidden */ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx index 33aca80b940fe..fbe3c475c9fe6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx @@ -50,7 +50,7 @@ const FlyoutPaneComponent: React.FC = ({ const dispatch = useDispatch(); const onResizeStop: ResizeCallback = useCallback( - (e, direction, ref, delta) => { + (_e, _direction, _ref, delta) => { const bodyClientWidthPixels = document.body.clientWidth; if (delta.width) { @@ -65,6 +65,7 @@ const FlyoutPaneComponent: React.FC = ({ ); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [dispatch] ); const resizableDefaultSize = useMemo( @@ -72,6 +73,7 @@ const FlyoutPaneComponent: React.FC = ({ width, height: '100%', }), + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); const resizableHandleComponent = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx index c82a8bd18e6f8..8856805e6b660 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx @@ -190,6 +190,7 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT initializeTimeline({ id }); return { ...timelineDefaults, id }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [state] ); const isManagedTimeline = useCallback((id: string): boolean => state[id] != null, [state]); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_actions.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_actions.tsx index 112e73a47ce7d..f88690ccd8aef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_actions.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_actions.tsx @@ -16,6 +16,7 @@ export const useEditTimelineActions = () => { const onCloseDeleteTimelineModal = useCallback(() => { setIsDeleteTimelineModalOpen(false); setActionTimeline(null); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [actionItem]); const onOpenDeleteTimelineModal = useCallback((selectedActionItem?: OpenTimelineResult) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx index 7fb03b98432d0..4d45b74e9b1b4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx @@ -47,6 +47,7 @@ export const useEditTimelineBatchActions = ({ disableExportTimelineDownloader(); onCloseDeleteTimelineModal(); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [disableExportTimelineDownloader, onCloseDeleteTimelineModal, tableRef.current] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx index f9c9d28ad89e1..92a8fc9338877 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx @@ -125,6 +125,7 @@ export const StatefulOpenTimelineComponent = React.memo( onlyUserFavorite: onlyFavorites, timelineType, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [pageIndex, pageSize, search, sortField, sortDirection, timelineType, onlyFavorites]); /** Invoked when the user presses enters to submit the text in the search input */ @@ -177,6 +178,7 @@ export const StatefulOpenTimelineComponent = React.memo( const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback( async (timelineIds: string[]) => { + // The type for `deleteTimelines` is incorrect, it returns a Promise await deleteTimelines(timelineIds); }, [deleteTimelines] @@ -184,6 +186,7 @@ export const StatefulOpenTimelineComponent = React.memo( /** Invoked when the user clicks the action to delete the selected timelines */ const onDeleteSelected: OnDeleteSelected = useCallback(async () => { + // The type for `deleteTimelines` is incorrect, it returns a Promise await deleteTimelines(getSelectedTimelineIds(selectedItems)); // NOTE: we clear the selection state below, but if the server fails to @@ -191,6 +194,7 @@ export const StatefulOpenTimelineComponent = React.memo( resetSelectionState(); // TODO: the query must re-execute to show the results of the deletion + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedItems, deleteTimelines]); /** Invoked when the user selects (or de-selects) timelines */ @@ -244,6 +248,7 @@ export const StatefulOpenTimelineComponent = React.memo( updateTimeline, }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [apolloClient, updateIsLoading, updateTimeline] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx index f99d8c566c4a5..afc66e337d7b2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx @@ -79,6 +79,7 @@ export const useTimelineTypes = (): { ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [tabName]); const timelineFilters = useMemo(() => { @@ -95,6 +96,7 @@ export const useTimelineTypes = (): { ))} ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [timelineType]); return { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index 2d9f907cc7179..2bb78c0dcb0ad 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -108,7 +108,7 @@ export const ColumnHeadersComponent = ({ ); const renderClone: DraggableChildrenFn = useCallback( - (dragProvided, dragSnapshot, rubric) => { + (dragProvided, _dragSnapshot, rubric) => { // TODO: Remove after github.com/DefinitelyTyped/DefinitelyTyped/pull/43057 is merged // eslint-disable-next-line @typescript-eslint/no-explicit-any const index = (rubric as any).source.index; @@ -151,6 +151,7 @@ export const ColumnHeadersComponent = ({ sort={sort} /> )), + // eslint-disable-next-line react-hooks/exhaustive-deps [ columnHeaders, timelineId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx index 3808a10e851e6..a9ade7b34344e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx @@ -106,6 +106,7 @@ export const EventColumnView = React.memo( const onClickCb = useCallback((cb: () => void) => { cb(); closePopover(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const additionalActions = useMemo(() => { @@ -175,6 +176,7 @@ export const EventColumnView = React.memo( , ] : grouped.icon; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [button, ecsData, timelineActions, isPopoverOpen]); // , isPopoverOpen, closePopover, onButtonClick]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index 61c5809518928..cf76cd3ddb8d4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -174,6 +174,7 @@ const StatefulEventComponent: React.FC = ({ return () => { _isMounted = false; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Number of current columns plus one for actions. diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/stateful_body.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/stateful_body.tsx index 351465dd45137..2d5e64fb09ffc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/stateful_body.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/stateful_body.tsx @@ -94,6 +94,7 @@ const StatefulBodyComponent = React.memo( const onAddNoteToEvent: AddNoteToEvent = useCallback( ({ eventId, noteId }: { eventId: string; noteId: string }) => addNoteToEvent!({ id, eventId, noteId }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -131,27 +132,34 @@ const StatefulBodyComponent = React.memo( (sorted) => { updateSort!({ id, sort: sorted }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); const onColumnRemoved: OnColumnRemoved = useCallback( (columnId) => removeColumn!({ id, columnId }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); const onColumnResized: OnColumnResized = useCallback( ({ columnId, delta }) => applyDeltaToColumnWidth!({ id, columnId, delta }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); + // eslint-disable-next-line react-hooks/exhaustive-deps const onPinEvent: OnPinEvent = useCallback((eventId) => pinEvent!({ id, eventId }), [id]); + // eslint-disable-next-line react-hooks/exhaustive-deps const onUnPinEvent: OnUnPinEvent = useCallback((eventId) => unPinEvent!({ id, eventId }), [id]); + // eslint-disable-next-line react-hooks/exhaustive-deps const onUpdateNote: UpdateNote = useCallback((note: Note) => updateNote!({ note }), []); const onUpdateColumns: OnUpdateColumns = useCallback( (columns) => updateColumns!({ id, columns }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -160,6 +168,7 @@ const StatefulBodyComponent = React.memo( if (selectAll) { onSelectAll({ isSelected: true }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectAll]); // onSelectAll dependency not necessary return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx index aaf73487c4878..1f6fe998a44e9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx @@ -71,11 +71,13 @@ export const ProviderItemBadge = React.memo( const onToggleEnabledProvider = useCallback(() => { toggleEnabledProvider(); closePopover(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [toggleEnabledProvider]); const onToggleExcludedProvider = useCallback(() => { toggleExcludedProvider(); closePopover(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [toggleExcludedProvider]); const [providerRegistered, setProviderRegistered] = useState(false); @@ -100,6 +102,7 @@ export const ProviderItemBadge = React.memo( () => () => { unRegisterProvider(); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/fetch_kql_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/fetch_kql_timeline.tsx index 77bd9aeba3ed2..cec889fe6ee34 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/fetch_kql_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/fetch_kql_timeline.tsx @@ -39,6 +39,7 @@ const TimelineKqlFetchComponent = memo( timelineId: id, }), }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [kueryFilterQueryDraft, kueryFilterQuery, id]); return null; }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx index 607e004a8294e..594bf6d43e285 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx @@ -266,6 +266,7 @@ export const FooterComponent = ({ if (updatedAt === null || !isLoading) { setUpdatedAt(getUpdatedAt()); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading]); if (isLoading && !paginationLoading) { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 42fd6422d3a38..b53a34041a689 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -78,6 +78,7 @@ const StatefulTimelineComponent = React.memo( const onDataProviderRemoved: OnDataProviderRemoved = useCallback( (providerId: string, andProviderId?: string) => removeProvider!({ id, providerId, andProviderId }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -89,6 +90,7 @@ const StatefulTimelineComponent = React.memo( providerId, andProviderId, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -100,6 +102,7 @@ const StatefulTimelineComponent = React.memo( providerId, andProviderId, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -114,11 +117,13 @@ const StatefulTimelineComponent = React.memo( providerId, value, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback( (itemsChangedPerPage) => updateItemsPerPage!({ id, itemsPerPage: itemsChangedPerPage }), + // eslint-disable-next-line react-hooks/exhaustive-deps [id] ); @@ -141,6 +146,7 @@ const StatefulTimelineComponent = React.memo( }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [columns, id] ); @@ -148,6 +154,7 @@ const StatefulTimelineComponent = React.memo( if (createTimeline != null) { createTimeline({ id, columns: defaultHeaders, show: false }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/index.tsx index 2a673427d906c..ed4d742bb8b4d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/index.tsx @@ -52,6 +52,7 @@ export const InsertTimelinePopoverComponent: React.FC = ({ ); setRouterState(null); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [routerState]); const handleClosePopover = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx index 1a81c131de015..0f9e64082a603 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx @@ -28,12 +28,14 @@ export const useInsertTimeline = (form: FormHook, fieldNa ].join(''); form.setFieldValue(fieldName, newValue); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [form] ); const handleCursorChange = useCallback( (cp: CursorPosition) => { setCursorPosition(cp); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [cursorPosition] ); return { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx index 3ef10d394bc7b..38a85a7a92631 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx @@ -142,6 +142,7 @@ export const NewCase = React.memo( }, }, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [onClosePopover, history, timelineId, timelineTitle]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx index d8966a58748ed..1532a64e4083e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx @@ -87,6 +87,7 @@ export const Properties = React.memo( const onOpenTimelineModal = useCallback(() => { onClosePopover(); setShowTimelineModal(true); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const datePickerWidth = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index 9852fc7dd300a..80729eda051b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -120,6 +120,7 @@ export const QueryBarTimeline = memo( isSubscribed = false; subscriptions.unsubscribe(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -129,6 +130,7 @@ export const QueryBarTimeline = memo( if (!deepEqual(filters, filterWithoutDropArea)) { filterManager.setFilters(filters); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filters]); useEffect(() => { @@ -188,6 +190,7 @@ export const QueryBarTimeline = memo( return () => { isSubscribed = false; }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [savedQueryId]); const onChangedQuery = useCallback( @@ -203,6 +206,7 @@ export const QueryBarTimeline = memo( ); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [filterQueryDraft] ); @@ -232,6 +236,7 @@ export const QueryBarTimeline = memo( }); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [filterQuery, timelineId] ); @@ -274,6 +279,7 @@ export const QueryBarTimeline = memo( setSavedQueryId(null); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [dataProvidersDsl, savedQueryId, savedQueryServices] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx index 22fbaadf2e816..5f35bc5212d37 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx @@ -78,6 +78,7 @@ const StatefulSearchOrFilterComponent = React.memo( serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern), }, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [indexPattern, timelineId] ); @@ -90,6 +91,7 @@ const StatefulSearchOrFilterComponent = React.memo( expression, }, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [timelineId] ); @@ -99,6 +101,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, filters: newFilters, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [timelineId] ); @@ -108,6 +111,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, savedQueryId: newSavedQueryId, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [timelineId] ); @@ -117,6 +121,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, eventType: newEventType, }), + // eslint-disable-next-line react-hooks/exhaustive-deps [timelineId] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx index 2d215267d8382..2e12ebad2f99d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx @@ -258,6 +258,7 @@ const SelectableTimelineComponent: React.FC = ({ onlyUserFavorite: onlyFavorites, timelineType, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [onlyFavorites, pageSize, searchTimelineValue, timelineType]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx index 340818efa5514..884d693ca6ade 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx @@ -171,12 +171,15 @@ export const TimelineComponent: React.FC = ({ } = useManageTimeline(); useEffect(() => { initializeTimeline({ id, indexToAdd }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { setIsTimelineLoading({ id, isLoading: isQueryLoading || loadingIndexName }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [loadingIndexName, isQueryLoading]); useEffect(() => { setTimelineFilterManager({ id, filterManager }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterManager]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx index 3bca7ce5f1c08..19112221cbfd0 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx @@ -48,7 +48,7 @@ export interface AllTimelinesVariables { export const ALL_TIMELINE_QUERY_ID = 'FETCH_ALL_TIMELINES'; export const getAllTimeline = memoizeOne( - (variables: string, timelines: TimelineResult[]): OpenTimelineResult[] => + (_variables: string, timelines: TimelineResult[]): OpenTimelineResult[] => timelines.map((timeline) => ({ created: timeline.created, description: timeline.description, @@ -168,6 +168,7 @@ export const useGetAllTimeline = (): AllTimelinesArgs => { abortCtrl.abort(); }; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [apolloClient, allTimelines] ); From e2f11e9fe93bdf1e5d0ef63f8f4e3ae5fd4ff617 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 8 Jun 2020 11:19:01 -0400 Subject: [PATCH 22/27] [ML] DFAnalytics results: ensure ml result fields are shown in data grid (#68305) * wip: ensure top classes and influencer result col show up correctly * ensure ml subFields columns are populated --- .../components/data_grid/common.ts | 6 ++++- .../data_frame_analytics/common/analytics.ts | 10 +++++++++ .../data_frame_analytics/common/constants.ts | 1 + .../data_frame_analytics/common/fields.ts | 22 ++++++++++++++++--- .../use_exploration_results.ts | 11 +++++++--- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 44a2473f75937..7d0559c215114 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -29,6 +29,7 @@ import { FEATURE_IMPORTANCE, FEATURE_INFLUENCE, OUTLIER_SCORE, + TOP_CLASSES, } from '../../data_frame_analytics/common/constants'; import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils'; import { getNestedProperty } from '../../util/object_utils'; @@ -110,7 +111,10 @@ export const getDataGridSchemasFromFieldTypes = (fieldTypes: FieldTypes, results schema = 'numeric'; } - if (field.includes(`${resultsField}.${FEATURE_IMPORTANCE}`)) { + if ( + field.includes(`${resultsField}.${FEATURE_IMPORTANCE}`) || + field.includes(`${resultsField}.${TOP_CLASSES}`) + ) { schema = 'json'; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 0b4e6d27b96e5..16d888a9da27b 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -228,6 +228,16 @@ export const getPredictionFieldName = ( return predictionFieldName; }; +export const getNumTopClasses = ( + analysis: AnalysisConfig +): ClassificationAnalysis['classification']['num_top_classes'] => { + let numTopClasses; + if (isClassificationAnalysis(analysis) && analysis.classification.num_top_classes !== undefined) { + numTopClasses = analysis.classification.num_top_classes; + } + return numTopClasses; +}; + export const getNumTopFeatureImportanceValues = ( analysis: AnalysisConfig ): diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/constants.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/constants.ts index 51b2918012c8d..2f14dfdfdfca3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/constants.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/constants.ts @@ -7,4 +7,5 @@ export const DEFAULT_RESULTS_FIELD = 'ml'; export const FEATURE_IMPORTANCE = 'feature_importance'; export const FEATURE_INFLUENCE = 'feature_influence'; +export const TOP_CLASSES = 'top_classes'; export const OUTLIER_SCORE = 'outlier_score'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts index 8db349b395cfc..0a64886c80a63 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts @@ -5,6 +5,7 @@ */ import { + getNumTopClasses, getNumTopFeatureImportanceValues, getPredictedFieldName, getDependentVar, @@ -18,7 +19,7 @@ import { Field } from '../../../../common/types/fields'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; import { newJobCapsService } from '../../services/new_job_capabilities_service'; -import { FEATURE_IMPORTANCE, FEATURE_INFLUENCE, OUTLIER_SCORE } from './constants'; +import { FEATURE_IMPORTANCE, FEATURE_INFLUENCE, OUTLIER_SCORE, TOP_CLASSES } from './constants'; export type EsId = string; export type EsDocSource = Record; @@ -177,6 +178,7 @@ export const getDefaultFieldsFromJobCaps = ( const featureImportanceFields = []; const featureInfluenceFields = []; + const topClassesFields = []; const allFields: any = []; let type: ES_FIELD_TYPES | undefined; let predictedField: string | undefined; @@ -207,13 +209,14 @@ export const getDefaultFieldsFromJobCaps = ( type = newJobCapsService.getFieldById(dependentVariable)?.type; const predictionFieldName = getPredictionFieldName(jobConfig.analysis); const numTopFeatureImportanceValues = getNumTopFeatureImportanceValues(jobConfig.analysis); + const numTopClasses = getNumTopClasses(jobConfig.analysis); const defaultPredictionField = `${dependentVariable}_prediction`; predictedField = `${resultsField}.${ predictionFieldName ? predictionFieldName : defaultPredictionField }`; - if ((numTopFeatureImportanceValues ?? 0) > 0 && needsDestIndexFields === true) { + if ((numTopFeatureImportanceValues ?? 0) > 0) { featureImportanceFields.push({ id: `${resultsField}.${FEATURE_IMPORTANCE}`, name: `${resultsField}.${FEATURE_IMPORTANCE}`, @@ -221,6 +224,14 @@ export const getDefaultFieldsFromJobCaps = ( }); } + if ((numTopClasses ?? 0) > 0) { + topClassesFields.push({ + id: `${resultsField}.${TOP_CLASSES}`, + name: `${resultsField}.${TOP_CLASSES}`, + type: KBN_FIELD_TYPES.UNKNOWN, + }); + } + // Only need to add these fields if we didn't use dest index pattern to get the fields if (needsDestIndexFields === true) { allFields.push( @@ -234,7 +245,12 @@ export const getDefaultFieldsFromJobCaps = ( } } - allFields.push(...fields, ...featureImportanceFields, ...featureInfluenceFields); + allFields.push( + ...fields, + ...featureImportanceFields, + ...featureInfluenceFields, + ...topClassesFields + ); allFields.sort(({ name: a }: { name: string }, { name: b }: { name: string }) => sortExplorationResultsFields(a, b, jobConfig) ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts index e391b90e6eb96..b8b5a16c84e85 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts @@ -19,7 +19,11 @@ import { import { SavedSearchQuery } from '../../../../../contexts/ml'; import { getIndexData, getIndexFields, DataFrameAnalyticsConfig } from '../../../../common'; -import { DEFAULT_RESULTS_FIELD, FEATURE_IMPORTANCE } from '../../../../common/constants'; +import { + DEFAULT_RESULTS_FIELD, + FEATURE_IMPORTANCE, + TOP_CLASSES, +} from '../../../../common/constants'; import { sortExplorationResultsFields, ML__ID_COPY } from '../../../../common/fields'; export const useExplorationResults = ( @@ -47,8 +51,9 @@ export const useExplorationResults = ( 25, // reduce default selected rows from 20 to 8 for performance reasons. 8, - // by default, hide feature-importance columns and the doc id copy - (d) => !d.includes(`.${FEATURE_IMPORTANCE}.`) && d !== ML__ID_COPY + // by default, hide feature-importance and top-classes columns and the doc id copy + (d) => + !d.includes(`.${FEATURE_IMPORTANCE}.`) && !d.includes(`.${TOP_CLASSES}.`) && d !== ML__ID_COPY ); useEffect(() => { From 0189ae5c3ff7d3d49a8f182138de743ad748eb78 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 8 Jun 2020 11:51:04 -0400 Subject: [PATCH 23/27] [Maps] Enable gridding/clustering/heatmaps for geo_shape fields (#67886) Enables heatmap, clusters, and grid layers for index-patterns with geo_shape field. This feature is only available for Gold+ users. --- .../create_source_editor.js | 4 +- .../es_geo_grid_source/es_geo_grid_source.js | 7 ++ .../es_pew_pew_source/create_source_editor.js | 5 +- .../maps/public/elasticsearch_geo_utils.js | 45 +++++---- .../public/elasticsearch_geo_utils.test.js | 93 +++++++++++++++++++ .../plugins/maps/public/index_pattern_util.js | 13 ++- .../maps/public/index_pattern_util.test.js | 81 +++++++++++++++- .../plugins/maps/public/kibana_services.d.ts | 2 + x-pack/plugins/maps/public/kibana_services.js | 9 ++ x-pack/plugins/maps/public/plugin.ts | 25 +++-- .../apps/maps/es_geo_grid_source.js | 33 +++++++ .../es_archives/maps/kibana/data.json | 33 ++++++- 12 files changed, 308 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js index 7661ad7020194..91dcb057dd837 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js @@ -14,7 +14,7 @@ import { NoIndexPatternCallout } from '../../../components/no_index_pattern_call import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { AGGREGATABLE_GEO_FIELD_TYPES, getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; +import { getAggregatableGeoFieldTypes, getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; import { RenderAsSelect } from './render_as_select'; export class CreateSourceEditor extends Component { @@ -176,7 +176,7 @@ export class CreateSourceEditor extends Component { placeholder={i18n.translate('xpack.maps.source.esGeoGrid.indexPatternPlaceholder', { defaultMessage: 'Select index pattern', })} - fieldTypes={AGGREGATABLE_GEO_FIELD_TYPES} + fieldTypes={getAggregatableGeoFieldTypes()} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js index e77fd93872612..c05c1f2dd7c1e 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js @@ -21,6 +21,7 @@ import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { AbstractESAggSource } from '../es_agg_source'; import { DataRequestAbortError } from '../../util/data_request'; import { registerSource } from '../source_registry'; +import { makeESBbox } from '../../../elasticsearch_geo_utils'; export const MAX_GEOTILE_LEVEL = 29; @@ -146,6 +147,7 @@ export class ESGeoGridSource extends AbstractESAggSource { registerCancelCallback, bucketsPerGrid, isRequestStillActive, + bufferedExtent, }) { const gridsPerRequest = Math.floor(DEFAULT_MAX_BUCKETS_LIMIT / bucketsPerGrid); const aggs = { @@ -156,6 +158,7 @@ export class ESGeoGridSource extends AbstractESAggSource { { gridSplit: { geotile_grid: { + bounds: makeESBbox(bufferedExtent), field: this._descriptor.geoField, precision, }, @@ -234,10 +237,12 @@ export class ESGeoGridSource extends AbstractESAggSource { precision, layerName, registerCancelCallback, + bufferedExtent, }) { searchSource.setField('aggs', { gridSplit: { geotile_grid: { + bounds: makeESBbox(bufferedExtent), field: this._descriptor.geoField, precision, }, @@ -282,6 +287,7 @@ export class ESGeoGridSource extends AbstractESAggSource { precision: searchFilters.geogridPrecision, layerName, registerCancelCallback, + bufferedExtent: searchFilters.buffer, }) : await this._compositeAggRequest({ searchSource, @@ -291,6 +297,7 @@ export class ESGeoGridSource extends AbstractESAggSource { registerCancelCallback, bucketsPerGrid, isRequestStillActive, + bufferedExtent: searchFilters.buffer, }); return { diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js index f599ba93d40d6..38a5850537200 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js @@ -14,7 +14,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFormRow, EuiCallOut } from '@elastic/eui'; -import { AGGREGATABLE_GEO_FIELD_TYPES, getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; +import { getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; +import { ES_GEO_FIELD_TYPE } from '../../../../common/constants'; export class CreateSourceEditor extends Component { static propTypes = { @@ -177,7 +178,7 @@ export class CreateSourceEditor extends Component { placeholder={i18n.translate('xpack.maps.source.pewPew.indexPatternPlaceholder', { defaultMessage: 'Select index pattern', })} - fieldTypes={AGGREGATABLE_GEO_FIELD_TYPES} + fieldTypes={[ES_GEO_FIELD_TYPE.GEO_POINT]} /> ); diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js index b89fda29554ee..efd243595db3e 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js @@ -225,39 +225,36 @@ export function geoShapeToGeometry(value, accumulator) { accumulator.push(geoJson); } -function createGeoBoundBoxFilter({ maxLat, maxLon, minLat, minLon }, geoFieldName) { - const top = clampToLatBounds(maxLat); +export function makeESBbox({ maxLat, maxLon, minLat, minLon }) { const bottom = clampToLatBounds(minLat); - - // geo_bounding_box does not support ranges outside of -180 and 180 - // When the area crosses the 180° meridian, - // the value of the lower left longitude will be greater than the value of the upper right longitude. - // http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#30 - let boundingBox; + const top = clampToLatBounds(maxLat); + let esBbox; if (maxLon - minLon >= 360) { - boundingBox = { + esBbox = { top_left: [-180, top], bottom_right: [180, bottom], }; - } else if (maxLon > 180) { - const overflow = maxLon - 180; - boundingBox = { - top_left: [minLon, top], - bottom_right: [-180 + overflow, bottom], - }; - } else if (minLon < -180) { - const overflow = Math.abs(minLon) - 180; - boundingBox = { - top_left: [180 - overflow, top], - bottom_right: [maxLon, bottom], - }; } else { - boundingBox = { - top_left: [minLon, top], - bottom_right: [maxLon, bottom], + // geo_bounding_box does not support ranges outside of -180 and 180 + // When the area crosses the 180° meridian, + // the value of the lower left longitude will be greater than the value of the upper right longitude. + // http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#30 + // + // This ensures bbox goes West->East in the happy case, + // but will be formatted East->West in case it crosses the date-line + const newMinlon = ((minLon + 180 + 360) % 360) - 180; + const newMaxlon = ((maxLon + 180 + 360) % 360) - 180; + esBbox = { + top_left: [newMinlon, top], + bottom_right: [newMaxlon, bottom], }; } + return esBbox; +} + +function createGeoBoundBoxFilter({ maxLat, maxLon, minLat, minLon }, geoFieldName) { + const boundingBox = makeESBbox({ maxLat, maxLon, minLat, minLon }); return { geo_bounding_box: { [geoFieldName]: boundingBox, diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js index 05f6a9eec5bd5..a1e4e43f3ab75 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js @@ -19,6 +19,7 @@ import { createExtentFilter, roundCoordinates, extractFeaturesFromFilters, + makeESBbox, } from './elasticsearch_geo_utils'; import { indexPatterns } from '../../../../src/plugins/data/public'; @@ -594,3 +595,95 @@ describe('extractFeaturesFromFilters', () => { expect(extractFeaturesFromFilters([spatialFilter])).toEqual([]); }); }); + +describe('makeESBbox', () => { + it('Should invert Y-axis', () => { + const bbox = makeESBbox({ + minLon: 10, + maxLon: 20, + minLat: 0, + maxLat: 1, + }); + expect(bbox).toEqual({ bottom_right: [20, 0], top_left: [10, 1] }); + }); + + it('Should snap to 360 width', () => { + const bbox = makeESBbox({ + minLon: 10, + maxLon: 400, + minLat: 0, + maxLat: 1, + }); + expect(bbox).toEqual({ bottom_right: [180, 0], top_left: [-180, 1] }); + }); + + it('Should clamp latitudes', () => { + const bbox = makeESBbox({ + minLon: 10, + maxLon: 400, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [180, -89], top_left: [-180, 89] }); + }); + + it('Should swap West->East orientation to East->West orientation when crossing dateline (West extension)', () => { + const bbox = makeESBbox({ + minLon: -190, + maxLon: 20, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [20, -89], top_left: [170, 89] }); + }); + + it('Should swap West->East orientation to East->West orientation when crossing dateline (West extension) (overrated)', () => { + const bbox = makeESBbox({ + minLon: -190 + 360 + 360, + maxLon: 20 + 360 + 360, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [20, -89], top_left: [170, 89] }); + }); + + it('Should swap West->East orientation to East->West orientation when crossing dateline (east extension)', () => { + const bbox = makeESBbox({ + minLon: 175, + maxLon: 190, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [-170, -89], top_left: [175, 89] }); + }); + + it('Should preserve West->East orientation when _not_ crossing dateline', () => { + const bbox = makeESBbox({ + minLon: 20, + maxLon: 170, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [170, -89], top_left: [20, 89] }); + }); + + it('Should preserve West->East orientation when _not_ crossing dateline _and_ snap longitudes (west extension)', () => { + const bbox = makeESBbox({ + minLon: -190, + maxLon: -185, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [175, -89], top_left: [170, 89] }); + }); + + it('Should preserve West->East orientation when _not_ crossing dateline _and_ snap longitudes (east extension)', () => { + const bbox = makeESBbox({ + minLon: 185, + maxLon: 190, + minLat: -100, + maxLat: 100, + }); + expect(bbox).toEqual({ bottom_right: [-170, -89], top_left: [-175, 89] }); + }); +}); diff --git a/x-pack/plugins/maps/public/index_pattern_util.js b/x-pack/plugins/maps/public/index_pattern_util.js index d695d1087a38d..514feeaa22072 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.js +++ b/x-pack/plugins/maps/public/index_pattern_util.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getIndexPatternService } from './kibana_services'; +import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; import { indexPatterns } from '../../../../src/plugins/data/public'; import { ES_GEO_FIELD_TYPE } from '../common/constants'; @@ -30,19 +30,24 @@ export function getTermsFields(fields) { }); } -export const AGGREGATABLE_GEO_FIELD_TYPES = [ES_GEO_FIELD_TYPE.GEO_POINT]; +export function getAggregatableGeoFieldTypes() { + const aggregatableFieldTypes = [ES_GEO_FIELD_TYPE.GEO_POINT]; + if (getIsGoldPlus()) { + aggregatableFieldTypes.push(ES_GEO_FIELD_TYPE.GEO_SHAPE); + } + return aggregatableFieldTypes; +} export function getFieldsWithGeoTileAgg(fields) { return fields.filter(supportsGeoTileAgg); } export function supportsGeoTileAgg(field) { - // TODO add geo_shape support with license check return ( field && field.aggregatable && !indexPatterns.isNestedField(field) && - field.type === ES_GEO_FIELD_TYPE.GEO_POINT + getAggregatableGeoFieldTypes().includes(field.type) ); } diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.js b/x-pack/plugins/maps/public/index_pattern_util.test.js index 7f8f1c175cf15..4fa9eb3cadb49 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.test.js +++ b/x-pack/plugins/maps/public/index_pattern_util.test.js @@ -6,7 +6,12 @@ jest.mock('./kibana_services', () => ({})); -import { getSourceFields } from './index_pattern_util'; +import { + getSourceFields, + getAggregatableGeoFieldTypes, + supportsGeoTileAgg, +} from './index_pattern_util'; +import { ES_GEO_FIELD_TYPE } from '../common/constants'; describe('getSourceFields', () => { test('Should remove multi fields from field list', () => { @@ -27,3 +32,77 @@ describe('getSourceFields', () => { expect(sourceFields).toEqual([{ name: 'agent' }]); }); }); + +describe('Gold+ licensing', () => { + const testStubs = [ + { + field: { + type: 'geo_point', + aggregatable: true, + }, + supportedInBasic: true, + supportedInGold: true, + }, + { + field: { + type: 'geo_shape', + aggregatable: false, + }, + supportedInBasic: false, + supportedInGold: false, + }, + { + field: { + type: 'geo_shape', + aggregatable: true, + }, + supportedInBasic: false, + supportedInGold: true, + }, + ]; + + describe('basic license', () => { + beforeEach(() => { + require('./kibana_services').getIsGoldPlus = () => false; + }); + + describe('getAggregatableGeoFieldTypes', () => { + test('Should only include geo_point fields ', () => { + const aggregatableGeoFieldTypes = getAggregatableGeoFieldTypes(); + expect(aggregatableGeoFieldTypes).toEqual([ES_GEO_FIELD_TYPE.GEO_POINT]); + }); + }); + + describe('supportsGeoTileAgg', () => { + testStubs.forEach((stub, index) => { + test(`stub: ${index}`, () => { + const supported = supportsGeoTileAgg(stub.field); + expect(supported).toEqual(stub.supportedInBasic); + }); + }); + }); + }); + + describe('gold license', () => { + beforeEach(() => { + require('./kibana_services').getIsGoldPlus = () => true; + }); + describe('getAggregatableGeoFieldTypes', () => { + test('Should add geo_shape field', () => { + const aggregatableGeoFieldTypes = getAggregatableGeoFieldTypes(); + expect(aggregatableGeoFieldTypes).toEqual([ + ES_GEO_FIELD_TYPE.GEO_POINT, + ES_GEO_FIELD_TYPE.GEO_SHAPE, + ]); + }); + }); + describe('supportsGeoTileAgg', () => { + testStubs.forEach((stub, index) => { + test(`stub: ${index}`, () => { + const supported = supportsGeoTileAgg(stub.field); + expect(supported).toEqual(stub.supportedInGold); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index 45a4ace8e27a5..d45d7286ebf68 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -48,6 +48,7 @@ export function getShowMapsInspectorAdapter(): boolean; export function getPreserveDrawingBuffer(): boolean; export function getEnableVectorTiles(): boolean; export function getProxyElasticMapsServiceInMaps(): boolean; +export function getIsGoldPlus(): boolean; export function setLicenseId(args: unknown): void; export function setInspector(args: unknown): void; @@ -74,3 +75,4 @@ export function setSearchService(args: DataPublicPluginStart['search']): void; export function setKibanaCommonConfig(config: MapsLegacyConfigType): void; export function setMapAppConfig(config: MapsConfigType): void; export function setKibanaVersion(version: string): void; +export function setIsGoldPlus(isGoldPlus: boolean): void; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index ba7be7a3b2464..1684acfb0f463 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -166,3 +166,12 @@ export const getProxyElasticMapsServiceInMaps = () => getKibanaCommonConfig().proxyElasticMapsServiceInMaps; export const getRegionmapLayers = () => _.get(getKibanaCommonConfig(), 'regionmap.layers', []); export const getTilemap = () => _.get(getKibanaCommonConfig(), 'tilemap', []); + +let isGoldPlus = false; +export const setIsGoldPlus = (igp) => { + isGoldPlus = igp; +}; + +export const getIsGoldPlus = () => { + return isGoldPlus; +}; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 3a20f6738a05a..319be46870ebc 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from 'src/core/public'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; import { Setup as InspectorSetupContract } from 'src/plugins/inspector/public'; // @ts-ignore import { MapView } from './inspector/views/map_view'; @@ -21,29 +21,31 @@ import { setIndexPatternSelect, setIndexPatternService, setInspector, + setIsGoldPlus, + setKibanaCommonConfig, + setKibanaVersion, setLicenseId, + setMapAppConfig, setMapsCapabilities, setNavigation, setSavedObjectsClient, + setSearchService, setTimeFilter, setToasts, setUiActions, setUiSettings, setVisualizations, - setSearchService, - setMapAppConfig, - setKibanaCommonConfig, - setKibanaVersion, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; -import { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +import { APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; -import { MapsXPackConfig, MapsConfigType } from '../config'; +import { MapsConfigType, MapsXPackConfig } from '../config'; +import { ILicense } from '../../licensing/common/types'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -76,7 +78,14 @@ export const bindSetupCoreAndPlugins = ( }; export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { - const { fileUpload, data, inspector } = plugins; + const { fileUpload, data, inspector, licensing } = plugins; + if (licensing) { + licensing.license$.subscribe((license: ILicense) => { + const gold = license.check(APP_ID, 'gold'); + setIsGoldPlus(gold.state === 'valid'); + }); + } + setInspector(inspector); setFileUpload(fileUpload); setIndexPatternSelect(data.ui.IndexPatternSelect); diff --git a/x-pack/test/functional/apps/maps/es_geo_grid_source.js b/x-pack/test/functional/apps/maps/es_geo_grid_source.js index 7346ab842ff59..f5a603742c89a 100644 --- a/x-pack/test/functional/apps/maps/es_geo_grid_source.js +++ b/x-pack/test/functional/apps/maps/es_geo_grid_source.js @@ -241,5 +241,38 @@ export default function ({ getPageObjects, getService }) { }); }); }); + + describe('vector grid with geo_shape', () => { + before(async () => { + await PageObjects.maps.loadSavedMap('geo grid vector grid example with shape'); + }); + + const LAYER_ID = 'g1xkv'; + it('should get expected number of grid cells', async () => { + const mapboxStyle = await PageObjects.maps.getMapboxStyle(); + expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(13); + }); + + describe('inspector', () => { + afterEach(async () => { + await inspector.close(); + }); + + it('should contain geotile_grid aggregation elasticsearch request', async () => { + await inspector.open(); + await inspector.openInspectorRequestsView(); + const requestStats = await inspector.getTableData(); + const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)'); + expect(totalHits).to.equal('4'); //4 geometries result in 13 cells due to way they overlap geotile_grid cells + const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits'); + expect(hits).to.equal('0'); // aggregation requests do not return any documents + const indexPatternName = PageObjects.maps.getInspectorStatRowHit( + requestStats, + 'Index pattern' + ); + expect(indexPatternName).to.equal('geo_shapes*'); + }); + }); + }); }); } diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index d313fd2046c03..c173d75075041 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -36,7 +36,7 @@ "index": ".kibana", "source": { "index-pattern": { - "fields" : "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"geometry\",\"type\":\"geo_shape\",\"esTypes\":[\"geo_shape\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"prop1\",\"type\":\"number\",\"esTypes\":[\"byte\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "fields" : "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"geometry\",\"type\":\"geo_shape\",\"esTypes\":[\"geo_shape\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"prop1\",\"type\":\"number\",\"esTypes\":[\"byte\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", "title": "geo_shapes*" }, "type": "index-pattern" @@ -553,6 +553,37 @@ } } +{ + "type": "doc", + "value": { + "id": "map:0c86d024-a767-11ea-bb37-0242ac130002", + "index": ".kibana", + "source": { + "map": { + "bounds": { + "coordinates": [ + [ + -160, + 60 + ], + [ + 160, + -60 + ] + ], + "type": "envelope" + }, + "description": "", + "layerListJSON": "[{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"64ddd934-a767-11ea-bb37-0242ac130002\",\"indexPatternId\":\"561253e0-f731-11e8-8487-11b9dd924f96\",\"geoField\":\"geometry\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "mapStateJSON": "{\"zoom\":3,\"center\":{\"lon\":76,\"lat\":4},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", + "title": "geo grid vector grid example with shape", + "uiStateJSON": "{\"isDarkMode\":false}" + }, + "type": "map" + } + } +} + { "type": "doc", "value": { From 16fbbf434550645d7386da34c00716f73274f224 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Mon, 8 Jun 2020 14:11:51 -0400 Subject: [PATCH 24/27] [Canvas] Tear down history instances (#68277) * [Canvas] Tear down history instances * Always create history on Canvas startup Co-authored-by: Poff Poffenberger Co-authored-by: Elastic Machine --- x-pack/plugins/canvas/public/application.tsx | 5 +++- .../canvas/public/lib/history_provider.js | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/canvas/public/application.tsx b/x-pack/plugins/canvas/public/application.tsx index 8751d8102ad37..c799f36a283c1 100644 --- a/x-pack/plugins/canvas/public/application.tsx +++ b/x-pack/plugins/canvas/public/application.tsx @@ -33,7 +33,7 @@ import { CapabilitiesStrings } from '../i18n'; import { startServices, services } from './services'; // @ts-ignore Untyped local -import { destroyHistory } from './lib/history_provider'; +import { createHistory, destroyHistory } from './lib/history_provider'; // @ts-ignore Untyped local import { stopRouter } from './lib/router_provider'; import { initFunctions } from './functions'; @@ -97,6 +97,9 @@ export const initializeCanvas = async ( services.expressions.getService().registerFunction(fn); } + // Re-initialize our history + createHistory(); + // Create Store const canvasStore = await createStore(coreSetup, setupPlugins); diff --git a/x-pack/plugins/canvas/public/lib/history_provider.js b/x-pack/plugins/canvas/public/lib/history_provider.js index 649f101126012..396372eeb85c1 100644 --- a/x-pack/plugins/canvas/public/lib/history_provider.js +++ b/x-pack/plugins/canvas/public/lib/history_provider.js @@ -134,7 +134,7 @@ function wrapHistoryInstance(history) { return wrappedHistory; } -let instances = new WeakMap(); +const instances = new WeakMap(); const getHistoryInstance = (win) => { // if no window object, use memory module @@ -144,6 +144,15 @@ const getHistoryInstance = (win) => { return createHashStateHistory(); }; +export const createHistory = (win = getWindow()) => { + // create and cache wrapped history instance + const historyInstance = getHistoryInstance(win); + const wrappedInstance = wrapHistoryInstance(historyInstance); + instances.set(win, wrappedInstance); + + return wrappedInstance; +}; + export const historyProvider = (win = getWindow()) => { // return cached instance if one exists const instance = instances.get(win); @@ -151,14 +160,13 @@ export const historyProvider = (win = getWindow()) => { return instance; } - // create and cache wrapped history instance - const historyInstance = getHistoryInstance(win); - const wrappedInstance = wrapHistoryInstance(historyInstance); - instances.set(win, wrappedInstance); - - return wrappedInstance; + return createHistory(win); }; -export const destroyHistory = () => { - instances = new WeakMap(); +export const destroyHistory = (win = getWindow()) => { + const instance = instances.get(win); + + if (instance) { + instance.resetOnChange(); + } }; From 8174b5ce29e7a4e24b3b78e0d1b89be17e2efd09 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 8 Jun 2020 11:22:09 -0700 Subject: [PATCH 25/27] [Search] Refactor service to register search strategies, not providers (#60342) * Add async search strategy * Add async search * Fix async strategy and add tests * Move types to separate file * Revert changes to demo search * Update demo search strategy to use async * Add async es search strategy * Return response as rawResponse * Poll after initial request * Add cancellation to search strategies * Add tests * Simplify async search strategy * Move loadingCount to search strategy * Update abort controller library * Bootstrap * Abort when the request is aborted * Add utility and update value suggestions route * Fix bad merge conflict * Update tests * Move to data_enhanced plugin * Remove bad merge * Revert switching abort controller libraries * Revert package.json in lib * Move to previous abort controller * Add support for frozen indices * Fix test to use fake timers to run debounced handlers * Revert changes to example plugin * Fix loading bar not going away when cancelling * Call getSearchStrategy instead of passing directly * Add async demo search strategy * Fix error with setting state * Update how aborting works * Fix type checks * Add test for loading count * Attempt to fix broken example test * Revert changes to test * Fix test * Update name to camelCase * Fix failing test * Don't require data_enhanced in example plugin * Actually send DELETE request * Use waitForCompletion parameter * Use default search params * Add support for rollups * Only make changes needed for frozen indices/rollups * Only make changes needed for frozen indices/rollups * Add back in async functionality * Fix tests/types * Fix issue with sending empty body in GET * Don't include skipped in loaded/total * Don't wait before polling the next time * Add search interceptor for bulk managing searches * Simplify search logic * Fix merge error * Review feedback * UI to stop async searches * Add service for running beyond timeout * Refactor abort utils * Remove unneeded changes * Add tests * Refactor search service to register strategies directly * Remove accidental change * re-generate docs * Fix merge * types * doc * eslint * Fix async strategy jest test * type fix * Use getStartServices in search strategies * Code review + snapshot * eslint * Type script Co-authored-by: Elastic Machine Co-authored-by: Liza K --- ...plugins-data-public.isearchcontext.core.md | 11 -- ...public.isearchcontext.getsearchstrategy.md | 11 -- ...ugin-plugins-data-public.isearchcontext.md | 19 ---- .../kibana-plugin-plugins-data-public.md | 2 - ...ins-data-public.tsearchstrategyprovider.md | 13 --- .../public/async_demo_search_strategy.ts | 68 ++++------- .../public/demo_search_strategy.ts | 43 +++---- examples/demo_search/public/plugin.ts | 23 ++-- examples/demo_search/public/types.ts | 28 +++++ src/plugins/data/public/index.ts | 2 - src/plugins/data/public/mocks.ts | 12 +- src/plugins/data/public/public.api.md | 43 +++---- .../es_search/es_search_strategy.test.ts | 25 ++-- .../search/es_search/es_search_strategy.ts | 35 +++--- src/plugins/data/public/search/index.ts | 8 +- src/plugins/data/public/search/mocks.ts | 10 +- .../data/public/search/search_service.test.ts | 2 +- .../data/public/search/search_service.ts | 47 ++++---- .../search/sync_search_strategy.test.ts | 33 ++---- .../public/search/sync_search_strategy.ts | 21 ++-- src/plugins/data/public/search/types.ts | 34 +++--- .../saved_objects_table.test.tsx.snap | 1 + x-pack/plugins/data_enhanced/public/plugin.ts | 19 ++-- .../search/async_search_strategy.test.ts | 89 ++++----------- .../public/search/async_search_strategy.ts | 107 +++++++++--------- .../public/search/es_search_strategy.test.ts | 35 ++++++ .../public/search/es_search_strategy.ts | 22 ++-- 27 files changed, 329 insertions(+), 434 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.core.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.tsearchstrategyprovider.md create mode 100644 examples/demo_search/public/types.ts create mode 100644 x-pack/plugins/data_enhanced/public/search/es_search_strategy.test.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.core.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.core.md deleted file mode 100644 index 7a7ea43bd3d40..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.core.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) > [core](./kibana-plugin-plugins-data-public.isearchcontext.core.md) - -## ISearchContext.core property - -Signature: - -```typescript -core: CoreStart; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md deleted file mode 100644 index 93ac88d200bb8..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) > [getSearchStrategy](./kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md) - -## ISearchContext.getSearchStrategy property - -Signature: - -```typescript -getSearchStrategy: (name: T) => TSearchStrategyProvider; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.md deleted file mode 100644 index 9b89f71434119..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchcontext.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) - -## ISearchContext interface - -Signature: - -```typescript -export interface ISearchContext -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [core](./kibana-plugin-plugins-data-public.isearchcontext.core.md) | CoreStart | | -| [getSearchStrategy](./kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md) | <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T> | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 75d3abefc74b9..e818fb009fb19 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -67,7 +67,6 @@ | [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | | | [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md) | | | [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.md) | | -| [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) | | | [ISearchStrategy](./kibana-plugin-plugins-data-public.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. | | [ISyncSearchRequest](./kibana-plugin-plugins-data-public.isyncsearchrequest.md) | | @@ -157,5 +156,4 @@ | [TabbedAggRow](./kibana-plugin-plugins-data-public.tabbedaggrow.md) | \* | | [TimefilterContract](./kibana-plugin-plugins-data-public.timefiltercontract.md) | | | [TimeHistoryContract](./kibana-plugin-plugins-data-public.timehistorycontract.md) | | -| [TSearchStrategyProvider](./kibana-plugin-plugins-data-public.tsearchstrategyprovider.md) | Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.tsearchstrategyprovider.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.tsearchstrategyprovider.md deleted file mode 100644 index 3233bb48cea2c..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.tsearchstrategyprovider.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TSearchStrategyProvider](./kibana-plugin-plugins-data-public.tsearchstrategyprovider.md) - -## TSearchStrategyProvider type - -Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. - -Signature: - -```typescript -export declare type TSearchStrategyProvider = (context: ISearchContext) => ISearchStrategy; -``` diff --git a/examples/demo_search/public/async_demo_search_strategy.ts b/examples/demo_search/public/async_demo_search_strategy.ts index 7a3f33ce05a75..862324002840c 100644 --- a/examples/demo_search/public/async_demo_search_strategy.ts +++ b/examples/demo_search/public/async_demo_search_strategy.ts @@ -17,53 +17,27 @@ * under the License. */ -import { Observable } from 'rxjs'; -import { - ISearchContext, - TSearchStrategyProvider, - ISearchStrategy, -} from '../../../src/plugins/data/public'; - -import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoResponse } from '../common'; +import { Observable, from } from 'rxjs'; +import { CoreSetup } from 'kibana/public'; +import { flatMap } from 'rxjs/operators'; +import { ISearch } from '../../../src/plugins/data/public'; import { ASYNC_SEARCH_STRATEGY } from '../../../x-pack/plugins/data_enhanced/public'; +import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoResponse } from '../common'; +import { DemoDataSearchStartDependencies } from './types'; -/** - * This demo search strategy provider simply provides a shortcut for calling the DEMO_ASYNC_SEARCH_STRATEGY - * on the server side, without users having to pass it in explicitly, and it takes advantage of the - * already registered ASYNC_SEARCH_STRATEGY that exists on the client. - * - * so instead of callers having to do: - * - * ``` - * search( - * { ...request, serverStrategy: DEMO_ASYNC_SEARCH_STRATEGY }, - * options, - * ASYNC_SEARCH_STRATEGY - * ) as Observable, - *``` - - * They can instead just do - * - * ``` - * search(request, options, DEMO_ASYNC_SEARCH_STRATEGY); - * ``` - * - * and are ensured type safety in regard to the request and response objects. - * - * @param context - context supplied by other plugins. - * @param search - a search function to access other strategies that have already been registered. - */ -export const asyncDemoClientSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -): ISearchStrategy => { - const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY); - const { search } = asyncStrategyProvider(context); - return { - search: (request, options) => { - return search( - { ...request, serverStrategy: ASYNC_DEMO_SEARCH_STRATEGY }, - options - ) as Observable; - }, +export function asyncDemoClientSearchStrategyProvider(core: CoreSetup) { + const search: ISearch = (request, options) => { + return from(core.getStartServices()).pipe( + flatMap((startServices) => { + const asyncStrategy = (startServices[1] as DemoDataSearchStartDependencies).data.search.getSearchStrategy( + ASYNC_SEARCH_STRATEGY + ); + return asyncStrategy.search( + { ...request, serverStrategy: ASYNC_DEMO_SEARCH_STRATEGY }, + options + ) as Observable; + }) + ); }; -}; + return { search }; +} diff --git a/examples/demo_search/public/demo_search_strategy.ts b/examples/demo_search/public/demo_search_strategy.ts index 8dc2779a8544c..d56d827a5c0f8 100644 --- a/examples/demo_search/public/demo_search_strategy.ts +++ b/examples/demo_search/public/demo_search_strategy.ts @@ -17,11 +17,12 @@ * under the License. */ -import { Observable } from 'rxjs'; -import { ISearchContext, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public'; -import { TSearchStrategyProvider, ISearchStrategy } from '../../../src/plugins/data/public'; - +import { Observable, from } from 'rxjs'; +import { flatMap } from 'rxjs/operators'; +import { CoreSetup } from 'kibana/public'; +import { ISearch, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public'; import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; +import { DemoDataSearchStartDependencies } from './types'; /** * This demo search strategy provider simply provides a shortcut for calling the DEMO_SEARCH_STRATEGY @@ -31,7 +32,7 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; * so instead of callers having to do: * * ``` - * context.search( + * data.search.search( * { ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, * options, * SYNC_SEARCH_STRATEGY @@ -41,24 +42,24 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; * They can instead just do * * ``` - * context.search(request, options, DEMO_SEARCH_STRATEGY); + * data.search.search(request, options, DEMO_SEARCH_STRATEGY); * ``` * * and are ensured type safety in regard to the request and response objects. - * - * @param context - context supplied by other plugins. - * @param search - a search function to access other strategies that have already been registered. */ -export const demoClientSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -): ISearchStrategy => { - const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); - const { search } = syncStrategyProvider(context); - return { - search: (request, options) => { - return search({ ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, options) as Observable< - IDemoResponse - >; - }, +export function demoClientSearchStrategyProvider(core: CoreSetup) { + const search: ISearch = (request, options) => { + return from(core.getStartServices()).pipe( + flatMap((startServices) => { + const syncStrategy = (startServices[1] as DemoDataSearchStartDependencies).data.search.getSearchStrategy( + SYNC_SEARCH_STRATEGY + ); + return syncStrategy.search( + { ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, + options + ) as Observable; + }) + ); }; -}; + return { search }; +} diff --git a/examples/demo_search/public/plugin.ts b/examples/demo_search/public/plugin.ts index a2539cc7a21c5..5d074c19903e2 100644 --- a/examples/demo_search/public/plugin.ts +++ b/examples/demo_search/public/plugin.ts @@ -17,7 +17,6 @@ * under the License. */ -import { DataPublicPluginSetup } from '../../../src/plugins/data/public'; import { Plugin, CoreSetup } from '../../../src/core/public'; import { DEMO_SEARCH_STRATEGY, @@ -29,10 +28,7 @@ import { } from '../common'; import { demoClientSearchStrategyProvider } from './demo_search_strategy'; import { asyncDemoClientSearchStrategyProvider } from './async_demo_search_strategy'; - -interface DemoDataSearchSetupDependencies { - data: DataPublicPluginSetup; -} +import { DemoDataSearchSetupDependencies, DemoDataSearchStartDependencies } from './types'; /** * Add the typescript mappings for our search strategy to the request and @@ -55,16 +51,13 @@ declare module '../../../src/plugins/data/public' { } } -export class DemoDataPlugin implements Plugin { - public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) { - deps.data.search.registerSearchStrategyProvider( - DEMO_SEARCH_STRATEGY, - demoClientSearchStrategyProvider - ); - deps.data.search.registerSearchStrategyProvider( - ASYNC_DEMO_SEARCH_STRATEGY, - asyncDemoClientSearchStrategyProvider - ); +export class DemoDataPlugin + implements Plugin { + public setup(core: CoreSetup, { data }: DemoDataSearchSetupDependencies) { + const demoClientSearchStrategy = demoClientSearchStrategyProvider(core); + const asyncDemoClientSearchStrategy = asyncDemoClientSearchStrategyProvider(core); + data.search.registerSearchStrategy(DEMO_SEARCH_STRATEGY, demoClientSearchStrategy); + data.search.registerSearchStrategy(ASYNC_DEMO_SEARCH_STRATEGY, asyncDemoClientSearchStrategy); } public start() {} diff --git a/examples/demo_search/public/types.ts b/examples/demo_search/public/types.ts new file mode 100644 index 0000000000000..64725da7df870 --- /dev/null +++ b/examples/demo_search/public/types.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DataPublicPluginStart, DataPublicPluginSetup } from '../../../src/plugins/data/public'; + +export interface DemoDataSearchSetupDependencies { + data: DataPublicPluginSetup; +} + +export interface DemoDataSearchStartDependencies { + data: DataPublicPluginStart; +} diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 5540039323756..eb3f937a4168b 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -341,8 +341,6 @@ export { SYNC_SEARCH_STRATEGY, getEsPreference, getSearchErrorType, - ISearchContext, - TSearchStrategyProvider, ISearchStrategy, ISearch, ISearchOptions, diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 7307c93139d59..d544d3c800bbe 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -21,11 +21,17 @@ import { Plugin, IndexPatternsContract } from '.'; import { fieldFormatsServiceMock } from './field_formats/mocks'; import { searchSetupMock, searchStartMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; +import { AutocompleteStart, AutocompleteSetup } from './autocomplete'; export type Setup = jest.Mocked>; export type Start = jest.Mocked>; -const autocompleteMock: any = { +const automcompleteSetupMock: jest.Mocked = { + addQuerySuggestionProvider: jest.fn(), + getQuerySuggestions: jest.fn(), +}; + +const autocompleteStartMock: jest.Mocked = { getValueSuggestions: jest.fn(), getQuerySuggestions: jest.fn(), hasQuerySuggestions: jest.fn(), @@ -34,7 +40,7 @@ const autocompleteMock: any = { const createSetupContract = (): Setup => { const querySetupMock = queryServiceMock.createSetupContract(); return { - autocomplete: autocompleteMock, + autocomplete: automcompleteSetupMock, search: searchSetupMock, fieldFormats: fieldFormatsServiceMock.createSetupContract(), query: querySetupMock, @@ -48,7 +54,7 @@ const createStartContract = (): Start => { createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']), createFiltersFromRangeSelectAction: jest.fn(), }, - autocomplete: autocompleteMock, + autocomplete: autocompleteStartMock, search: searchStartMock, fieldFormats: fieldFormatsServiceMock.createStartContract(), query: queryStartMock, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index e6f6ba0c1bd12..7054575e8ef9e 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1116,16 +1116,6 @@ export interface IResponseTypesMap { // @public (undocumented) export type ISearch = (request: IRequestTypesMap[T], options?: ISearchOptions) => Observable; -// Warning: (ae-missing-release-tag) "ISearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface ISearchContext { - // (undocumented) - core: CoreStart; - // (undocumented) - getSearchStrategy: (name: T) => TSearchStrategyProvider; -} - // Warning: (ae-missing-release-tag) "ISearchGeneric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1788,11 +1778,6 @@ export interface TimeRange { to: string; } -// Warning: (ae-missing-release-tag) "TSearchStrategyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export type TSearchStrategyProvider = (context: ISearchContext) => ISearchStrategy; - // Warning: (ae-missing-release-tag) "UI_SETTINGS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1866,20 +1851,20 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:379:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:378:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.test.ts b/src/plugins/data/public/search/es_search/es_search_strategy.test.ts index de1835f09f3bf..b580c5170aad7 100644 --- a/src/plugins/data/public/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/public/search/es_search/es_search_strategy.test.ts @@ -17,40 +17,31 @@ * under the License. */ +import { CoreSetup } from '../../../../../core/public'; import { coreMock } from '../../../../../core/public/mocks'; import { esSearchStrategyProvider } from './es_search_strategy'; -import { CoreStart } from 'kibana/public'; import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search'; describe('ES search strategy', () => { - let mockCoreStart: MockedKeys; - const mockSearch = jest.fn(); + let mockCoreSetup: MockedKeys; + const mockSearch = { search: jest.fn() }; beforeEach(() => { - mockCoreStart = coreMock.createStart(); - mockSearch.mockClear(); + mockCoreSetup = coreMock.createSetup(); + mockSearch.search.mockClear(); }); it('returns a strategy with `search` that calls the sync search `search`', () => { const request = { params: {} }; const options = {}; - const esSearch = esSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); + const esSearch = esSearchStrategyProvider(mockCoreSetup, mockSearch); esSearch.search(request, options); - expect(mockSearch.mock.calls[0][0]).toEqual({ + expect(mockSearch.search.mock.calls[0][0]).toEqual({ ...request, serverStrategy: ES_SEARCH_STRATEGY, }); - expect(mockSearch.mock.calls[0][1]).toBe(options); + expect(mockSearch.search.mock.calls[0][1]).toBe(options); }); }); diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.ts b/src/plugins/data/public/search/es_search/es_search_strategy.ts index a61428c998157..82f8bf21ee57b 100644 --- a/src/plugins/data/public/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/public/search/es_search/es_search_strategy.ts @@ -18,25 +18,26 @@ */ import { Observable } from 'rxjs'; +import { CoreSetup } from '../../../../../core/public'; import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../common/search'; +import { ISearch } from '../i_search'; +import { ISearchStrategy } from '../types'; import { SYNC_SEARCH_STRATEGY } from '../sync_search_strategy'; import { getEsPreference } from './get_es_preference'; -import { ISearchContext, TSearchStrategyProvider, ISearchStrategy } from '../types'; -export const esSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -): ISearchStrategy => { - const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); - const { search } = syncStrategyProvider(context); - return { - search: (request, options) => { - request.params = { - preference: getEsPreference(context.core.uiSettings), - ...request.params, - }; - return search({ ...request, serverStrategy: ES_SEARCH_STRATEGY }, options) as Observable< - IEsSearchResponse - >; - }, +export function esSearchStrategyProvider( + core: CoreSetup, + syncStrategy: ISearchStrategy +) { + const search: ISearch = (request, options) => { + request.params = { + preference: getEsPreference(core.uiSettings), + ...request.params, + }; + return syncStrategy.search( + { ...request, serverStrategy: ES_SEARCH_STRATEGY }, + options + ) as Observable; }; -}; + return { search }; +} diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 26149432f6117..53686f9be9b4d 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -21,13 +21,7 @@ export * from './aggs'; export * from './expressions'; export * from './tabify'; -export { - ISearchSetup, - ISearchStart, - ISearchContext, - TSearchStrategyProvider, - ISearchStrategy, -} from './types'; +export { ISearchSetup, ISearchStart, ISearchStrategy } from './types'; export { ISearch, diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index 44082040b5b0b..fcdbeb515423d 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -18,18 +18,20 @@ */ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; -import { ISearchStart } from './types'; +import { ISearchSetup, ISearchStart } from './types'; import { searchSourceMock, createSearchSourceMock } from './search_source/mocks'; -const searchSetupMock = { +export * from './search_source/mocks'; + +const searchSetupMock: jest.Mocked = { aggs: searchAggsSetupMock(), - registerSearchStrategyContext: jest.fn(), - registerSearchStrategyProvider: jest.fn(), + registerSearchStrategy: jest.fn(), }; const searchStartMock: jest.Mocked = { aggs: searchAggsStartMock(), setInterceptor: jest.fn(), + getSearchStrategy: jest.fn(), search: jest.fn(), searchSource: searchSourceMock, __LEGACY: { diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts index b1f7925bec4bb..fa138cef1a4a0 100644 --- a/src/plugins/data/public/search/search_service.test.ts +++ b/src/plugins/data/public/search/search_service.test.ts @@ -38,7 +38,7 @@ describe('Search service', () => { packageInfo: { version: '8' }, expressions: expressionsPluginMock.createSetupContract(), } as any); - expect(setup).toHaveProperty('registerSearchStrategyProvider'); + expect(setup).toHaveProperty('registerSearchStrategy'); }); }); }); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 1615aac9e7b7d..d5997c15817f6 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -18,11 +18,11 @@ */ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public'; +import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy'; +import { ISearchSetup, ISearchStart, TSearchStrategiesMap, ISearchStrategy } from './types'; import { ExpressionsSetup } from '../../../../plugins/expressions/public'; -import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy'; import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source'; -import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types'; import { TStrategyTypes } from './strategy_types'; import { getEsClient, LegacyApiCaller } from './legacy'; import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search'; @@ -54,11 +54,8 @@ interface SearchServiceStartDependencies { } /** - * The search plugin exposes two registration methods for other plugins: - * - registerSearchStrategyProvider for plugins to add their own custom - * search strategies - * - registerSearchStrategyContext for plugins to expose information - * and/or functionality for other search strategies to use + * The search plugin exposes a method `registerSearchStrategy` for other plugins + * to add their own custom search strategies. * * It also comes with two search strategy implementations - SYNC_SEARCH_STRATEGY and ES_SEARCH_STRATEGY. */ @@ -73,17 +70,19 @@ export class SearchService implements Plugin { private readonly aggTypesRegistry = new AggTypesRegistry(); private searchInterceptor!: SearchInterceptor; - private registerSearchStrategyProvider = ( + private registerSearchStrategy = ( name: T, - strategyProvider: TSearchStrategyProvider + strategy: ISearchStrategy ) => { - this.searchStrategies[name] = strategyProvider; + this.searchStrategies[name] = strategy; }; - private getSearchStrategy = (name: T): TSearchStrategyProvider => { - const strategyProvider = this.searchStrategies[name]; - if (!strategyProvider) throw new Error(`Search strategy ${name} not found`); - return strategyProvider; + private getSearchStrategy = (name: T): ISearchStrategy => { + const strategy = this.searchStrategies[name]; + if (!strategy) { + throw new Error(`Search strategy ${name} not found`); + } + return strategy; }; public setup( @@ -91,8 +90,11 @@ export class SearchService implements Plugin { { expressions, packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies ): ISearchSetup { this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo); - this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider); - this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider); + + const syncSearchStrategy = syncSearchStrategyProvider(core); + const esSearchStrategy = esSearchStrategyProvider(core, syncSearchStrategy); + this.registerSearchStrategy(SYNC_SEARCH_STRATEGY, syncSearchStrategy); + this.registerSearchStrategy(ES_SEARCH_STRATEGY, esSearchStrategy); const aggTypesSetup = this.aggTypesRegistry.setup(); @@ -114,7 +116,7 @@ export class SearchService implements Plugin { calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings), types: aggTypesSetup, }, - registerSearchStrategyProvider: this.registerSearchStrategyProvider, + registerSearchStrategy: this.registerSearchStrategy, }; } @@ -134,12 +136,10 @@ export class SearchService implements Plugin { const aggTypesStart = this.aggTypesRegistry.start(); const search: ISearchGeneric = (request, options, strategyName) => { - const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY); - const searchStrategy = strategyProvider({ - core, - getSearchStrategy: this.getSearchStrategy, - }); - return this.searchInterceptor.search(searchStrategy.search as any, request, options); + const { search: defaultSearch } = this.getSearchStrategy( + strategyName || DEFAULT_SEARCH_STRATEGY + ); + return this.searchInterceptor.search(defaultSearch as any, request, options); }; const legacySearch = { @@ -164,6 +164,7 @@ export class SearchService implements Plugin { }, types: aggTypesStart, }, + getSearchStrategy: this.getSearchStrategy, search, searchSource: { create: createSearchSource(dependencies.indexPatterns, searchSourceDependencies), diff --git a/src/plugins/data/public/search/sync_search_strategy.test.ts b/src/plugins/data/public/search/sync_search_strategy.test.ts index 6197980314b86..383a312fffad9 100644 --- a/src/plugins/data/public/search/sync_search_strategy.test.ts +++ b/src/plugins/data/public/search/sync_search_strategy.test.ts @@ -19,26 +19,23 @@ import { coreMock } from '../../../../core/public/mocks'; import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy'; -import { CoreStart } from 'kibana/public'; +import { CoreSetup } from 'kibana/public'; describe('Sync search strategy', () => { - let mockCoreStart: MockedKeys; + let mockCoreSetup: MockedKeys; beforeEach(() => { - mockCoreStart = coreMock.createStart(); + mockCoreSetup = coreMock.createSetup(); }); it('returns a strategy with `search` that calls the backend API', () => { - mockCoreStart.http.fetch.mockImplementationOnce(() => Promise.resolve()); + mockCoreSetup.http.fetch.mockImplementationOnce(() => Promise.resolve()); - const syncSearch = syncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn(), - }); + const syncSearch = syncSearchStrategyProvider(mockCoreSetup); const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; syncSearch.search(request, {}); - expect(mockCoreStart.http.fetch.mock.calls[0][0]).toEqual({ + expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({ path: `/internal/search/${SYNC_SEARCH_STRATEGY}`, body: JSON.stringify({ serverStrategy: 'SYNC_SEARCH_STRATEGY', @@ -52,15 +49,12 @@ describe('Sync search strategy', () => { const expectedLoadingCountValues = [0, 1, 0]; const receivedLoadingCountValues: number[] = []; - mockCoreStart.http.fetch.mockResolvedValueOnce('response'); + mockCoreSetup.http.fetch.mockResolvedValueOnce('response'); - const syncSearch = syncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn(), - }); + const syncSearch = syncSearchStrategyProvider(mockCoreSetup); const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; - const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0]; + const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0]; loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value)); await syncSearch.search(request, {}).toPromise(); @@ -73,15 +67,12 @@ describe('Sync search strategy', () => { const expectedLoadingCountValues = [0, 1, 0]; const receivedLoadingCountValues: number[] = []; - mockCoreStart.http.fetch.mockRejectedValueOnce('error'); + mockCoreSetup.http.fetch.mockRejectedValueOnce('error'); - const syncSearch = syncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn(), - }); + const syncSearch = syncSearchStrategyProvider(mockCoreSetup); const request = { serverStrategy: SYNC_SEARCH_STRATEGY }; - const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0]; + const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0]; loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value)); try { diff --git a/src/plugins/data/public/search/sync_search_strategy.ts b/src/plugins/data/public/search/sync_search_strategy.ts index 860ce593ae217..25d2731df7d5c 100644 --- a/src/plugins/data/public/search/sync_search_strategy.ts +++ b/src/plugins/data/public/search/sync_search_strategy.ts @@ -19,9 +19,9 @@ import { BehaviorSubject, from } from 'rxjs'; import { finalize } from 'rxjs/operators'; +import { CoreSetup } from '../../../../core/public'; import { IKibanaSearchRequest } from '../../common/search'; -import { ISearch, ISearchOptions } from './i_search'; -import { TSearchStrategyProvider, ISearchStrategy, ISearchContext } from './types'; +import { ISearch } from './i_search'; export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY'; @@ -29,27 +29,22 @@ export interface ISyncSearchRequest extends IKibanaSearchRequest { serverStrategy: string; } -export const syncSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -): ISearchStrategy => { +export function syncSearchStrategyProvider(core: CoreSetup) { const loadingCount$ = new BehaviorSubject(0); - context.core.http.addLoadingCountSource(loadingCount$); + core.http.addLoadingCountSource(loadingCount$); - const search: ISearch = ( - request: ISyncSearchRequest, - options: ISearchOptions = {} - ) => { + const search: ISearch = (request, options) => { loadingCount$.next(loadingCount$.getValue() + 1); return from( - context.core.http.fetch({ + core.http.fetch({ path: `/internal/search/${request.serverStrategy}`, method: 'POST', body: JSON.stringify(request), - signal: options.signal, + signal: options?.signal, }) ).pipe(finalize(() => loadingCount$.next(loadingCount$.getValue() - 1))); }; return { search }; -}; +} diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 64b4f1c5c2983..135974b697739 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -17,7 +17,6 @@ * under the License. */ -import { CoreStart } from 'kibana/public'; import { SearchAggsSetup, SearchAggsStart } from './aggs'; import { ISearch, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; @@ -25,11 +24,6 @@ import { LegacyApiCaller } from './legacy/es_client'; import { SearchInterceptor } from './search_interceptor'; import { ISearchSource, SearchSourceFields } from './search_source'; -export interface ISearchContext { - core: CoreStart; - getSearchStrategy: (name: T) => TSearchStrategyProvider; -} - /** * Search strategy interface contains a search method that takes in * a request and returns a promise that resolves to a response. @@ -39,27 +33,23 @@ export interface ISearchStrategy { } export type TSearchStrategiesMap = { - [K in TStrategyTypes]?: TSearchStrategyProvider; + [K in TStrategyTypes]?: ISearchStrategy; }; -/** - * Search strategy provider creates an instance of a search strategy with the request - * handler context bound to it. This way every search strategy can use - * whatever information they require from the request context. - */ -export type TSearchStrategyProvider = ( - context: ISearchContext -) => ISearchStrategy; - /** * Extension point exposed for other plugins to register their own search * strategies. */ -export type TRegisterSearchStrategyProvider = ( +export type TRegisterSearchStrategy = ( name: T, - searchStrategyProvider: TSearchStrategyProvider + searchStrategy: ISearchStrategy ) => void; +/** + * Used if a plugin needs access to an already registered search strategy. + */ +export type TGetSearchStrategy = (name: T) => ISearchStrategy; + export interface ISearchStartLegacy { esClient: LegacyApiCaller; } @@ -74,12 +64,18 @@ export interface ISearchSetup { * Extension point exposed for other plugins to register their own search * strategies. */ - registerSearchStrategyProvider: TRegisterSearchStrategyProvider; + registerSearchStrategy: TRegisterSearchStrategy; } export interface ISearchStart { aggs: SearchAggsStart; setInterceptor: (searchInterceptor: SearchInterceptor) => void; + + /** + * Used if a plugin needs access to an already registered search strategy. + */ + getSearchStrategy: TGetSearchStrategy; + search: ISearchGeneric; searchSource: { create: (fields?: SearchSourceFields) => Promise; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index 26ddb0809d24b..707e65d60870c 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -277,6 +277,7 @@ exports[`SavedObjectsTable import should show the flyout 1`] = ` "getMetrics": [Function], }, }, + "getSearchStrategy": [MockFunction], "search": [MockFunction], "searchSource": Object { "create": [MockFunction], diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index 72e0817eea8df..879c73587ed96 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -29,19 +29,20 @@ export interface DataEnhancedStartDependencies { export type DataEnhancedSetup = ReturnType; export type DataEnhancedStart = ReturnType; -export class DataEnhancedPlugin implements Plugin { - constructor() {} - - public setup(core: CoreSetup, { data }: DataEnhancedSetupDependencies) { +export class DataEnhancedPlugin + implements Plugin { + public setup( + core: CoreSetup, + { data }: DataEnhancedSetupDependencies + ) { data.autocomplete.addQuerySuggestionProvider( KUERY_LANGUAGE_NAME, setupKqlQuerySuggestionProvider(core) ); - data.search.registerSearchStrategyProvider(ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider); - data.search.registerSearchStrategyProvider( - ES_SEARCH_STRATEGY, - enhancedEsSearchStrategyProvider - ); + const asyncSearchStrategy = asyncSearchStrategyProvider(core); + const esSearchStrategy = enhancedEsSearchStrategyProvider(core, asyncSearchStrategy); + data.search.registerSearchStrategy(ASYNC_SEARCH_STRATEGY, asyncSearchStrategy); + data.search.registerSearchStrategy(ES_SEARCH_STRATEGY, esSearchStrategy); } public start(core: CoreStart, plugins: DataEnhancedStartDependencies) { diff --git a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts index 6c635cc5b4489..3013f9966f068 100644 --- a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts @@ -6,35 +6,37 @@ import { of } from 'rxjs'; import { AbortController } from 'abort-controller'; +import { CoreSetup } from '../../../../../src/core/public'; import { coreMock } from '../../../../../src/core/public/mocks'; +import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; import { asyncSearchStrategyProvider } from './async_search_strategy'; -import { IAsyncSearchOptions } from './types'; -import { CoreStart } from 'kibana/public'; +import { IAsyncSearchOptions } from '.'; +import { DataEnhancedStartDependencies } from '../plugin'; describe('Async search strategy', () => { - let mockCoreStart: MockedKeys; + let mockCoreSetup: jest.Mocked>; + let mockDataStart: jest.Mocked; const mockSearch = jest.fn(); const mockRequest = { params: {}, serverStrategy: 'foo' }; const mockOptions: IAsyncSearchOptions = { pollInterval: 0 }; beforeEach(() => { - mockCoreStart = coreMock.createStart(); + mockCoreSetup = coreMock.createSetup(); + mockDataStart = dataPluginMock.createStartContract(); + (mockDataStart.search.getSearchStrategy as jest.Mock).mockReturnValue({ search: mockSearch }); + mockCoreSetup.getStartServices.mockResolvedValue([ + undefined as any, + { data: mockDataStart }, + undefined, + ]); mockSearch.mockReset(); }); it('only sends one request if the first response is complete', async () => { mockSearch.mockReturnValueOnce(of({ id: 1, total: 1, loaded: 1 })); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); await asyncSearch.search(mockRequest, mockOptions).toPromise(); @@ -51,17 +53,7 @@ describe('Async search strategy', () => { of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false }) ); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); - + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); expect(mockSearch).toBeCalledTimes(0); await asyncSearch.search(mockRequest, mockOptions).toPromise(); @@ -75,17 +67,7 @@ describe('Async search strategy', () => { .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: true })) .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: true })); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); - + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); expect(mockSearch).toBeCalledTimes(0); await asyncSearch @@ -104,16 +86,7 @@ describe('Async search strategy', () => { of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false }) ); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); expect(mockSearch).toBeCalledTimes(0); @@ -131,16 +104,7 @@ describe('Async search strategy', () => { of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false }) ); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); expect(mockSearch).toBeCalledTimes(0); @@ -157,16 +121,7 @@ describe('Async search strategy', () => { .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })) .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 })); - const asyncSearch = asyncSearchStrategyProvider({ - core: mockCoreStart, - getSearchStrategy: jest.fn().mockImplementation(() => { - return () => { - return { - search: mockSearch, - }; - }; - }), - }); + const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup); const abortController = new AbortController(); const options = { ...mockOptions, signal: abortController.signal }; @@ -178,7 +133,7 @@ describe('Async search strategy', () => { } catch (e) { expect(e.name).toBe('AbortError'); expect(mockSearch).toBeCalledTimes(1); - expect(mockCoreStart.http.delete).toBeCalled(); + expect(mockCoreSetup.http.delete).toBeCalled(); } }); }); diff --git a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts index 18b5b976b3c1b..7de4dd28ad3d7 100644 --- a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.ts @@ -4,17 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EMPTY, fromEvent, NEVER, Observable, throwError, timer } from 'rxjs'; -import { mergeMap, expand, takeUntil } from 'rxjs/operators'; +import { EMPTY, fromEvent, NEVER, throwError, timer, Observable, from } from 'rxjs'; +import { mergeMap, expand, takeUntil, share, flatMap } from 'rxjs/operators'; +import { CoreSetup } from '../../../../../src/core/public'; import { AbortError } from '../../../../../src/plugins/data/common'; import { - IKibanaSearchResponse, - ISearchContext, + ISearch, ISearchStrategy, + ISyncSearchRequest, SYNC_SEARCH_STRATEGY, - TSearchStrategyProvider, } from '../../../../../src/plugins/data/public'; -import { IAsyncSearchRequest, IAsyncSearchOptions, IAsyncSearchResponse } from './types'; +import { IAsyncSearchOptions, IAsyncSearchResponse, IAsyncSearchRequest } from './types'; +import { DataEnhancedStartDependencies } from '../plugin'; export const ASYNC_SEARCH_STRATEGY = 'ASYNC_SEARCH_STRATEGY'; @@ -24,55 +25,59 @@ declare module '../../../../../src/plugins/data/public' { } } -export const asyncSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -): ISearchStrategy => { - const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); - const { search } = syncStrategyProvider(context); - return { - search: ( - request: IAsyncSearchRequest, - { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} - ): Observable => { - const { serverStrategy } = request; - let id: string | undefined = request.id; +export function asyncSearchStrategyProvider( + core: CoreSetup +): ISearchStrategy { + const startServices$ = from(core.getStartServices()).pipe(share()); - const aborted$ = options.signal - ? fromEvent(options.signal, 'abort').pipe( - mergeMap(() => { - // If we haven't received the response to the initial request, including the ID, then - // we don't need to send a follow-up request to delete this search. Otherwise, we - // send the follow-up request to delete this search, then throw an abort error. - if (id !== undefined) { - context.core.http.delete(`/internal/search/${request.serverStrategy}/${id}`); - } - return throwError(new AbortError()); - }) - ) - : NEVER; + const search: ISearch = ( + request: ISyncSearchRequest, + { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} + ) => { + const { serverStrategy } = request; + let { id } = request; - return search(request, options).pipe( - expand((response: IAsyncSearchResponse) => { - // If the response indicates of an error, stop polling and complete the observable - if (!response || (response.is_partial && !response.is_running)) { + const aborted$ = options.signal + ? fromEvent(options.signal, 'abort').pipe( + mergeMap(() => { + // If we haven't received the response to the initial request, including the ID, then + // we don't need to send a follow-up request to delete this search. Otherwise, we + // send the follow-up request to delete this search, then throw an abort error. + if (id !== undefined) { + core.http.delete(`/internal/search/${request.serverStrategy}/${id}`); + } return throwError(new AbortError()); - } + }) + ) + : NEVER; + + return startServices$.pipe( + flatMap((startServices) => { + const syncSearch = startServices[1].data.search.getSearchStrategy(SYNC_SEARCH_STRATEGY); + return (syncSearch.search(request, options) as Observable).pipe( + expand((response) => { + // If the response indicates of an error, stop polling and complete the observable + if (!response || (response.is_partial && !response.is_running)) { + return throwError(new AbortError()); + } - // If the response indicates it is complete, stop polling and complete the observable - if (!response.is_running) return EMPTY; + // If the response indicates it is complete, stop polling and complete the observable + if (!response.is_running) return EMPTY; - id = response.id; + id = response.id; - // Delay by the given poll interval - return timer(pollInterval).pipe( - // Send future requests using just the ID from the response - mergeMap(() => { - return search({ id, serverStrategy }, options); - }) - ); - }), - takeUntil(aborted$) - ); - }, + // Delay by the given poll interval + return timer(pollInterval).pipe( + // Send future requests using just the ID from the response + mergeMap(() => { + return search({ id, serverStrategy }, options); + }) + ); + }), + takeUntil(aborted$) + ); + }) + ); }; -}; + return { search }; +} diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.test.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.test.ts new file mode 100644 index 0000000000000..5d6bd53e2c945 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.test.ts @@ -0,0 +1,35 @@ +/* + * 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 { CoreSetup } from '../../../../../src/core/public'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common'; +import { enhancedEsSearchStrategyProvider } from './es_search_strategy'; +import { IAsyncSearchOptions } from '.'; + +describe('Enhanced ES search strategy', () => { + let mockCoreSetup: jest.Mocked; + const mockSearch = { search: jest.fn() }; + + beforeEach(() => { + mockCoreSetup = coreMock.createSetup(); + mockSearch.search.mockClear(); + }); + + it('returns a strategy with `search` that calls the async search `search`', () => { + const request = { params: {} }; + const options: IAsyncSearchOptions = { pollInterval: 0 }; + + const esSearch = enhancedEsSearchStrategyProvider(mockCoreSetup, mockSearch); + esSearch.search(request, options); + + expect(mockSearch.search.mock.calls[0][0]).toEqual({ + ...request, + serverStrategy: ES_SEARCH_STRATEGY, + }); + expect(mockSearch.search.mock.calls[0][1]).toEqual(options); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts index 3a511c7b5a176..c4b293a52a104 100644 --- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -5,42 +5,40 @@ */ import { Observable } from 'rxjs'; +import { CoreSetup } from '../../../../../src/core/public'; import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../../../src/plugins/data/common'; import { - TSearchStrategyProvider, - ISearchContext, ISearch, getEsPreference, + ISearchStrategy, UI_SETTINGS, } from '../../../../../src/plugins/data/public'; import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common'; import { ASYNC_SEARCH_STRATEGY } from './async_search_strategy'; import { IAsyncSearchOptions } from './types'; -export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext -) => { - const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY); - const { search: asyncSearch } = asyncStrategyProvider(context); - +export function enhancedEsSearchStrategyProvider( + core: CoreSetup, + asyncStrategy: ISearchStrategy +) { const search: ISearch = ( request: IEnhancedEsSearchRequest, options ) => { const params: EnhancedSearchParams = { - ignoreThrottled: !context.core.uiSettings.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), - preference: getEsPreference(context.core.uiSettings), + ignoreThrottled: !core.uiSettings.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), + preference: getEsPreference(core.uiSettings), ...request.params, }; request.params = params; const asyncOptions: IAsyncSearchOptions = { pollInterval: 0, ...options }; - return asyncSearch( + return asyncStrategy.search( { ...request, serverStrategy: ES_SEARCH_STRATEGY }, asyncOptions ) as Observable; }; return { search }; -}; +} From 23be466c977e9f0b6367de7f5693a4ea1f6387f7 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 8 Jun 2020 20:24:54 +0200 Subject: [PATCH 26/27] [Discover] Replace EuiTooltip by native title for better performance (#68280) --- .../components/sidebar/discover_field.tsx | 18 ++++++++---------- .../components/sidebar/discover_sidebar.scss | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx index 9e5429882e3c3..5f40c55e30e7e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { EuiButton, EuiToolTip, EuiText } from '@elastic/eui'; +import { EuiButton, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DiscoverFieldDetails } from './discover_field_details'; import { FieldIcon } from '../../../../../kibana_react/public'; @@ -125,16 +125,14 @@ export function DiscoverField({ /> - - - {useShortDots ? shortenDottedString(field.name) : field.displayName} - - + {useShortDots ? shortenDottedString(field.name) : field.displayName} + {field.name !== '_source' && !selected && ( diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss index fe04d42d614ff..9f7700c7f395c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss @@ -107,6 +107,7 @@ */ .dscSidebarItem__action { opacity: 0; /* 1 */ + transition: none; &:focus { opacity: 1; /* 2 */ From 25e71c10963f8cc959b63bfc83da9e4b3a09fd5e Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 8 Jun 2020 11:29:09 -0700 Subject: [PATCH 27/27] upgrade npm-run-path (#68539) Co-authored-by: spalger --- package.json | 2 +- packages/kbn-dev-utils/package.json | 2 +- packages/kbn-es/package.json | 2 +- packages/kbn-plugin-generator/package.json | 2 +- packages/kbn-plugin-helpers/package.json | 2 +- packages/kbn-pm/dist/index.js | 2 +- packages/kbn-pm/package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 8 ++++---- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 0873ab8c158e1..9423ee31c8bbe 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "elastic-apm-node": "^3.6.0", "elasticsearch": "^16.7.0", "elasticsearch-browser": "^16.7.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "expiry-js": "0.1.7", "fast-deep-equal": "^3.1.1", "file-loader": "4.2.0", diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index 08b2e5b226967..5000d8b7490be 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -13,7 +13,7 @@ "axios": "^0.19.0", "chalk": "^2.4.2", "dedent": "^0.7.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "exit-hook": "^2.2.0", "getopts": "^2.2.5", "load-json-file": "^6.2.0", diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index 8b964d8399904..271b4a3dc661b 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -11,7 +11,7 @@ "chalk": "^2.4.2", "dedent": "^0.7.0", "del": "^5.1.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "getopts": "^2.2.4", "glob": "^7.1.2", "node-fetch": "^2.6.0", diff --git a/packages/kbn-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json index b3b1eff41e4b5..b9df67b32e5d3 100644 --- a/packages/kbn-plugin-generator/package.json +++ b/packages/kbn-plugin-generator/package.json @@ -6,7 +6,7 @@ "dependencies": { "chalk": "^2.4.2", "dedent": "^0.7.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "getopts": "^2.2.4", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 040b779a69951..362e8302c3bec 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -16,7 +16,7 @@ "argv-split": "^2.0.1", "commander": "^3.0.0", "del": "^5.1.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "globby": "^8.0.1", "gulp-babel": "^8.0.0", "gulp-rename": "1.4.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 21fff4d85ece6..7b233ef010653 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -34655,7 +34655,7 @@ const npmRunPath = options => { // Ensure the running `node` binary is used const execPathDir = path.resolve(options.cwd, options.execPath, '..'); - result.unshift(execPathDir); + result.push(execPathDir); return result.concat(options.path).join(path.delimiter); }; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 234877e9ae626..5938d15573d63 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -42,7 +42,7 @@ "cpy": "^8.0.0", "dedent": "^0.7.0", "del": "^5.1.0", - "execa": "^4.0.0", + "execa": "^4.0.2", "getopts": "^2.2.4", "glob": "^7.1.2", "globby": "^8.0.1", diff --git a/x-pack/package.json b/x-pack/package.json index 227547ce15fd1..b3dcde2194d3f 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -134,7 +134,7 @@ "enzyme-adapter-react-16": "^1.15.2", "enzyme-adapter-utils": "^1.13.0", "enzyme-to-json": "^3.4.4", - "execa": "^4.0.0", + "execa": "^4.0.2", "fancy-log": "^1.3.2", "fetch-mock": "^7.3.9", "graphql-code-generator": "^0.18.2", diff --git a/yarn.lock b/yarn.lock index e47af2c6fe284..df478e79d2bf6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13078,7 +13078,7 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0, execa@^4.0.2: +execa@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== @@ -21741,9 +21741,9 @@ npm-run-path@^2.0.0: path-key "^2.0.0" npm-run-path@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.0.tgz#d644ec1bd0569187d2a52909971023a0a58e8438" - integrity sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ== + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0"