From be2c6b79a5fa0303dc893cbd5abe524c9fc21615 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 16 Jan 2020 11:50:11 +0100 Subject: [PATCH 1/2] remove old API and implement NP redirect app --- .../state_session_storage_redirect/index.js | 32 ------- .../package.json | 5 -- src/legacy/server/kbn_server.js | 4 - src/legacy/server/url_shortening/index.js | 20 ----- .../routes/lib/short_url_assert_valid.js | 41 --------- .../routes/lib/short_url_assert_valid.test.js | 63 -------------- .../routes/lib/short_url_error.js | 26 ------ .../routes/lib/short_url_error.test.js | 67 --------------- .../routes/lib/short_url_lookup.js | 43 ---------- .../routes/lib/short_url_lookup.test.js | 84 ------------------- .../url_shortening/url_shortening_mixin.js | 23 ----- src/plugins/newsfeed/public/plugin.tsx | 22 ++--- .../share/common/short_url_routes.ts} | 16 ++-- src/plugins/share/public/lib/url_shortener.ts | 5 +- src/plugins/share/public/plugin.test.ts | 17 +++- src/plugins/share/public/plugin.ts | 6 +- .../services/short_url_redirect_app.test.ts | 46 ++++++++++ .../services/short_url_redirect_app.ts} | 39 +++++---- .../share/server/routes/create_routes.ts | 2 + .../share/server/routes/get.ts} | 47 +++++++---- src/plugins/share/server/routes/goto.ts | 10 ++- .../share/server/routes/shorten_url.ts | 3 +- 22 files changed, 155 insertions(+), 466 deletions(-) delete mode 100644 src/legacy/core_plugins/state_session_storage_redirect/index.js delete mode 100644 src/legacy/core_plugins/state_session_storage_redirect/package.json delete mode 100644 src/legacy/server/url_shortening/index.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_error.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_error.test.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_lookup.js delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js delete mode 100644 src/legacy/server/url_shortening/url_shortening_mixin.js rename src/{legacy/server/url_shortening/routes/create_routes.js => plugins/share/common/short_url_routes.ts} (67%) create mode 100644 src/plugins/share/public/services/short_url_redirect_app.test.ts rename src/{legacy/core_plugins/state_session_storage_redirect/public/index.js => plugins/share/public/services/short_url_redirect_app.ts} (50%) rename src/{legacy/server/url_shortening/routes/goto.js => plugins/share/server/routes/get.ts} (55%) diff --git a/src/legacy/core_plugins/state_session_storage_redirect/index.js b/src/legacy/core_plugins/state_session_storage_redirect/index.js deleted file mode 100644 index 2d4d7c97232c0..0000000000000 --- a/src/legacy/core_plugins/state_session_storage_redirect/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 default function(kibana) { - return new kibana.Plugin({ - uiExports: { - app: { - require: ['kibana'], - title: 'Redirecting', - id: 'stateSessionStorageRedirect', - main: 'plugins/state_session_storage_redirect', - hidden: true, - }, - }, - }); -} diff --git a/src/legacy/core_plugins/state_session_storage_redirect/package.json b/src/legacy/core_plugins/state_session_storage_redirect/package.json deleted file mode 100644 index 21956e5d76d5b..0000000000000 --- a/src/legacy/core_plugins/state_session_storage_redirect/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "state_session_storage_redirect", - "version": "kibana", - "description": "When using the state:storeInSessionStorage setting with the short-urls, we need some way to get the full URL's hashed states into sessionStorage, this app will grab the URL from the injected state and and put the URL hashed states into sessionStorage before redirecting the user." -} diff --git a/src/legacy/server/kbn_server.js b/src/legacy/server/kbn_server.js index 8ab55d4c517b5..6991527a9503c 100644 --- a/src/legacy/server/kbn_server.js +++ b/src/legacy/server/kbn_server.js @@ -36,7 +36,6 @@ import * as Plugins from './plugins'; import { indexPatternsMixin } from './index_patterns'; import { savedObjectsMixin } from './saved_objects/saved_objects_mixin'; import { capabilitiesMixin } from './capabilities'; -import { urlShorteningMixin } from './url_shortening'; import { serverExtensionsMixin } from './server_extensions'; import { uiMixin } from '../ui'; import { sassMixin } from './sass'; @@ -123,9 +122,6 @@ export default class KbnServer { // setup capabilities routes capabilitiesMixin, - // setup routes for short urls - urlShorteningMixin, - // ensure that all bundles are built, or that the // watch bundle server is running optimizeMixin, diff --git a/src/legacy/server/url_shortening/index.js b/src/legacy/server/url_shortening/index.js deleted file mode 100644 index 031af0618d7bc..0000000000000 --- a/src/legacy/server/url_shortening/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 { urlShorteningMixin } from './url_shortening_mixin'; diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js b/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js deleted file mode 100644 index ff2b0f214e5ee..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 { parse } from 'url'; -import { trim } from 'lodash'; -import Boom from 'boom'; - -export function shortUrlAssertValid(url) { - const { protocol, hostname, pathname } = parse(url); - - if (protocol) { - throw Boom.notAcceptable(`Short url targets cannot have a protocol, found "${protocol}"`); - } - - if (hostname) { - throw Boom.notAcceptable(`Short url targets cannot have a hostname, found "${hostname}"`); - } - - const pathnameParts = trim(pathname, '/').split('/'); - if (pathnameParts.length !== 2) { - throw Boom.notAcceptable( - `Short url target path must be in the format "/app/{{appId}}", found "${pathname}"` - ); - } -} diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js deleted file mode 100644 index f83073e6aefe9..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 { shortUrlAssertValid } from './short_url_assert_valid'; - -describe('shortUrlAssertValid()', () => { - const invalid = [ - ['protocol', 'http://localhost:5601/app/kibana'], - ['protocol', 'https://localhost:5601/app/kibana'], - ['protocol', 'mailto:foo@bar.net'], - ['protocol', 'javascript:alert("hi")'], // eslint-disable-line no-script-url - ['hostname', 'localhost/app/kibana'], - ['hostname and port', 'local.host:5601/app/kibana'], - ['hostname and auth', 'user:pass@localhost.net/app/kibana'], - ['path traversal', '/app/../../not-kibana'], - ['deep path', '/app/kibana/foo'], - ['deep path', '/app/kibana/foo/bar'], - ['base path', '/base/app/kibana'], - ]; - - invalid.forEach(([desc, url]) => { - it(`fails when url has ${desc}`, () => { - try { - shortUrlAssertValid(url); - throw new Error(`expected assertion to throw`); - } catch (err) { - if (!err || !err.isBoom) { - throw err; - } - } - }); - }); - - const valid = [ - '/app/kibana', - '/app/monitoring#angular/route', - '/app/text#document-id', - '/app/some?with=query', - '/app/some?with=query#and-a-hash', - ]; - - valid.forEach(url => { - it(`allows ${url}`, () => { - shortUrlAssertValid(url); - }); - }); -}); diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_error.js b/src/legacy/server/url_shortening/routes/lib/short_url_error.js deleted file mode 100644 index ed44ba21aa4c4..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_error.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 Boom from 'boom'; - -export function handleShortUrlError(error) { - return Boom.boomify(error, { - statusCode: error.statusCode || error.status || 500, - }); -} diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js deleted file mode 100644 index 4eca6320ec834..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 _ from 'lodash'; -import { handleShortUrlError } from './short_url_error'; - -function createErrorWithStatus(status) { - const error = new Error(); - error.status = status; - return error; -} - -function createErrorWithStatusCode(statusCode) { - const error = new Error(); - error.statusCode = statusCode; - return error; -} - -describe('handleShortUrlError()', () => { - const caughtErrorsWithStatus = [ - createErrorWithStatus(401), - createErrorWithStatus(403), - createErrorWithStatus(404), - ]; - - const caughtErrorsWithStatusCode = [ - createErrorWithStatusCode(401), - createErrorWithStatusCode(403), - createErrorWithStatusCode(404), - ]; - - const uncaughtErrors = [new Error(), createErrorWithStatus(500), createErrorWithStatusCode(500)]; - - caughtErrorsWithStatus.forEach(err => { - it(`should handle errors with status of ${err.status}`, function() { - expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.status); - }); - }); - - caughtErrorsWithStatusCode.forEach(err => { - it(`should handle errors with statusCode of ${err.statusCode}`, function() { - expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.statusCode); - }); - }); - - uncaughtErrors.forEach(err => { - it(`should not handle unknown errors`, function() { - expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(500); - }); - }); -}); diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js b/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js deleted file mode 100644 index a8a01d1433a7a..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 { get } from 'lodash'; - -export function shortUrlLookupProvider(server) { - async function updateMetadata(doc, req) { - try { - await req.getSavedObjectsClient().update('url', doc.id, { - accessDate: new Date(), - accessCount: get(doc, 'attributes.accessCount', 0) + 1, - }); - } catch (err) { - server.log('Warning: Error updating url metadata', err); - //swallow errors. It isn't critical if there is no update. - } - } - - return { - async getUrl(id, req) { - const doc = await req.getSavedObjectsClient().get('url', id); - updateMetadata(doc, req); - - return doc.attributes.url; - }, - }; -} diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js deleted file mode 100644 index e8bf72a142d11..0000000000000 --- a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 sinon from 'sinon'; -import { shortUrlLookupProvider } from './short_url_lookup'; -import { SavedObjectsClient } from '../../../../../core/server'; - -describe('shortUrlLookupProvider', () => { - const ID = 'bf00ad16941fc51420f91a93428b27a0'; - const TYPE = 'url'; - const URL = 'http://elastic.co'; - const server = { log: sinon.stub() }; - const sandbox = sinon.createSandbox(); - - let savedObjectsClient; - let req; - let shortUrl; - - beforeEach(() => { - savedObjectsClient = { - get: sandbox.stub(), - create: sandbox.stub().returns(Promise.resolve({ id: ID })), - update: sandbox.stub(), - errors: SavedObjectsClient.errors, - }; - - req = { getSavedObjectsClient: () => savedObjectsClient }; - shortUrl = shortUrlLookupProvider(server); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe('getUrl', () => { - beforeEach(() => { - const attributes = { accessCount: 2, url: URL }; - savedObjectsClient.get.returns({ id: ID, attributes }); - }); - - it('provides the ID to savedObjectsClient', async () => { - await shortUrl.getUrl(ID, req); - - sinon.assert.calledOnce(savedObjectsClient.get); - const [type, id] = savedObjectsClient.get.getCall(0).args; - - expect(type).toEqual(TYPE); - expect(id).toEqual(ID); - }); - - it('returns the url', async () => { - const response = await shortUrl.getUrl(ID, req); - expect(response).toEqual(URL); - }); - - it('increments accessCount', async () => { - await shortUrl.getUrl(ID, req); - - sinon.assert.calledOnce(savedObjectsClient.update); - const [type, id, attributes] = savedObjectsClient.update.getCall(0).args; - - expect(type).toEqual(TYPE); - expect(id).toEqual(ID); - expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate']); - expect(attributes.accessCount).toEqual(3); - }); - }); -}); diff --git a/src/legacy/server/url_shortening/url_shortening_mixin.js b/src/legacy/server/url_shortening/url_shortening_mixin.js deleted file mode 100644 index 867898cac845a..0000000000000 --- a/src/legacy/server/url_shortening/url_shortening_mixin.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 { createRoutes } from './routes/create_routes'; - -export function urlShorteningMixin(kbnServer, server) { - createRoutes(server); -} diff --git a/src/plugins/newsfeed/public/plugin.tsx b/src/plugins/newsfeed/public/plugin.tsx index 5ea5e5b324717..2877c63435d4b 100644 --- a/src/plugins/newsfeed/public/plugin.tsx +++ b/src/plugins/newsfeed/public/plugin.tsx @@ -23,9 +23,10 @@ import ReactDOM from 'react-dom'; import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { NewsfeedPluginInjectedConfig } from '../types'; +import { FetchResult, NewsfeedPluginInjectedConfig } from '../types'; import { NewsfeedNavButton, NewsfeedApiFetchResult } from './components/newsfeed_header_nav_button'; import { getApi } from './lib/api'; +import { Observable } from 'rxjs'; export type Setup = void; export type Start = void; @@ -53,15 +54,16 @@ export class NewsfeedPublicPlugin implements Plugin { } private fetchNewsfeed(core: CoreStart) { - const { http, injectedMetadata } = core; - const config = injectedMetadata.getInjectedVar( - 'newsfeed' - ) as NewsfeedPluginInjectedConfig['newsfeed']; - - return getApi(http, config, this.kibanaVersion).pipe( - takeUntil(this.stop$), // stop the interval when stop method is called - catchError(() => Rx.of(null)) // do not throw error - ); + // const { http, injectedMetadata } = core; + // const config = injectedMetadata.getInjectedVar( + // 'newsfeed' + // ) as NewsfeedPluginInjectedConfig['newsfeed']; + // + return new Observable(); + // return getApi(http, config, this.kibanaVersion).pipe( + // takeUntil(this.stop$), // stop the interval when stop method is called + // catchError(() => Rx.of(null)) // do not throw error + // ); } private mount(api$: NewsfeedApiFetchResult, targetDomElement: HTMLElement) { diff --git a/src/legacy/server/url_shortening/routes/create_routes.js b/src/plugins/share/common/short_url_routes.ts similarity index 67% rename from src/legacy/server/url_shortening/routes/create_routes.js rename to src/plugins/share/common/short_url_routes.ts index 9540e7441a268..7b42534de2ab1 100644 --- a/src/legacy/server/url_shortening/routes/create_routes.js +++ b/src/plugins/share/common/short_url_routes.ts @@ -17,11 +17,15 @@ * under the License. */ -import { shortUrlLookupProvider } from './lib/short_url_lookup'; -import { createGotoRoute } from './goto'; +export const GOTO_PREFIX = '/goto'; -export function createRoutes(server) { - const shortUrlLookup = shortUrlLookupProvider(server); +export const getUrlIdFromGotoRoute = (path: string) => + path.match(new RegExp(`${GOTO_PREFIX}/(.*)$`))?.[1]; - server.route(createGotoRoute({ server, shortUrlLookup })); -} +export const getGotoPath = (urlId: string) => `${GOTO_PREFIX}/${urlId}`; + +export const GETTER_PREFIX = '/api/short_url'; + +export const getUrlPath = (urlId: string) => `${GETTER_PREFIX}/${urlId}`; + +export const CREATE_PATH = '/api/shorten_url'; diff --git a/src/plugins/share/public/lib/url_shortener.ts b/src/plugins/share/public/lib/url_shortener.ts index 29d91bdb1aae6..f2259f1fabf7d 100644 --- a/src/plugins/share/public/lib/url_shortener.ts +++ b/src/plugins/share/public/lib/url_shortener.ts @@ -19,6 +19,7 @@ import url from 'url'; import { HttpStart } from 'kibana/public'; +import { CREATE_PATH, getGotoPath } from '../../common/short_url_routes'; export async function shortenUrl( absoluteUrl: string, @@ -34,10 +35,10 @@ export async function shortenUrl( const body = JSON.stringify({ url: relativeUrl }); - const resp = await post('/api/shorten_url', { body }); + const resp = await post(CREATE_PATH, { body }); return url.format({ protocol: parsedUrl.protocol, host: parsedUrl.host, - pathname: `${basePath}/goto/${resp.urlId}`, + pathname: `${basePath}${getGotoPath(resp.urlId)}`, }); } diff --git a/src/plugins/share/public/plugin.test.ts b/src/plugins/share/public/plugin.test.ts index 5610490be33b3..730814fe9ed23 100644 --- a/src/plugins/share/public/plugin.test.ts +++ b/src/plugins/share/public/plugin.test.ts @@ -20,6 +20,7 @@ import { registryMock, managerMock } from './plugin.test.mocks'; import { SharePlugin } from './plugin'; import { CoreStart } from 'kibana/public'; +import { coreMock } from '../../../core/public/mocks'; describe('SharePlugin', () => { beforeEach(() => { @@ -30,16 +31,28 @@ describe('SharePlugin', () => { describe('setup', () => { test('wires up and returns registry', async () => { - const setup = await new SharePlugin().setup(); + const coreSetup = coreMock.createSetup(); + const setup = await new SharePlugin().setup(coreSetup); expect(registryMock.setup).toHaveBeenCalledWith(); expect(setup.register).toBeDefined(); }); + + test('registers redirect app', async () => { + const coreSetup = coreMock.createSetup(); + await new SharePlugin().setup(coreSetup); + expect(coreSetup.application.register).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'short_url_redirect', + }) + ); + }); }); describe('start', () => { test('wires up and returns show function, but not registry', async () => { + const coreSetup = coreMock.createSetup(); const service = new SharePlugin(); - await service.setup(); + await service.setup(coreSetup); const start = await service.start({} as CoreStart); expect(registryMock.start).toHaveBeenCalled(); expect(managerMock.start).toHaveBeenCalledWith( diff --git a/src/plugins/share/public/plugin.ts b/src/plugins/share/public/plugin.ts index 6d78211cf9954..01c248624950a 100644 --- a/src/plugins/share/public/plugin.ts +++ b/src/plugins/share/public/plugin.ts @@ -17,15 +17,17 @@ * under the License. */ -import { CoreStart, Plugin } from 'src/core/public'; +import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { ShareMenuManager, ShareMenuManagerStart } from './services'; import { ShareMenuRegistry, ShareMenuRegistrySetup } from './services'; +import { createShortUrlRedirectApp } from './services/short_url_redirect_app'; export class SharePlugin implements Plugin { private readonly shareMenuRegistry = new ShareMenuRegistry(); private readonly shareContextMenu = new ShareMenuManager(); - public async setup() { + public async setup(core: CoreSetup) { + core.application.register(createShortUrlRedirectApp(core, window.location)); return { ...this.shareMenuRegistry.setup(), }; diff --git a/src/plugins/share/public/services/short_url_redirect_app.test.ts b/src/plugins/share/public/services/short_url_redirect_app.test.ts new file mode 100644 index 0000000000000..206e637451ec0 --- /dev/null +++ b/src/plugins/share/public/services/short_url_redirect_app.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { createShortUrlRedirectApp } from './short_url_redirect_app'; +import { coreMock } from '../../../../core/public/mocks'; +import { hashUrl } from '../../../kibana_utils/public'; + +jest.mock('../../../kibana_utils/public', () => ({ hashUrl: jest.fn(x => `${x}/hashed`) })); + +describe('short_url_redirect_app', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should fetch url and redirect to hashed version', async () => { + const coreSetup = coreMock.createSetup({ basePath: 'base' }); + coreSetup.http.get.mockResolvedValueOnce({ url: '/app/abc' }); + const locationMock = { pathname: '/base/goto/12345', href: '' } as Location; + + const { mount } = createShortUrlRedirectApp(coreSetup, locationMock); + await mount(); + + // check for fetching the complete URL + expect(coreSetup.http.get).toHaveBeenCalledWith('/api/short_url/12345'); + // check for hashing the URL returned from the server + expect(hashUrl).toHaveBeenCalledWith('/app/abc'); + // check for redirecting to the prepended path + expect(locationMock.href).toEqual('base/app/abc/hashed'); + }); +}); diff --git a/src/legacy/core_plugins/state_session_storage_redirect/public/index.js b/src/plugins/share/public/services/short_url_redirect_app.ts similarity index 50% rename from src/legacy/core_plugins/state_session_storage_redirect/public/index.js rename to src/plugins/share/public/services/short_url_redirect_app.ts index 701a5736c7d3b..6f72b711f6602 100644 --- a/src/legacy/core_plugins/state_session_storage_redirect/public/index.js +++ b/src/plugins/share/public/services/short_url_redirect_app.ts @@ -17,24 +17,29 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { hashUrl } from '../../../../plugins/kibana_utils/public'; -import uiRoutes from 'ui/routes'; -import { fatalError } from 'ui/notify'; +import { CoreSetup } from 'kibana/public'; +import { getUrlIdFromGotoRoute, getUrlPath, GOTO_PREFIX } from '../../common/short_url_routes'; +import { hashUrl } from '../../../kibana_utils/public'; -uiRoutes.enable(); -uiRoutes.when('/', { - resolve: { - url: function(AppState, globalState, $window) { - const redirectUrl = chrome.getInjected('redirectUrl'); - try { - const hashedUrl = hashUrl(redirectUrl); - const url = chrome.addBasePath(hashedUrl); +export const createShortUrlRedirectApp = (core: CoreSetup, location: Location) => ({ + id: 'short_url_redirect', + appRoute: GOTO_PREFIX, + chromeless: true, + title: 'Short URL Redirect', + async mount() { + const urlId = getUrlIdFromGotoRoute(location.pathname); - $window.location = url; - } catch (e) { - fatalError(e); - } - }, + if (!urlId) { + throw new Error('Url id not present in path'); + } + + const response = await core.http.get<{ url: string }>(getUrlPath(urlId)); + const redirectUrl = response.url; + const hashedUrl = hashUrl(redirectUrl); + const url = core.http.basePath.prepend(hashedUrl); + + location.href = url; + + return () => {}; }, }); diff --git a/src/plugins/share/server/routes/create_routes.ts b/src/plugins/share/server/routes/create_routes.ts index bd4b6fdb08791..22d10c25197c9 100644 --- a/src/plugins/share/server/routes/create_routes.ts +++ b/src/plugins/share/server/routes/create_routes.ts @@ -22,11 +22,13 @@ import { CoreSetup, Logger } from 'kibana/server'; import { shortUrlLookupProvider } from './lib/short_url_lookup'; import { createGotoRoute } from './goto'; import { createShortenUrlRoute } from './shorten_url'; +import { createGetterRoute } from './get'; export function createRoutes({ http }: CoreSetup, logger: Logger) { const shortUrlLookup = shortUrlLookupProvider({ logger }); const router = http.createRouter(); createGotoRoute({ router, shortUrlLookup, http }); + createGetterRoute({ router, shortUrlLookup, http }); createShortenUrlRoute({ router, shortUrlLookup }); } diff --git a/src/legacy/server/url_shortening/routes/goto.js b/src/plugins/share/server/routes/get.ts similarity index 55% rename from src/legacy/server/url_shortening/routes/goto.js rename to src/plugins/share/server/routes/get.ts index 7a874786423d8..d6b191341dbe1 100644 --- a/src/legacy/server/url_shortening/routes/goto.js +++ b/src/plugins/share/server/routes/get.ts @@ -17,23 +17,40 @@ * under the License. */ -import { handleShortUrlError } from './lib/short_url_error'; +import { CoreSetup, IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; + import { shortUrlAssertValid } from './lib/short_url_assert_valid'; +import { ShortUrlLookupService } from './lib/short_url_lookup'; +import { getUrlPath } from '../../common/short_url_routes'; -export const createGotoRoute = ({ server, shortUrlLookup }) => ({ - method: 'GET', - path: '/goto_LP/{urlId}', - handler: async function(request, h) { - try { - const url = await shortUrlLookup.getUrl(request.params.urlId, request); +export const createGetterRoute = ({ + router, + shortUrlLookup, + http, +}: { + router: IRouter; + shortUrlLookup: ShortUrlLookupService; + http: CoreSetup['http']; +}) => { + router.get( + { + path: getUrlPath('{urlId}'), + validate: { + params: schema.object({ urlId: schema.string() }), + }, + }, + router.handleLegacyErrors(async function(context, request, response) { + const url = await shortUrlLookup.getUrl(request.params.urlId, { + savedObjects: context.core.savedObjects.client, + }); shortUrlAssertValid(url); - const app = server.getHiddenUiAppById('stateSessionStorageRedirect'); - return h.renderApp(app, { - redirectUrl: url, + return response.ok({ + body: { + url, + }, }); - } catch (err) { - throw handleShortUrlError(err); - } - }, -}); + }) + ); +}; diff --git a/src/plugins/share/server/routes/goto.ts b/src/plugins/share/server/routes/goto.ts index 7343dc1bd34a2..5c3a4e441099f 100644 --- a/src/plugins/share/server/routes/goto.ts +++ b/src/plugins/share/server/routes/goto.ts @@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema'; import { shortUrlAssertValid } from './lib/short_url_assert_valid'; import { ShortUrlLookupService } from './lib/short_url_lookup'; +import { getGotoPath } from '../../common/short_url_routes'; export const createGotoRoute = ({ router, @@ -34,7 +35,7 @@ export const createGotoRoute = ({ }) => { router.get( { - path: '/goto/{urlId}', + path: getGotoPath('{urlId}'), validate: { params: schema.object({ urlId: schema.string() }), }, @@ -54,10 +55,13 @@ export const createGotoRoute = ({ }, }); } - return response.redirected({ + const body = await context.core.rendering.render(); + + return response.ok({ headers: { - location: http.basePath.prepend('/goto_LP/' + request.params.urlId), + 'content-security-policy': http.csp.header, }, + body, }); }) ); diff --git a/src/plugins/share/server/routes/shorten_url.ts b/src/plugins/share/server/routes/shorten_url.ts index 116b90c6971c5..41570f8a5f453 100644 --- a/src/plugins/share/server/routes/shorten_url.ts +++ b/src/plugins/share/server/routes/shorten_url.ts @@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema'; import { shortUrlAssertValid } from './lib/short_url_assert_valid'; import { ShortUrlLookupService } from './lib/short_url_lookup'; +import { CREATE_PATH } from '../../common/short_url_routes'; export const createShortenUrlRoute = ({ shortUrlLookup, @@ -32,7 +33,7 @@ export const createShortenUrlRoute = ({ }) => { router.post( { - path: '/api/shorten_url', + path: CREATE_PATH, validate: { body: schema.object({ url: schema.string() }), }, From 62c88e72e554a7866e375b92e22fc7993b148434 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 16 Jan 2020 11:53:13 +0100 Subject: [PATCH 2/2] implement temporary solution for the newsfeed plugin --- src/plugins/newsfeed/public/plugin.tsx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/plugins/newsfeed/public/plugin.tsx b/src/plugins/newsfeed/public/plugin.tsx index 2877c63435d4b..c4e042fe452f9 100644 --- a/src/plugins/newsfeed/public/plugin.tsx +++ b/src/plugins/newsfeed/public/plugin.tsx @@ -26,7 +26,6 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core import { FetchResult, NewsfeedPluginInjectedConfig } from '../types'; import { NewsfeedNavButton, NewsfeedApiFetchResult } from './components/newsfeed_header_nav_button'; import { getApi } from './lib/api'; -import { Observable } from 'rxjs'; export type Setup = void; export type Start = void; @@ -54,16 +53,19 @@ export class NewsfeedPublicPlugin implements Plugin { } private fetchNewsfeed(core: CoreStart) { - // const { http, injectedMetadata } = core; - // const config = injectedMetadata.getInjectedVar( - // 'newsfeed' - // ) as NewsfeedPluginInjectedConfig['newsfeed']; - // - return new Observable(); - // return getApi(http, config, this.kibanaVersion).pipe( - // takeUntil(this.stop$), // stop the interval when stop method is called - // catchError(() => Rx.of(null)) // do not throw error - // ); + const { http, injectedMetadata } = core; + const config = injectedMetadata.getInjectedVar('newsfeed') as + | NewsfeedPluginInjectedConfig['newsfeed'] + | undefined; + + if (!config) { + // running in new platform, injected metadata not available + return new Rx.Observable(); + } + return getApi(http, config, this.kibanaVersion).pipe( + takeUntil(this.stop$), // stop the interval when stop method is called + catchError(() => Rx.of(null)) // do not throw error + ); } private mount(api$: NewsfeedApiFetchResult, targetDomElement: HTMLElement) {