Skip to content

Commit

Permalink
"Explore underlying data" in-chart action (elastic#69494) (elastic#70061
Browse files Browse the repository at this point in the history
)

* refactor: πŸ’‘ rename folder to "explore_data"

* style: πŸ’„ check for "share" plugin in more semantic way

"explore data" actions use Discover URL generator, which is registered
in "share" plugin, which is optional plugin, so we check for its
existance, because otherwise URL generator is not available.

* refactor: πŸ’‘ move KibanaURL to a separate file

* feat: 🎸 add "Explore underlying data" in-chart action

* fix: πŸ› fix imports after refactor

* feat: 🎸 add start.filtersFromContext to embeddable plugin

* feat: 🎸 add type checkers to data plugin

* feat: 🎸 better handle empty filters in Discover URL generator

* feat: 🎸 implement .getUrl() method of explore data in-chart act

* feat: 🎸 add embeddable.filtersAndTimeRangeFromContext()

* feat: 🎸 improve getUrl() method of explore data action

* test: πŸ’ update test mock

* fix possible stale hashHistory.location in discover

* style: πŸ’„ ensureHashHistoryLocation -> syncHistoryLocations

* docs: ✏️ update autogenerated docs

* test: πŸ’ add in-chart "Explore underlying data" unit tests

* test: πŸ’ add in-chart "Explore underlying data" functional tests

* test: πŸ’ clean-up custom time range after panel action tests

* chore: πŸ€– fix embeddable plugin mocks

* chore: πŸ€– fix another mock

* test: πŸ’ add support for new action to pie chart service

Co-authored-by: Anton Dosov <anton.dosov@elastic.co>

Co-authored-by: Anton Dosov <anton.dosov@elastic.co>
  • Loading branch information
streamich and Dosant authored Jun 26, 2020
1 parent 7f46f11 commit 8788928
Show file tree
Hide file tree
Showing 41 changed files with 997 additions and 197 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [isFilter](./kibana-plugin-plugins-data-public.isfilter.md)

## isFilter variable

<b>Signature:</b>

```typescript
isFilter: (x: unknown) => x is Filter
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [isFilters](./kibana-plugin-plugins-data-public.isfilters.md)

## isFilters variable

<b>Signature:</b>

```typescript
isFilters: (x: unknown) => x is Filter[]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [isQuery](./kibana-plugin-plugins-data-public.isquery.md)

## isQuery variable

<b>Signature:</b>

```typescript
isQuery: (x: unknown) => x is Query
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [isTimeRange](./kibana-plugin-plugins-data-public.istimerange.md)

## isTimeRange variable

<b>Signature:</b>

```typescript
isTimeRange: (x: unknown) => x is TimeRange
```
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@
| [getKbnTypeNames](./kibana-plugin-plugins-data-public.getkbntypenames.md) | Get the esTypes known by all kbnFieldTypes {<!-- -->Array<string>} |
| [indexPatterns](./kibana-plugin-plugins-data-public.indexpatterns.md) | |
| [injectSearchSourceReferences](./kibana-plugin-plugins-data-public.injectsearchsourcereferences.md) | |
| [isFilter](./kibana-plugin-plugins-data-public.isfilter.md) | |
| [isFilters](./kibana-plugin-plugins-data-public.isfilters.md) | |
| [isQuery](./kibana-plugin-plugins-data-public.isquery.md) | |
| [isTimeRange](./kibana-plugin-plugins-data-public.istimerange.md) | |
| [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md) | |
| [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | |
| [search](./kibana-plugin-plugins-data-public.search.md) | |
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/data/common/es_query/filters/meta_filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,13 @@ export const pinFilter = (filter: Filter) =>

export const unpinFilter = (filter: Filter) =>
!isFilterPinned(filter) ? filter : toggleFilterPinned(filter);

export const isFilter = (x: unknown): x is Filter =>
!!x &&
typeof x === 'object' &&
!!(x as Filter).meta &&
typeof (x as Filter).meta === 'object' &&
typeof (x as Filter).meta.disabled === 'boolean';

export const isFilters = (x: unknown): x is Filter[] =>
Array.isArray(x) && !x.find((y) => !isFilter(y));
3 changes: 2 additions & 1 deletion src/plugins/data/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
export * from './constants';
export * from './es_query';
export * from './field_formats';
export * from './field_mapping';
export * from './index_patterns';
export * from './kbn_field_types';
export * from './query';
export * from './search';
export * from './search/aggs';
export * from './timefilter';
export * from './types';
export * from './utils';
export * from './field_mapping';
1 change: 1 addition & 0 deletions src/plugins/data/common/query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@

export * from './filter_manager';
export * from './types';
export * from './is_query';
27 changes: 27 additions & 0 deletions src/plugins/data/common/query/is_query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 { Query } from './types';

export const isQuery = (x: unknown): x is Query =>
!!x &&
typeof x === 'object' &&
typeof (x as Query).language === 'string' &&
(typeof (x as Query).query === 'string' ||
(typeof (x as Query).query === 'object' && !!(x as Query).query));
20 changes: 20 additions & 0 deletions src/plugins/data/common/timefilter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/

export { isTimeRange } from './is_time_range';
26 changes: 26 additions & 0 deletions src/plugins/data/common/timefilter/is_time_range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { TimeRange } from './types';

export const isTimeRange = (x: unknown): x is TimeRange =>
!!x &&
typeof x === 'object' &&
typeof (x as TimeRange).from === 'string' &&
typeof (x as TimeRange).to === 'string';
2 changes: 2 additions & 0 deletions src/plugins/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ export {
getKbnTypeNames,
} from '../common';

export { isTimeRange, isQuery, isFilter, isFilters } from '../common';

export * from '../common/field_mapping';

/*
Expand Down
20 changes: 20 additions & 0 deletions src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,26 @@ export interface ISearchStrategy<T extends TStrategyTypes> {
search: ISearch<T>;
}

// Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const isFilter: (x: unknown) => x is Filter;

// Warning: (ae-missing-release-tag) "isFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const isFilters: (x: unknown) => x is Filter[];

// Warning: (ae-missing-release-tag) "isQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const isQuery: (x: unknown) => x is Query;

// Warning: (ae-missing-release-tag) "isTimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const isTimeRange: (x: unknown) => x is TimeRange;

// Warning: (ae-missing-release-tag) "ISyncSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/discover/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export function plugin(initializerContext: PluginInitializerContext) {

export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches';
export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable';
export { DISCOVER_APP_URL_GENERATOR } from './url_generator';
export { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from './url_generator';
15 changes: 14 additions & 1 deletion src/plugins/discover/public/kibana_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,23 @@ export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter<Doc
'DocViewsRegistry'
);
/**
* Makes sure discover and context are using one instance of history
* Makes sure discover and context are using one instance of history.
*/
export const getHistory = _.once(() => createHashHistory());

/**
* Discover currently uses two `history` instances: one from Kibana Platform and
* another from `history` package. Below function is used every time Discover
* app is loaded to synchronize both instances.
*
* This helper is temporary until https://github.com/elastic/kibana/issues/65161 is resolved.
*/
export const syncHistoryLocations = () => {
const h = getHistory();
Object.assign(h.location, createHashHistory().location);
return h;
};

export const [getScopedHistory, setScopedHistory] = createGetterSetter<ScopedHistory>(
'scopedHistory'
);
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/discover/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
setServices,
setScopedHistory,
getScopedHistory,
syncHistoryLocations,
getServices,
} from './kibana_services';
import { createSavedSearchesLoader } from './saved_searches';
Expand Down Expand Up @@ -245,6 +246,7 @@ export class DiscoverPlugin
throw Error('Discover plugin method initializeInnerAngular is undefined');
}
setScopedHistory(params.history);
syncHistoryLocations();
appMounted();
const {
plugins: { data: dataStart },
Expand Down
6 changes: 4 additions & 2 deletions src/plugins/discover/public/url_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ export class DiscoverUrlGenerator
const queryState: QueryState = {};

if (query) appState.query = query;
if (filters) appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f));
if (filters && filters.length)
appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f));
if (indexPatternId) appState.index = indexPatternId;

if (timeRange) queryState.time = timeRange;
if (filters) queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f));
if (filters && filters.length)
queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f));
if (refreshInterval) queryState.refreshInterval = refreshInterval;

let url = `${this.params.appBasePath}#/${savedSearchPath}`;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/embeddable/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"server": false,
"ui": true,
"requiredPlugins": [
"data",
"inspector",
"uiActions"
]
Expand Down
1 change: 1 addition & 0 deletions src/plugins/embeddable/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export {
ACTION_EDIT_PANEL,
Adapters,
AddPanelAction,
ChartActionContext,
Container,
ContainerInput,
ContainerOutput,
Expand Down
14 changes: 9 additions & 5 deletions src/plugins/embeddable/public/lib/triggers/triggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ export interface ValueClickTriggerContext<T extends IEmbeddable = IEmbeddable> {
};
}

