From 318a61fe7fe63688deb027828046c8da1e0b79b7 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 8 Sep 2020 15:57:29 +0200 Subject: [PATCH] GS: use the request's basePath when processing server-side result urls (#76747) (#76918) --- x-pack/plugins/global_search/common/types.ts | 4 +-- .../server/services/search_service.test.ts | 6 ++-- .../server/services/search_service.ts | 4 ++- .../server/services/utils.test.ts | 34 +++++++++++++++++++ .../global_search/server/services/utils.ts | 20 +++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/global_search/server/services/utils.test.ts create mode 100644 x-pack/plugins/global_search/server/services/utils.ts diff --git a/x-pack/plugins/global_search/common/types.ts b/x-pack/plugins/global_search/common/types.ts index 26940806a4ecd2..a08ecaf41b2137 100644 --- a/x-pack/plugins/global_search/common/types.ts +++ b/x-pack/plugins/global_search/common/types.ts @@ -51,12 +51,12 @@ export interface GlobalSearchProviderResult { icon?: string; /** * The url associated with this result. - * This can be either an absolute url, a path relative to the basePath, or a structure specifying if the basePath should be prepended. + * This can be either an absolute url, a path relative to the incoming request's basePath, or a structure specifying if the basePath should be prepended. * * @example * `result.url = 'https://kibana-instance:8080/base-path/app/my-app/my-result-type/id';` * `result.url = '/app/my-app/my-result-type/id';` - * `result.url = { path: '/base-path/app/my-app/my-result-type/id', prependBasePath: false };` + * `result.url = { path: '/base-path/s/my-other-space/app/my-app/my-result-type/id', prependBasePath: false };` */ url: GlobalSearchProviderResultUrl; /** the score of the result, from 1 (lowest) to 100 (highest) */ diff --git a/x-pack/plugins/global_search/server/services/search_service.test.ts b/x-pack/plugins/global_search/server/services/search_service.test.ts index fd705b4286680a..2460100a46dbbe 100644 --- a/x-pack/plugins/global_search/server/services/search_service.test.ts +++ b/x-pack/plugins/global_search/server/services/search_service.test.ts @@ -62,8 +62,8 @@ describe('SearchService', () => { beforeEach(() => { service = new SearchService(); - basePath = httpServiceMock.createBasePath(); - basePath.prepend.mockImplementation((path) => `/base-path${path}`); + basePath = httpServiceMock.createBasePath('/base-path'); + basePath.get.mockReturnValue('/base-path/s/space'); coreStart = coreMock.createStart(); licenseChecker = licenseCheckerMock.create(); }); @@ -283,7 +283,7 @@ describe('SearchService', () => { expect(batch.results).toHaveLength(2); expect(batch.results[0]).toEqual({ ...resultA, - url: '/base-path/foo/bar', + url: '/base-path/s/space/foo/bar', }); expect(batch.results[1]).toEqual({ ...resultB, diff --git a/x-pack/plugins/global_search/server/services/search_service.ts b/x-pack/plugins/global_search/server/services/search_service.ts index 12eada2a1385ec..d79f3781c6bec1 100644 --- a/x-pack/plugins/global_search/server/services/search_service.ts +++ b/x-pack/plugins/global_search/server/services/search_service.ts @@ -17,6 +17,7 @@ import { processProviderResult } from '../../common/process_result'; import { GlobalSearchConfigType } from '../config'; import { getContextFactory, GlobalSearchContextFactory } from './context'; import { GlobalSearchResultProvider, GlobalSearchFindOptions } from '../types'; +import { getRequestBasePath } from './utils'; /** @public */ export interface SearchServiceSetup { @@ -132,6 +133,7 @@ export class SearchService { } const context = this.contextFactory!(request); + const basePath = getRequestBasePath(request, this.basePath!); const timeout$ = timer(this.config!.search_timeout.asMilliseconds()).pipe(map(mapToUndefined)); const aborted$ = options.aborted$ ? merge(options.aborted$, timeout$) : timeout$; @@ -143,7 +145,7 @@ export class SearchService { }; const processResult = (result: GlobalSearchProviderResult) => - processProviderResult(result, this.basePath!); + processProviderResult(result, basePath); const providersResults$ = [...this.providers.values()].map((provider) => provider.find(term, providerOptions, context).pipe( diff --git a/x-pack/plugins/global_search/server/services/utils.test.ts b/x-pack/plugins/global_search/server/services/utils.test.ts new file mode 100644 index 00000000000000..232f72818f3307 --- /dev/null +++ b/x-pack/plugins/global_search/server/services/utils.test.ts @@ -0,0 +1,34 @@ +/* + * 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 { httpServiceMock, httpServerMock } from '../../../../../src/core/server/mocks'; +import { getRequestBasePath } from './utils'; + +describe('getRequestBasePath', () => { + let basePath: ReturnType; + let request: ReturnType; + + beforeEach(() => { + basePath = httpServiceMock.createBasePath(); + request = httpServerMock.createKibanaRequest(); + }); + + it('return a IBasePath prepending the request basePath', () => { + basePath.get.mockReturnValue('/base-path/s/my-space'); + const requestBasePath = getRequestBasePath(request, basePath); + + const fullPath = requestBasePath.prepend('/app/dashboard/some-id'); + + expect(fullPath).toBe('/base-path/s/my-space/app/dashboard/some-id'); + + expect(basePath.get).toHaveBeenCalledTimes(1); + expect(basePath.get).toHaveBeenCalledWith(request); + + expect(basePath.prepend).not.toHaveBeenCalled(); + }); +}); + +httpServiceMock.createBasePath(); diff --git a/x-pack/plugins/global_search/server/services/utils.ts b/x-pack/plugins/global_search/server/services/utils.ts new file mode 100644 index 00000000000000..18a01cfbe97578 --- /dev/null +++ b/x-pack/plugins/global_search/server/services/utils.ts @@ -0,0 +1,20 @@ +/* + * 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 type { IBasePath, KibanaRequest } from 'src/core/server'; +import type { IBasePath as BasePathAccessor } from '../../common/utils'; + +export const getRequestBasePath = ( + request: KibanaRequest, + basePath: IBasePath +): BasePathAccessor => { + const requestBasePath = basePath.get(request); + return { + prepend: (path) => { + return `${requestBasePath}/${path}`.replace(/\/{2,}/g, '/'); + }, + }; +};