Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Discover URL generator #67937

Merged
merged 21 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cf9ca31
feat: 🎸 stub discover_enhanced plugin
streamich Apr 16, 2020
33521a0
feat: 🎸 add Discover URL generator
streamich Apr 30, 2020
185db86
chore: 🤖 remove x-pack plugin
streamich Jun 2, 2020
a203709
fix: 🐛 fix types in URL generator
streamich Apr 30, 2020
48757ff
test: 💍 setup test file for Discover URL generator
streamich Jun 2, 2020
baad7c6
feat: 🎸 expose Discover URL generator in start life-cycle
streamich Jun 2, 2020
385dc7e
feat: 🎸 add ability to specify saved search ID in URL generator
streamich Jun 2, 2020
02da13e
docs: ✏️ add JSDoc for Discover URL generator
streamich Jun 2, 2020
a0f9abf
fix: 🐛 set correctly global filters in Discover URL generator
streamich Jun 2, 2020
9bb749c
Merge remote-tracking branch 'upstream/master' into discover-url-gene…
streamich Jun 3, 2020
186f40e
docs: ✏️ remove wrong comment in JsDoc
streamich Jun 3, 2020
b8a2087
style: 💄 format single arg arrow function with parens
streamich Jun 3, 2020
5c4ff63
chore: 🤖 disable toggles in Dicover sample drilldown
streamich Jun 3, 2020
b46ab1c
feat: 🎸 use Discover URL generator in example plugin
streamich Jun 3, 2020
f177845
Merge remote-tracking branch 'upstream/master' into discover-url-gene…
streamich Jun 4, 2020
e7c61f1
test: 💍 add urlGenerator mock
streamich Jun 4, 2020
5cc81e8
test: 💍 add .registerUrlGenerator() test mock
streamich Jun 4, 2020
fb47a12
Merge remote-tracking branch 'upstream/master' into discover-url-gene…
streamich Jun 4, 2020
ae8df26
test: 💍 correct Karma mock for "share" plugin URL generator
streamich Jun 5, 2020
4312bef
Merge remote-tracking branch 'upstream/master' into discover-url-gene…
streamich Jun 5, 2020
803dfe9
Merge remote-tracking branch 'upstream/master' into discover-url-gene…
streamich Jun 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/plugins/discover/kibana.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "discover",
"version": "kibana",
"optionalPlugins": ["share"],
"server": true,
"ui": true,
"requiredPlugins": [
Expand Down
1 change: 1 addition & 0 deletions src/plugins/discover/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +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';
3 changes: 3 additions & 0 deletions src/plugins/discover/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const createSetupContract = (): Setup => {
const createStartContract = (): Start => {
const startContract: Start = {
savedSearchLoader: {} as any,
urlGenerator: {
createUrl: jest.fn(),
} as any,
};
return startContract;
};
Expand Down
49 changes: 46 additions & 3 deletions src/plugins/discover/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public';
import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public';
import { ChartsPluginStart } from 'src/plugins/charts/public';
import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
import { SharePluginStart } from 'src/plugins/share/public';
import { SharePluginStart, SharePluginSetup, UrlGeneratorContract } from 'src/plugins/share/public';
import { VisualizationsStart, VisualizationsSetup } from 'src/plugins/visualizations/public';
import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public';
import { HomePublicPluginSetup } from 'src/plugins/home/public';
Expand All @@ -43,7 +43,7 @@ import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../d
import { SavedObjectLoader } from '../../saved_objects/public';
import { createKbnUrlTracker } from '../../kibana_utils/public';
import { DEFAULT_APP_CATEGORIES } from '../../../core/public';

import { UrlGeneratorState } from '../../share/public';
import { DocViewInput, DocViewInputFn } from './application/doc_views/doc_views_types';
import { DocViewsRegistry } from './application/doc_views/doc_views_registry';
import { DocViewTable } from './application/components/table/table';
Expand All @@ -59,6 +59,17 @@ import {
import { createSavedSearchesLoader } from './saved_searches';
import { registerFeature } from './register_feature';
import { buildServices } from './build_services';
import {
DiscoverUrlGeneratorState,
DISCOVER_APP_URL_GENERATOR,
DiscoverUrlGenerator,
} from './url_generator';

declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
[DISCOVER_APP_URL_GENERATOR]: UrlGeneratorState<DiscoverUrlGeneratorState>;
}
}
kertal marked this conversation as resolved.
Show resolved Hide resolved

/**
* @public
Expand All @@ -76,12 +87,31 @@ export interface DiscoverSetup {

export interface DiscoverStart {
savedSearchLoader: SavedObjectLoader;

/**
* `share` plugin URL generator for Discover app. Use it to generate links into
* Discover application, example:
*
* ```ts
* const url = await plugins.discover.urlGenerator.createUrl({
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
* timeRange: {
* to: 'now',
* from: 'now-15m',
* mode: 'relative',
* },
* });
* ```
*/
readonly urlGenerator: undefined | UrlGeneratorContract<'DISCOVER_APP_URL_GENERATOR'>;
}

/**
* @internal
*/
export interface DiscoverSetupPlugins {
share?: SharePluginSetup;
uiActions: UiActionsSetup;
embeddable: EmbeddableSetup;
kibanaLegacy: KibanaLegacySetup;
Expand Down Expand Up @@ -122,6 +152,7 @@ export class DiscoverPlugin
private stopUrlTracking: (() => void) | undefined = undefined;
private servicesInitialized: boolean = false;
private innerAngularInitialized: boolean = false;
private urlGenerator?: DiscoverStart['urlGenerator'];

/**
* why are those functions public? they are needed for some mocha tests
Expand All @@ -131,6 +162,17 @@ export class DiscoverPlugin
public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>;

setup(core: CoreSetup<DiscoverStartPlugins, DiscoverStart>, plugins: DiscoverSetupPlugins) {
const baseUrl = core.http.basePath.prepend('/app/discover');

if (plugins.share) {
this.urlGenerator = plugins.share.urlGenerators.registerUrlGenerator(
new DiscoverUrlGenerator({
appBasePath: baseUrl,
useHash: core.uiSettings.get('state:storeInSessionStorage'),
})
);
}

this.docViewsRegistry = new DocViewsRegistry();
setDocViewsRegistry(this.docViewsRegistry);
this.docViewsRegistry.addDocView({
Expand Down Expand Up @@ -158,7 +200,7 @@ export class DiscoverPlugin
// so history is lazily created (when app is mounted)
// this prevents redundant `#` when not in discover app
getHistory: getScopedHistory,
baseUrl: core.http.basePath.prepend('/app/discover'),
baseUrl,
defaultSubUrl: '#/',
storageKey: `lastUrl:${core.http.basePath.get()}:discover`,
navLinkUpdater$: this.appStateUpdater,
Expand Down Expand Up @@ -266,6 +308,7 @@ export class DiscoverPlugin
};

return {
urlGenerator: this.urlGenerator,
savedSearchLoader: createSavedSearchesLoader({
savedObjectsClient: core.savedObjects.client,
indexPatterns: plugins.data.indexPatterns,
Expand Down
259 changes: 259 additions & 0 deletions src/plugins/discover/public/url_generator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
* 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 { DiscoverUrlGenerator } from './url_generator';
import { hashedItemStore, getStatesFromKbnUrl } from '../../kibana_utils/public';
// eslint-disable-next-line
import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock';
import { FilterStateStore } from '../../data/common';

const appBasePath: string = 'xyz/app/discover';
const indexPatternId: string = 'c367b774-a4c2-11ea-bb37-0242ac130002';
const savedSearchId: string = '571aaf70-4c88-11e8-b3d7-01146121b73d';

interface SetupParams {
useHash?: boolean;
}

const setup = async ({ useHash = false }: SetupParams = {}) => {
const generator = new DiscoverUrlGenerator({
appBasePath,
useHash,
});

return {
generator,
};
};

beforeEach(() => {
// @ts-ignore
hashedItemStore.storage = mockStorage;
});

describe('Discover url generator', () => {
test('can create a link to Discover with no state and no saved search', async () => {
const { generator } = await setup();
const url = await generator.createUrl({});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(url.startsWith(appBasePath)).toBe(true);
expect(_a).toEqual({});
expect(_g).toEqual({});
});

test('can create a link to a saved search in Discover', async () => {
const { generator } = await setup();
const url = await generator.createUrl({ savedSearchId });
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(url.startsWith(`${appBasePath}#/${savedSearchId}`)).toBe(true);
expect(_a).toEqual({});
expect(_g).toEqual({});
});

test('can specify specific index pattern', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
indexPatternId,
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({
index: indexPatternId,
});
expect(_g).toEqual({});
});

test('can specify specific time range', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
timeRange: { to: 'now', from: 'now-15m', mode: 'relative' },
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({});
expect(_g).toEqual({
time: {
from: 'now-15m',
mode: 'relative',
to: 'now',
},
});
});

test('can specify query', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
query: {
language: 'kuery',
query: 'foo',
},
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({
query: {
language: 'kuery',
query: 'foo',
},
});
expect(_g).toEqual({});
});

test('can specify local and global filters', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
filters: [
{
meta: {
alias: 'foo',
disabled: false,
negate: false,
},
$state: {
store: FilterStateStore.APP_STATE,
},
},
{
meta: {
alias: 'bar',
disabled: false,
negate: false,
},
$state: {
store: FilterStateStore.GLOBAL_STATE,
},
},
],
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({
filters: [
{
$state: {
store: 'appState',
},
meta: {
alias: 'foo',
disabled: false,
negate: false,
},
},
],
});
expect(_g).toEqual({
filters: [
{
$state: {
store: 'globalState',
},
meta: {
alias: 'bar',
disabled: false,
negate: false,
},
},
],
});
});

test('can set refresh interval', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
refreshInterval: {
pause: false,
value: 666,
},
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({});
expect(_g).toEqual({
refreshInterval: {
pause: false,
value: 666,
},
});
});

test('can set time range', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
timeRange: {
from: 'now-3h',
to: 'now',
},
});
const { _a, _g } = getStatesFromKbnUrl(url, ['_a', '_g']);

expect(_a).toEqual({});
expect(_g).toEqual({
time: {
from: 'now-3h',
to: 'now',
},
});
});

describe('useHash property', () => {
describe('when default useHash is set to false', () => {
test('when using default, sets index pattern ID in the generated URL', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
indexPatternId,
});

expect(url.indexOf(indexPatternId) > -1).toBe(true);
});

test('when enabling useHash, does not set index pattern ID in the generated URL', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
useHash: true,
indexPatternId,
});

expect(url.indexOf(indexPatternId) > -1).toBe(false);
});
});

describe('when default useHash is set to true', () => {
test('when using default, does not set index pattern ID in the generated URL', async () => {
const { generator } = await setup({ useHash: true });
const url = await generator.createUrl({
indexPatternId,
});

expect(url.indexOf(indexPatternId) > -1).toBe(false);
});

test('when disabling useHash, sets index pattern ID in the generated URL', async () => {
const { generator } = await setup();
const url = await generator.createUrl({
useHash: false,
indexPatternId,
});

expect(url.indexOf(indexPatternId) > -1).toBe(true);
});
});
});
});
Loading