export const isValueClickTriggerContext = (
context: ValueClickTriggerContext | RangeSelectTriggerContext
): context is ValueClickTriggerContext => context.data && 'data' in context.data;

export interface RangeSelectTriggerContext<T extends IEmbeddable = IEmbeddable> {
embeddable?: T;
data: {
Expand All @@ -53,8 +49,16 @@ export interface RangeSelectTriggerContext<T extends IEmbeddable = IEmbeddable>
};
}

export type ChartActionContext<T extends IEmbeddable = IEmbeddable> =
| ValueClickTriggerContext<T>
| RangeSelectTriggerContext<T>;

export const isValueClickTriggerContext = (
context: ChartActionContext
): context is ValueClickTriggerContext => context.data && 'data' in context.data;

export const isRangeSelectTriggerContext = (
context: ValueClickTriggerContext | RangeSelectTriggerContext
context: ChartActionContext
): context is RangeSelectTriggerContext => context.data && 'range' in context.data;

export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER';
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/embeddable/public/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { coreMock } from '../../../core/public/mocks';
import { UiActionsService } from './lib/ui_actions';
import { CoreStart } from '../../../core/public';
import { Start as InspectorStart } from '../../inspector/public';
import { dataPluginMock } from '../../data/public/mocks';

// eslint-disable-next-line
import { inspectorPluginMock } from '../../inspector/public/mocks';
Expand Down Expand Up @@ -100,6 +101,8 @@ const createStartContract = (): Start => {
EmbeddablePanel: jest.fn(),
getEmbeddablePanel: jest.fn(),
getStateTransfer: jest.fn(() => createEmbeddableStateTransferMock() as EmbeddableStateTransfer),
filtersAndTimeRangeFromContext: jest.fn(),
filtersFromContext: jest.fn(),
};
return startContract;
};
Expand All @@ -108,11 +111,13 @@ const createInstance = (setupPlugins: Partial<EmbeddableSetupDependencies> = {})
const plugin = new EmbeddablePublicPlugin({} as any);
const setup = plugin.setup(coreMock.createSetup(), {
uiActions: setupPlugins.uiActions || uiActionsPluginMock.createSetupContract(),
data: dataPluginMock.createSetupContract(),
});
const doStart = (startPlugins: Partial<EmbeddableStartDependencies> = {}) =>
plugin.start(coreMock.createStart(), {
uiActions: startPlugins.uiActions || uiActionsPluginMock.createStartContract(),
inspector: inspectorPluginMock.createStartContract(),
data: dataPluginMock.createStartContract(),
});
return {
plugin,
Expand Down
Loading

0 comments on commit 8788928

Please sign in to comment.