mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Redirect endpoint compression (#111471)
* add ability to lz-encode redirect url params * move redirect param utils to /common * improve error message * add getRedirectUrl() to locators * wire in version into locator redirect method * make redirect url default to compressed * fix typescript errors * pass throuh initialization context to share plugin * fix test mocks * improve locators mocks usage * fix typescript types * use getRedirectUrl in example plugin * make redirect url options optional * add locator tests * deprecate geturl * load redirect app UI on demand
This commit is contained in:
parent
9303f781b1
commit
708e235457
25 changed files with 297 additions and 123 deletions
|
@ -19,7 +19,7 @@ import { EuiFieldText } from '@elastic/eui';
|
|||
import { EuiPageHeader } from '@elastic/eui';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { AppMountParameters } from '../../../src/core/public';
|
||||
import { formatSearchParams, SharePluginSetup } from '../../../src/plugins/share/public';
|
||||
import { SharePluginSetup } from '../../../src/plugins/share/public';
|
||||
import {
|
||||
HelloLocatorV1Params,
|
||||
HelloLocatorV2Params,
|
||||
|
@ -164,14 +164,7 @@ const ActionsExplorer = ({ share }: Props) => {
|
|||
<EuiLink
|
||||
color={link.version !== '0.0.2' ? 'danger' : 'primary'}
|
||||
data-test-subj="linkToHelloPage"
|
||||
href={
|
||||
'/app/r?' +
|
||||
formatSearchParams({
|
||||
id: 'HELLO_LOCATOR',
|
||||
version: link.version,
|
||||
params: link.params,
|
||||
}).toString()
|
||||
}
|
||||
href={share.url.locators.get('HELLO_LOCATOR')?.getRedirectUrl(link.params)}
|
||||
target="_blank"
|
||||
>
|
||||
through redirect app
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { DiscoverSetup, DiscoverStart } from '.';
|
||||
import { sharePluginMock } from '../../share/public/mocks';
|
||||
|
||||
export type Setup = jest.Mocked<DiscoverSetup>;
|
||||
export type Start = jest.Mocked<DiscoverStart>;
|
||||
|
@ -16,16 +17,7 @@ const createSetupContract = (): Setup => {
|
|||
docViews: {
|
||||
addDocView: jest.fn(),
|
||||
},
|
||||
locator: {
|
||||
getLocation: jest.fn(),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
locator: sharePluginMock.createLocator(),
|
||||
};
|
||||
return setupContract;
|
||||
};
|
||||
|
@ -36,16 +28,7 @@ const createStartContract = (): Start => {
|
|||
urlGenerator: ({
|
||||
createUrl: jest.fn(),
|
||||
} as unknown) as DiscoverStart['urlGenerator'],
|
||||
locator: {
|
||||
getLocation: jest.fn(),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
locator: sharePluginMock.createLocator(),
|
||||
};
|
||||
return startContract;
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { ManagementSetup, ManagementStart, DefinedSections } from '../types';
|
||||
import { ManagementSection } from '../index';
|
||||
import { sharePluginMock } from '../../../share/public/mocks';
|
||||
|
||||
export const createManagementSectionMock = () =>
|
||||
(({
|
||||
|
@ -31,18 +32,12 @@ const createSetupContract = (): ManagementSetup => ({
|
|||
} as unknown) as DefinedSections,
|
||||
},
|
||||
locator: {
|
||||
...sharePluginMock.createLocator(),
|
||||
getLocation: jest.fn(async () => ({
|
||||
app: 'MANAGEMENT',
|
||||
path: '',
|
||||
state: {},
|
||||
})),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { LocatorDefinition, LocatorPublic, useLocatorUrl } from './url_service';
|
||||
export { LocatorDefinition, LocatorPublic, useLocatorUrl, formatSearchParams } from './url_service';
|
||||
|
|
|
@ -9,4 +9,5 @@
|
|||
export * from './types';
|
||||
export * from './locator';
|
||||
export * from './locator_client';
|
||||
export * from './redirect';
|
||||
export { useLocatorUrl } from './use_locator_url';
|
||||
|
|
115
src/plugins/share/common/url_service/locators/locator.test.ts
Normal file
115
src/plugins/share/common/url_service/locators/locator.test.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { LocatorDefinition } from './types';
|
||||
import { Locator, LocatorDependencies } from './locator';
|
||||
import { KibanaLocation } from 'src/plugins/share/public';
|
||||
import { LocatorGetUrlParams } from '.';
|
||||
import { decompressFromBase64 } from 'lz-string';
|
||||
|
||||
const setup = () => {
|
||||
const baseUrl = 'http://localhost:5601';
|
||||
const version = '1.2.3';
|
||||
const deps: LocatorDependencies = {
|
||||
baseUrl,
|
||||
version,
|
||||
navigate: jest.fn(),
|
||||
getUrl: jest.fn(async (location: KibanaLocation, getUrlParams: LocatorGetUrlParams) => {
|
||||
return (getUrlParams.absolute ? baseUrl : '') + '/app/' + location.app + location.path;
|
||||
}),
|
||||
};
|
||||
const definition: LocatorDefinition<{ foo?: string; baz?: string }> = {
|
||||
id: 'TEST_LOCATOR',
|
||||
getLocation: jest.fn(async ({ foo = 'bar', baz = 'qux' }) => {
|
||||
return {
|
||||
app: 'test_app',
|
||||
path: `/foo/${foo}?baz=${baz}`,
|
||||
state: {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
const locator = new Locator(definition, deps);
|
||||
|
||||
return { baseUrl, version, deps, definition, locator };
|
||||
};
|
||||
|
||||
describe('Locator', () => {
|
||||
test('can create a locator', () => {
|
||||
const { locator } = setup();
|
||||
|
||||
expect(locator).toBeInstanceOf(Locator);
|
||||
expect(locator.definition.id).toBe('TEST_LOCATOR');
|
||||
});
|
||||
|
||||
describe('.getLocation()', () => {
|
||||
test('returns location as defined in definition', async () => {
|
||||
const { locator } = setup();
|
||||
const location = await locator.getLocation({});
|
||||
|
||||
expect(location).toEqual({
|
||||
app: 'test_app',
|
||||
path: '/foo/bar?baz=qux',
|
||||
state: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getUrl()', () => {
|
||||
test('returns URL of the location defined in definition', async () => {
|
||||
const { locator } = setup();
|
||||
const url1 = await locator.getUrl({});
|
||||
const url2 = await locator.getUrl({}, { absolute: true });
|
||||
const url3 = await locator.getUrl({ foo: 'a', baz: 'b' });
|
||||
const url4 = await locator.getUrl({ foo: 'a', baz: 'b' }, { absolute: true });
|
||||
|
||||
expect(url1).toBe('/app/test_app/foo/bar?baz=qux');
|
||||
expect(url2).toBe('http://localhost:5601/app/test_app/foo/bar?baz=qux');
|
||||
expect(url3).toBe('/app/test_app/foo/a?baz=b');
|
||||
expect(url4).toBe('http://localhost:5601/app/test_app/foo/a?baz=b');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getRedirectUrl()', () => {
|
||||
test('returns URL of the redirect endpoint', async () => {
|
||||
const { locator } = setup();
|
||||
const url = await locator.getRedirectUrl({ foo: 'a', baz: 'b' });
|
||||
const params = new URLSearchParams(url.split('?')[1]);
|
||||
|
||||
expect(params.get('l')).toBe('TEST_LOCATOR');
|
||||
expect(params.get('v')).toBe('1.2.3');
|
||||
expect(JSON.parse(decompressFromBase64(params.get('lz')!)!)).toMatchObject({
|
||||
foo: 'a',
|
||||
baz: 'b',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.navigate()', () => {
|
||||
test('returns URL of the redirect endpoint', async () => {
|
||||
const { locator, deps } = setup();
|
||||
|
||||
expect(deps.navigate).toHaveBeenCalledTimes(0);
|
||||
|
||||
await locator.navigate({ foo: 'a', baz: 'b' });
|
||||
|
||||
expect(deps.navigate).toHaveBeenCalledTimes(1);
|
||||
expect(deps.navigate).toHaveBeenCalledWith(
|
||||
await locator.getLocation({ foo: 'a', baz: 'b' }),
|
||||
{ replace: false }
|
||||
);
|
||||
|
||||
await locator.navigate({ foo: 'a2', baz: 'b2' }, { replace: true });
|
||||
|
||||
expect(deps.navigate).toHaveBeenCalledTimes(2);
|
||||
expect(deps.navigate).toHaveBeenCalledWith(
|
||||
await locator.getLocation({ foo: 'a2', baz: 'b2' }),
|
||||
{ replace: true }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -18,8 +18,19 @@ import type {
|
|||
LocatorNavigationParams,
|
||||
LocatorGetUrlParams,
|
||||
} from './types';
|
||||
import { formatSearchParams, FormatSearchParamsOptions, RedirectOptions } from './redirect';
|
||||
|
||||
export interface LocatorDependencies {
|
||||
/**
|
||||
* Public URL of the Kibana server.
|
||||
*/
|
||||
baseUrl?: string;
|
||||
|
||||
/**
|
||||
* Current version of Kibana, e.g. `7.0.0`.
|
||||
*/
|
||||
version?: string;
|
||||
|
||||
/**
|
||||
* Navigate without reloading the page to a KibanaLocation.
|
||||
*/
|
||||
|
@ -76,6 +87,22 @@ export class Locator<P extends SerializableRecord> implements LocatorPublic<P> {
|
|||
return url;
|
||||
}
|
||||
|
||||
public getRedirectUrl(params: P, options: FormatSearchParamsOptions = {}): string {
|
||||
const { baseUrl = '', version = '0.0.0' } = this.deps;
|
||||
const redirectOptions: RedirectOptions = {
|
||||
id: this.definition.id,
|
||||
version,
|
||||
params,
|
||||
};
|
||||
const formatOptions: FormatSearchParamsOptions = {
|
||||
...options,
|
||||
lzCompress: options.lzCompress ?? true,
|
||||
};
|
||||
const search = formatSearchParams(redirectOptions, formatOptions).toString();
|
||||
|
||||
return baseUrl + '/app/r?' + search;
|
||||
}
|
||||
|
||||
public async navigate(
|
||||
params: P,
|
||||
{ replace = false }: LocatorNavigationParams = {}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { decompressFromBase64 } from 'lz-string';
|
||||
import { formatSearchParams } from './format_search_params';
|
||||
import { parseSearchParams } from './parse_search_params';
|
||||
|
||||
|
@ -41,3 +42,23 @@ test('can format and then parse redirect options', () => {
|
|||
|
||||
expect(parsed).toEqual(options);
|
||||
});
|
||||
|
||||
test('compresses params using lz-string when { lzCompress: true } provided', () => {
|
||||
const options = {
|
||||
id: 'LOCATOR_ID',
|
||||
version: '7.21.3',
|
||||
params: {
|
||||
dashboardId: '123',
|
||||
mode: 'edit',
|
||||
},
|
||||
};
|
||||
const formatted = formatSearchParams(options, { lzCompress: true });
|
||||
const search = new URLSearchParams(formatted);
|
||||
const paramsJson = decompressFromBase64(search.get('lz')!)!;
|
||||
|
||||
expect(JSON.parse(paramsJson)).toEqual(options.params);
|
||||
|
||||
const parsed = parseSearchParams(formatted.toString());
|
||||
|
||||
expect(parsed).toEqual(options);
|
||||
});
|
|
@ -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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { compressToBase64 } from 'lz-string';
|
||||
import { RedirectOptions } from './types';
|
||||
|
||||
export interface FormatSearchParamsOptions {
|
||||
lzCompress?: boolean;
|
||||
}
|
||||
|
||||
export function formatSearchParams(
|
||||
opts: RedirectOptions,
|
||||
{ lzCompress }: FormatSearchParamsOptions = {}
|
||||
): URLSearchParams {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
searchParams.set('l', opts.id);
|
||||
searchParams.set('v', opts.version);
|
||||
|
||||
const json = JSON.stringify(opts.params);
|
||||
|
||||
if (lzCompress) {
|
||||
const compressed = compressToBase64(json);
|
||||
searchParams.set('lz', compressed);
|
||||
} else {
|
||||
searchParams.set('p', JSON.stringify(opts.params));
|
||||
}
|
||||
|
||||
return searchParams;
|
||||
}
|
|
@ -6,14 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { RedirectOptions } from '../redirect_manager';
|
||||
|
||||
export function formatSearchParams(opts: RedirectOptions): URLSearchParams {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
searchParams.set('l', opts.id);
|
||||
searchParams.set('v', opts.version);
|
||||
searchParams.set('p', JSON.stringify(opts.params));
|
||||
|
||||
return searchParams;
|
||||
}
|
||||
export * from './types';
|
||||
export * from './format_search_params';
|
||||
export * from './parse_search_params';
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { RedirectOptions } from '../redirect_manager';
|
||||
import { decompressFromBase64 } from 'lz-string';
|
||||
import type { RedirectOptions } from './types';
|
||||
|
||||
/**
|
||||
* Parses redirect endpoint URL path search parameters. Expects them in the
|
||||
|
@ -23,9 +24,11 @@ import type { RedirectOptions } from '../redirect_manager';
|
|||
*/
|
||||
export function parseSearchParams(urlSearch: string): RedirectOptions {
|
||||
const search = new URLSearchParams(urlSearch);
|
||||
|
||||
const id = search.get('l');
|
||||
const version = search.get('v');
|
||||
const paramsJson = search.get('p');
|
||||
const compressed = search.get('lz');
|
||||
const paramsJson: string | null = compressed ? decompressFromBase64(compressed) : search.get('p');
|
||||
|
||||
if (!id) {
|
||||
const message = i18n.translate(
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Serializable locator parameters that can be used by the redirect service to navigate to a
|
||||
* location in Kibana.
|
||||
*
|
||||
* When passed to the public `share` plugin `.navigate()` function, locator params will also
|
||||
* be migrated.
|
||||
*/
|
||||
export interface RedirectOptions<P extends SerializableRecord = unknown & SerializableRecord> {
|
||||
/** Locator ID. */
|
||||
id: string;
|
||||
|
||||
/** Kibana version when locator params were generated. */
|
||||
version: string;
|
||||
|
||||
/** Locator params. */
|
||||
params: P;
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { DependencyList } from 'react';
|
||||
import { PersistableState } from 'src/plugins/kibana_utils/common';
|
||||
import type { FormatSearchParamsOptions } from './redirect';
|
||||
|
||||
/**
|
||||
* URL locator registry.
|
||||
|
@ -62,11 +63,24 @@ export interface LocatorPublic<P extends SerializableRecord> extends Persistable
|
|||
/**
|
||||
* Returns a URL as a string.
|
||||
*
|
||||
* @deprecated Use `getRedirectUrl` instead. `getRedirectUrl` will preserve
|
||||
* the location state, whereas the `getUrl` just return the URL without
|
||||
* the location state.
|
||||
*
|
||||
* @param params URL locator parameters.
|
||||
* @param getUrlParams URL construction parameters.
|
||||
*/
|
||||
getUrl(params: P, getUrlParams?: LocatorGetUrlParams): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns a URL to the redirect endpoint, which will redirect the user to
|
||||
* the final destination.
|
||||
*
|
||||
* @param params URL locator parameters.
|
||||
* @param options URL serialization options.
|
||||
*/
|
||||
getRedirectUrl(params: P, options?: FormatSearchParamsOptions): string;
|
||||
|
||||
/**
|
||||
* Navigate using the `core.application.navigateToApp()` method to a Kibana
|
||||
* location generated by this locator. This method is available only on the
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { PluginInitializerContext } from 'src/core/public';
|
||||
|
||||
export { CSV_QUOTE_VALUES_SETTING, CSV_SEPARATOR_SETTING } from '../common/constants';
|
||||
|
||||
export { LocatorDefinition, LocatorPublic, KibanaLocation } from '../common/url_service';
|
||||
export { parseSearchParams, formatSearchParams } from './url_service';
|
||||
|
||||
export { UrlGeneratorStateMapping } from './url_generators/url_generator_definition';
|
||||
|
||||
|
@ -31,7 +32,7 @@ export {
|
|||
UrlGeneratorsService,
|
||||
} from './url_generators';
|
||||
|
||||
export { RedirectOptions } from './url_service';
|
||||
export { RedirectOptions } from '../common/url_service';
|
||||
export { useLocatorUrl } from '../common/url_service/locators/use_locator_url';
|
||||
|
||||
import { SharePlugin } from './plugin';
|
||||
|
@ -40,4 +41,6 @@ export { KibanaURL } from './kibana_url';
|
|||
export { downloadMultipleAs, downloadFileAs } from './lib/download_as';
|
||||
export type { DownloadableContent } from './lib/download_as';
|
||||
|
||||
export const plugin = () => new SharePlugin();
|
||||
export function plugin(ctx: PluginInitializerContext) {
|
||||
return new SharePlugin(ctx);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ const createLocator = <T extends SerializableRecord = SerializableRecord>(): jes
|
|||
> => ({
|
||||
getLocation: jest.fn(),
|
||||
getUrl: jest.fn(),
|
||||
getRedirectUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
|
|
|
@ -25,7 +25,10 @@ describe('SharePlugin', () => {
|
|||
const plugins = {
|
||||
securityOss: mockSecurityOssPlugin.createSetup(),
|
||||
};
|
||||
const setup = await new SharePlugin().setup(coreSetup, plugins);
|
||||
const setup = await new SharePlugin(coreMock.createPluginInitializerContext()).setup(
|
||||
coreSetup,
|
||||
plugins
|
||||
);
|
||||
expect(registryMock.setup).toHaveBeenCalledWith();
|
||||
expect(setup.register).toBeDefined();
|
||||
});
|
||||
|
@ -35,7 +38,7 @@ describe('SharePlugin', () => {
|
|||
const plugins = {
|
||||
securityOss: mockSecurityOssPlugin.createSetup(),
|
||||
};
|
||||
await new SharePlugin().setup(coreSetup, plugins);
|
||||
await new SharePlugin(coreMock.createPluginInitializerContext()).setup(coreSetup, plugins);
|
||||
expect(coreSetup.application.register).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'short_url_redirect',
|
||||
|
@ -50,7 +53,7 @@ describe('SharePlugin', () => {
|
|||
const pluginsSetup = {
|
||||
securityOss: mockSecurityOssPlugin.createSetup(),
|
||||
};
|
||||
const service = new SharePlugin();
|
||||
const service = new SharePlugin(coreMock.createPluginInitializerContext());
|
||||
await service.setup(coreSetup, pluginsSetup);
|
||||
const pluginsStart = {
|
||||
securityOss: mockSecurityOssPlugin.createStart(),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import './index.scss';
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
|
||||
import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public';
|
||||
import { ShareMenuManager, ShareMenuManagerStart } from './services';
|
||||
import type { SecurityOssPluginSetup, SecurityOssPluginStart } from '../../security_oss/public';
|
||||
import { ShareMenuRegistry, ShareMenuRegistrySetup } from './services';
|
||||
|
@ -19,7 +19,8 @@ import {
|
|||
UrlGeneratorsStart,
|
||||
} from './url_generators/url_generator_service';
|
||||
import { UrlService } from '../common/url_service';
|
||||
import { RedirectManager, RedirectOptions } from './url_service';
|
||||
import { RedirectManager } from './url_service';
|
||||
import type { RedirectOptions } from '../common/url_service/locators/redirect';
|
||||
|
||||
export interface ShareSetupDependencies {
|
||||
securityOss?: SecurityOssPluginSetup;
|
||||
|
@ -79,10 +80,17 @@ export class SharePlugin implements Plugin<SharePluginSetup, SharePluginStart> {
|
|||
private redirectManager?: RedirectManager;
|
||||
private url?: UrlService;
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {}
|
||||
|
||||
public setup(core: CoreSetup, plugins: ShareSetupDependencies): SharePluginSetup {
|
||||
core.application.register(createShortUrlRedirectApp(core, window.location));
|
||||
const { application, http } = core;
|
||||
const { basePath } = http;
|
||||
|
||||
application.register(createShortUrlRedirectApp(core, window.location));
|
||||
|
||||
this.url = new UrlService({
|
||||
baseUrl: basePath.publicBaseUrl || basePath.serverBasePath,
|
||||
version: this.initializerContext.env.packageInfo.version,
|
||||
navigate: async ({ app, path, state }, { replace = false } = {}) => {
|
||||
const [start] = await core.getStartServices();
|
||||
await start.application.navigateToApp(app, {
|
||||
|
|
|
@ -7,5 +7,3 @@
|
|||
*/
|
||||
|
||||
export * from './redirect_manager';
|
||||
export { formatSearchParams } from './util/format_search_params';
|
||||
export { parseSearchParams } from './util/parse_search_params';
|
||||
|
|
|
@ -9,30 +9,9 @@
|
|||
import type { CoreSetup } from 'src/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { migrateToLatest } from '../../../../kibana_utils/common';
|
||||
import type { UrlService } from '../../../common/url_service';
|
||||
import { render } from './render';
|
||||
import { parseSearchParams } from './util/parse_search_params';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Serializable locator parameters that can be used by the redirect service to navigate to a location
|
||||
* in Kibana.
|
||||
*
|
||||
* When passed to the public {@link SharePluginSetup['navigate']} function, locator params will also be
|
||||
* migrated.
|
||||
*/
|
||||
export interface RedirectOptions<P extends SerializableRecord = unknown & SerializableRecord> {
|
||||
/** Locator ID. */
|
||||
id: string;
|
||||
|
||||
/** Kibana version when locator params where generated. */
|
||||
version: string;
|
||||
|
||||
/** Locator params. */
|
||||
params: P;
|
||||
}
|
||||
import { parseSearchParams, RedirectOptions } from '../../../common/url_service/locators/redirect';
|
||||
|
||||
export interface RedirectManagerDependencies {
|
||||
url: UrlService;
|
||||
|
@ -48,7 +27,8 @@ export class RedirectManager {
|
|||
id: 'r',
|
||||
title: 'Redirect endpoint',
|
||||
chromeless: true,
|
||||
mount: (params) => {
|
||||
mount: async (params) => {
|
||||
const { render } = await import('./render');
|
||||
const unmount = render(params.element, { manager: this });
|
||||
this.onMount(params.history.location.search);
|
||||
return () => {
|
||||
|
|
|
@ -31,8 +31,10 @@ export class SharePlugin implements Plugin<SharePluginSetup, SharePluginStart> {
|
|||
|
||||
public setup(core: CoreSetup) {
|
||||
this.url = new UrlService({
|
||||
baseUrl: core.http.basePath.publicBaseUrl || core.http.basePath.serverBasePath,
|
||||
version: this.initializerContext.env.packageInfo.version,
|
||||
navigate: async () => {
|
||||
throw new Error('Locator .navigate() currently is not supported on the server.');
|
||||
throw new Error('Locator .navigate() is not supported on the server.');
|
||||
},
|
||||
getUrl: async () => {
|
||||
throw new Error('Locator .getUrl() currently is not supported on the server.');
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
import { ViewMode } from '../../../../../../src/plugins/embeddable/public';
|
||||
import { Filter, RangeFilter } from '../../../../../../src/plugins/data/public';
|
||||
import { DiscoverAppLocator } from '../../../../../../src/plugins/discover/public';
|
||||
import { sharePluginMock } from '../../../../../../src/plugins/share/public/mocks';
|
||||
|
||||
const i18nTranslateSpy = (i18n.translate as unknown) as jest.SpyInstance;
|
||||
|
||||
|
@ -43,6 +44,7 @@ const setup = (
|
|||
) => {
|
||||
const core = coreMock.createStart();
|
||||
const locator: DiscoverAppLocator = {
|
||||
...sharePluginMock.createLocator(),
|
||||
getLocation: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
app: 'discover',
|
||||
|
@ -50,13 +52,6 @@ const setup = (
|
|||
state: {},
|
||||
})
|
||||
),
|
||||
navigate: jest.fn(async () => {}),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
};
|
||||
|
||||
const plugins: PluginDeps = {
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from '../../../../../../src/plugins/visualizations/public';
|
||||
import { ViewMode } from '../../../../../../src/plugins/embeddable/public';
|
||||
import { DiscoverAppLocator } from '../../../../../../src/plugins/discover/public';
|
||||
import { sharePluginMock } from '../../../../../../src/plugins/share/public/mocks';
|
||||
|
||||
const i18nTranslateSpy = (i18n.translate as unknown) as jest.SpyInstance;
|
||||
|
||||
|
@ -31,6 +32,7 @@ afterEach(() => {
|
|||
const setup = () => {
|
||||
const core = coreMock.createStart();
|
||||
const locator: DiscoverAppLocator = {
|
||||
...sharePluginMock.createLocator(),
|
||||
getLocation: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
app: 'discover',
|
||||
|
@ -38,13 +40,6 @@ const setup = () => {
|
|||
state: {},
|
||||
})
|
||||
),
|
||||
navigate: jest.fn(async () => {}),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
};
|
||||
|
||||
const plugins: PluginDeps = {
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
|
||||
import { ManagementAppLocatorDefinition } from 'src/plugins/management/common/locator';
|
||||
import { IngestPipelinesLocatorDefinition, INGEST_PIPELINES_PAGES } from './locator';
|
||||
import { sharePluginMock } from '../../../../src/plugins/share/public/mocks';
|
||||
|
||||
describe('Ingest pipeline locator', () => {
|
||||
const setup = () => {
|
||||
const managementDefinition = new ManagementAppLocatorDefinition();
|
||||
const definition = new IngestPipelinesLocatorDefinition({
|
||||
managementAppLocator: {
|
||||
...sharePluginMock.createLocator(),
|
||||
getLocation: (params) => managementDefinition.getLocation(params),
|
||||
getUrl: async () => {
|
||||
throw new Error('not implemented');
|
||||
|
@ -21,10 +23,6 @@ describe('Ingest pipeline locator', () => {
|
|||
throw new Error('not implemented');
|
||||
},
|
||||
useUrl: () => '',
|
||||
telemetry: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
});
|
||||
return { definition };
|
||||
|
|
|
@ -6,33 +6,17 @@
|
|||
*/
|
||||
|
||||
import { MlPluginSetup, MlPluginStart } from './plugin';
|
||||
import { sharePluginMock } from '../../../../src/plugins/share/public/mocks';
|
||||
|
||||
const createSetupContract = (): jest.Mocked<MlPluginSetup> => {
|
||||
return {
|
||||
locator: {
|
||||
getLocation: jest.fn(),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
locator: sharePluginMock.createLocator(),
|
||||
};
|
||||
};
|
||||
|
||||
const createStartContract = (): jest.Mocked<MlPluginStart> => {
|
||||
return {
|
||||
locator: {
|
||||
getLocation: jest.fn(),
|
||||
getUrl: jest.fn(),
|
||||
useUrl: jest.fn(),
|
||||
navigate: jest.fn(),
|
||||
extract: jest.fn(),
|
||||
inject: jest.fn(),
|
||||
telemetry: jest.fn(),
|
||||
migrations: {},
|
||||
},
|
||||
locator: sharePluginMock.createLocator(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue