Move /shorten to /api/shorten_url (#21808) (#21860)

* Move /shorten to /api/shorten_url

* better API test assertions

* add API documenation

* use async await

* import dependencies instead of pass in
This commit is contained in:
Nathan Reese 2018-08-09 17:11:01 -06:00 committed by GitHub
parent d5cd01ab1c
commit 7485f4edab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 426 additions and 215 deletions

View file

@ -29,8 +29,10 @@ entirely.
* <<saved-objects-api>>
* <<logstash-configuration-management-api>>
* <<url-shortening-api>>
--
include::api/saved-objects.asciidoc[]
include::api/logstash-configuration-management.asciidoc[]
include::api/url-shortening.asciidoc[]

View file

@ -0,0 +1,11 @@
[[url-shortening-api]]
== URL Shortening API
Kibana URLs contain the state of the application making them very long and cumbersome.
Internet Explorer has URL length restrictions, and some wiki and markup parsers don't do well with the full-length version of the Kibana URL.
The short URLs enabled by this API are designed to make sharing Kibana URLs easier.
* <<url-shortening-api-api-shorten-url>>
include::url_shortening/shorten_url.asciidoc[]

View file

@ -0,0 +1,46 @@
[[url-shortening-api-api-shorten-url]]
=== Shorten URL
The Shorten URL API allows for converting a Kibana URL into a token.
==== Request
`POST /api/shorten_url`
==== Request Body
The request body must be a JSON object containing the following properties:
`url` (required)::
(string) Kibana URL, relative to `/app/kibana`, to be shortened.
==== Response body
The response body will have a top level `urlId` property that contains
the shortened URL token for the provided request body.
==== Examples
[source,js]
--------------------------------------------------
POST api/shorten_url
{
"url": "/app/kibana#/dashboard?_g=()&_a=(description:'',filters:!(),fullScreenMode:!f,options:(darkTheme:!f,hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(),gridData:(h:15,i:'1',w:24,x:0,y:0),id:'8f4d0c00-4c86-11e8-b3d7-01146121b73d',panelIndex:'1',type:visualization,version:'7.0.0-alpha1')),query:(language:lucene,query:''),timeRestore:!f,title:'New%20Dashboard',viewMode:edit)",
}
--------------------------------------------------
// KIBANA
A successful call returns a response code of `200` and a response body
containing a JSON structure similar to the following example:
[source,js]
--------------------------------------------------
{
"urlId": "f73b295ff92718b26bc94edac766d8e3"
}
--------------------------------------------------
A shortened Kibana URL can then be constructed for easier sharing.
`http://localhost:5601/goto/f73b295ff92718b26bc94edac766d8e3`

View file

@ -24,9 +24,6 @@ import Boom from 'boom';
import Hapi from 'hapi';
import getDefaultRoute from './get_default_route';
import { setupVersionCheck } from './version_check';
import { handleShortUrlError } from './short_url_error';
import { shortUrlAssertValid } from './short_url_assert_valid';
import { shortUrlLookupProvider } from './short_url_lookup';
import { registerHapiPlugins } from './register_hapi_plugins';
import { setupXsrf } from './xsrf';
@ -34,8 +31,6 @@ export default async function (kbnServer, server, config) {
kbnServer.server = new Hapi.Server();
server = kbnServer.server;
const shortUrlLookup = shortUrlLookupProvider(server);
// Note that all connection options configured here should be exactly the same
// as in `getServerOptions()` in the new platform (see `src/core/server/http/http_tools`).
//
@ -144,45 +139,6 @@ export default async function (kbnServer, server, config) {
}
});
server.route({
method: 'GET',
path: '/goto/{urlId}',
handler: async function (request, reply) {
try {
const url = await shortUrlLookup.getUrl(request.params.urlId, request);
shortUrlAssertValid(url);
const uiSettings = request.getUiSettingsService();
const stateStoreInSessionStorage = await uiSettings.get('state:storeInSessionStorage');
if (!stateStoreInSessionStorage) {
reply().redirect(config.get('server.basePath') + url);
return;
}
const app = server.getHiddenUiAppById('stateSessionStorageRedirect');
reply.renderApp(app, {
redirectUrl: url,
});
} catch (err) {
reply(handleShortUrlError(err));
}
}
});
server.route({
method: 'POST',
path: '/shorten',
handler: async function (request, reply) {
try {
shortUrlAssertValid(request.payload.url);
const urlId = await shortUrlLookup.generateUrlId(request.payload.url, request);
reply(urlId);
} catch (err) {
reply(handleShortUrlError(err));
}
}
});
// Expose static assets (fonts, favicons).
server.exposeStaticDir('/ui/fonts/{path*}', resolve(__dirname, '../../ui/public/assets/fonts'));
server.exposeStaticDir('/ui/favicons/{path*}', resolve(__dirname, '../../ui/public/assets/favicons'));

View file

@ -36,6 +36,7 @@ import * as Plugins from './plugins';
import { indexPatternsMixin } from './index_patterns';
import { savedObjectsMixin } from './saved_objects';
import { sampleDataMixin } from './sample_data';
import { urlShorteningMixin } from './url_shortening';
import { kibanaIndexMappingsMixin } from './mappings';
import { serverExtensionsMixin } from './server_extensions';
import { uiMixin } from '../ui';
@ -93,6 +94,9 @@ export default class KbnServer {
// setup routes for installing/uninstalling sample data sets
sampleDataMixin,
// setup routes for short urls
urlShorteningMixin,
// ensure that all bundles are built, or that the
// watch bundle server is running
optimizeMixin,

View file

@ -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 { urlShorteningMixin } from './url_shortening_mixin';

View file

@ -0,0 +1,50 @@
/*
* 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 { handleShortUrlError } from './lib/short_url_error';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
import { shortUrlLookupProvider } from './lib/short_url_lookup';
import { createGotoRoute } from './goto';
import { createShortenUrlRoute } from './shorten_url';
export function createRoutes(server, config) {
const shortUrlLookup = shortUrlLookupProvider(server);
server.route(createGotoRoute({ server, config, shortUrlLookup }));
server.route(createShortenUrlRoute({ shortUrlLookup }));
// TODO remove deprecated '/shorten' API in master (7.0)
server.route({
method: 'POST',
path: '/shorten',
handler: async function (request, reply) {
server.log(
['warning', 'deprecation'],
`'/shorten' API has been deprecated and will be removed in 7.0, use the '/api/shorten_url' API instead`);
try {
shortUrlAssertValid(request.payload.url);
const urlId = await shortUrlLookup.generateUrlId(request.payload.url, request);
reply(urlId);
} catch (err) {
reply(handleShortUrlError(err));
}
}
});
}

View file

@ -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 { handleShortUrlError } from './lib/short_url_error';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
export const createGotoRoute = ({ server, config, shortUrlLookup }) => ({
method: 'GET',
path: '/goto/{urlId}',
handler: async function (request, reply) {
try {
const url = await shortUrlLookup.getUrl(request.params.urlId, request);
shortUrlAssertValid(url);
const uiSettings = request.getUiSettingsService();
const stateStoreInSessionStorage = await uiSettings.get('state:storeInSessionStorage');
if (!stateStoreInSessionStorage) {
reply().redirect(config.get('server.basePath') + url);
return;
}
const app = server.getHiddenUiAppById('stateSessionStorageRedirect');
reply.renderApp(app, {
redirectUrl: url,
});
} catch (err) {
reply(handleShortUrlError(err));
}
}
});

View file

@ -19,7 +19,7 @@
import sinon from 'sinon';
import { shortUrlLookupProvider } from './short_url_lookup';
import { SavedObjectsClient } from '../saved_objects';
import { SavedObjectsClient } from '../../../saved_objects';
describe('shortUrlLookupProvider', () => {
const ID = 'bf00ad16941fc51420f91a93428b27a0';

View file

@ -0,0 +1,35 @@
/*
* 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 { handleShortUrlError } from './lib/short_url_error';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
export const createShortenUrlRoute = ({ shortUrlLookup }) => ({
method: 'POST',
path: '/api/shorten_url',
handler: async function (request, reply) {
try {
shortUrlAssertValid(request.payload.url);
const urlId = await shortUrlLookup.generateUrlId(request.payload.url, request);
reply({ urlId });
} catch (err) {
reply(handleShortUrlError(err));
}
}
});

View file

@ -0,0 +1,23 @@
/*
* 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, config) {
createRoutes(server, config);
}

View file

@ -1,137 +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 expect from 'expect.js';
import ngMock from 'ng_mock';
import chrome from '../../chrome';
import { UrlShortenerProvider } from '../lib/url_shortener';
describe('Url shortener', () => {
let urlShortener;
let $httpBackend;
const shareId = 'id123';
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (_$rootScope_, _$location_, _$httpBackend_, Private) {
$httpBackend = _$httpBackend_;
urlShortener = Private(UrlShortenerProvider);
}));
describe('Shorten without base path', () => {
it('should shorten urls with a port', function (done) {
$httpBackend.when('POST', '/shorten').respond(function (type, route, data) {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl('http://localhost:5601/app/kibana#123').then(function (url) {
expect(url).to.be(`http://localhost:5601/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a port', function (done) {
$httpBackend.when('POST', '/shorten').respond(function (type, route, data) {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl('http://localhost/app/kibana#123').then(function (url) {
expect(url).to.be(`http://localhost/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
});
describe('Shorten with base path', () => {
const basePath = '/foo';
let getBasePath;
beforeEach(ngMock.inject((Private) => {
getBasePath = sinon.stub(chrome, 'getBasePath').returns(basePath);
urlShortener = Private(UrlShortenerProvider);
}));
it('should shorten urls with a port', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost:5601${basePath}/app/kibana#123`).then((url) => {
expect(url).to.be(`http://localhost:5601${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a port', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana#123`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls with a query string', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana?foo#123');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana?foo#123`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls without a hash', (done) => {
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be('/app/kibana');
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}/app/kibana`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
it('should shorten urls with a query string in the hash', (done) => {
const relativeUrl = "/app/kibana#/discover?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-15m,mode:quick,to:now))&_a=(columns:!(_source),index:%27logstash-*%27,interval:auto,query:(query_string:(analyze_wildcard:!t,query:%27*%27)),sort:!(%27@timestamp%27,desc))"; //eslint-disable-line max-len, quotes
$httpBackend.when('POST', `${basePath}/shorten`).respond((type, route, data) => {
expect(JSON.parse(data).url).to.be(relativeUrl);
return [200, shareId];
});
urlShortener.shortenUrl(`http://localhost${basePath}${relativeUrl}`).then((url) => {
expect(url).to.be(`http://localhost${basePath}/goto/${shareId}`);
done();
});
$httpBackend.flush();
});
afterEach(() => {
getBasePath.restore();
});
});
});

View file

@ -28,7 +28,7 @@ import {
} from '../../state_management/state_hashing';
import { toastNotifications } from '../../notify';
import { UrlShortenerProvider } from '../lib/url_shortener';
import { shortenUrl } from '../lib/url_shortener';
import { uiModules } from '../../modules';
import shareTemplate from '../views/share.html';
@ -36,7 +36,6 @@ const app = uiModules.get('kibana');
app.directive('share', function (Private) {
const getUnhashableStates = Private(getUnhashableStatesProvider);
const urlShortener = Private(UrlShortenerProvider);
return {
restrict: 'E',
@ -144,9 +143,12 @@ app.directive('share', function (Private) {
this.urlFlags.shortSnapshot = !this.urlFlags.shortSnapshot;
if (this.urlFlags.shortSnapshot) {
urlShortener.shortenUrl(this.urls.snapshot)
shortenUrl(this.urls.snapshot)
.then(shortUrl => {
this.urls.shortSnapshot = shortUrl;
// We're using ES6 Promises, not $q, so we have to wrap this in $apply.
$scope.$apply(() => {
this.urls.shortSnapshot = shortUrl;
});
});
}
};
@ -156,9 +158,12 @@ app.directive('share', function (Private) {
if (this.urlFlags.shortSnapshotIframe) {
const snapshotIframe = this.makeUrlEmbeddable(this.urls.snapshot);
urlShortener.shortenUrl(snapshotIframe)
shortenUrl(snapshotIframe)
.then(shortUrl => {
this.urls.shortSnapshotIframe = shortUrl;
// We're using ES6 Promises, not $q, so we have to wrap this in $apply.
$scope.$apply(() => {
this.urls.shortSnapshotIframe = shortUrl;
});
});
}
};

View file

@ -19,34 +19,30 @@
import chrome from '../../chrome';
import url from 'url';
import { kfetch } from 'ui/kfetch';
import { toastNotifications } from 'ui/notify';
export function UrlShortenerProvider(Notifier, $http) {
const notify = new Notifier({
location: 'Url Shortener'
});
export async function shortenUrl(absoluteUrl) {
const basePath = chrome.getBasePath();
function shortenUrl(absoluteUrl) {
const basePath = chrome.getBasePath();
const parsedUrl = url.parse(absoluteUrl);
const path = parsedUrl.path.replace(basePath, '');
const hash = parsedUrl.hash ? parsedUrl.hash : '';
const relativeUrl = path + hash;
const parsedUrl = url.parse(absoluteUrl);
const path = parsedUrl.path.replace(basePath, '');
const hash = parsedUrl.hash ? parsedUrl.hash : '';
const relativeUrl = path + hash;
const body = JSON.stringify({ url: relativeUrl });
const formData = { url: relativeUrl };
return $http.post(`${basePath}/shorten`, formData).then((result) => {
return url.format({
protocol: parsedUrl.protocol,
host: parsedUrl.host,
pathname: `${basePath}/goto/${result.data}`
});
}).catch((response) => {
notify.error(response);
try {
const resp = await kfetch({ method: 'POST', 'pathname': '/api/shorten_url', body });
return url.format({
protocol: parsedUrl.protocol,
host: parsedUrl.host,
pathname: `${basePath}/goto/${resp.urlId}`
});
} catch (fetchError) {
toastNotifications.addDanger({
title: `Unable to create short URL. Error: ${fetchError.message}`,
'data-test-subj': 'shortenUrlFailure',
});
}
return {
shortenUrl
};
}

View file

@ -0,0 +1,140 @@
/*
* 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.
*/
jest.mock('ui/kfetch', () => ({}));
jest.mock('../../chrome', () => ({}));
jest.mock('ui/notify',
() => ({
toastNotifications: {
addDanger: () => {},
}
}), { virtual: true });
import sinon from 'sinon';
import expect from 'expect.js';
import { shortenUrl } from './url_shortener';
describe('Url shortener', () => {
const shareId = 'id123';
let kfetchStub;
beforeEach(() => {
kfetchStub = sinon.stub();
require('ui/kfetch').kfetch = async (...args) => {
return kfetchStub(...args);
};
});
describe('Shorten without base path', () => {
beforeAll(() => {
require('../../chrome').getBasePath = () => {
return '';
};
});
it('should shorten urls with a port', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana#123"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl('http://localhost:5601/app/kibana#123');
expect(shortUrl).to.be(`http://localhost:5601/goto/${shareId}`);
});
it('should shorten urls without a port', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana#123"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl('http://localhost/app/kibana#123');
expect(shortUrl).to.be(`http://localhost/goto/${shareId}`);
});
});
describe('Shorten with base path', () => {
const basePath = '/foo';
beforeAll(() => {
require('../../chrome').getBasePath = () => {
return basePath;
};
});
it('should shorten urls with a port', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana#123"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl(`http://localhost:5601${basePath}/app/kibana#123`);
expect(shortUrl).to.be(`http://localhost:5601${basePath}/goto/${shareId}`);
});
it('should shorten urls without a port', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana#123"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl(`http://localhost${basePath}/app/kibana#123`);
expect(shortUrl).to.be(`http://localhost${basePath}/goto/${shareId}`);
});
it('should shorten urls with a query string', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana?foo#123"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl(`http://localhost${basePath}/app/kibana?foo#123`);
expect(shortUrl).to.be(`http://localhost${basePath}/goto/${shareId}`);
});
it('should shorten urls without a hash', async () => {
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana"}'
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl(`http://localhost${basePath}/app/kibana`);
expect(shortUrl).to.be(`http://localhost${basePath}/goto/${shareId}`);
});
it('should shorten urls with a query string in the hash', async () => {
const relativeUrl = "/app/kibana#/discover?_g=(refreshInterval:(pause:!f,value:0),time:(from:now-15m,mode:quick,to:now))&_a=(columns:!(_source),index:%27logstash-*%27,interval:auto,query:(query_string:(analyze_wildcard:!t,query:%27*%27)),sort:!(%27@timestamp%27,desc))"; //eslint-disable-line max-len, quotes
kfetchStub.withArgs({
method: 'POST',
pathname: `/api/shorten_url`,
body: '{"url":"/app/kibana#/discover?_g=(refreshInterval:(pause:!f,value:0),time:(from:now-15m,mode:quick,to:now))&_a=(columns:!(_source),index:%27logstash-*%27,interval:auto,query:(query_string:(analyze_wildcard:!t,query:%27*%27)),sort:!(%27@timestamp%27,desc))"}' //eslint-disable-line max-len, quotes
}).returns(Promise.resolve({ urlId: shareId }));
const shortUrl = await shortenUrl(`http://localhost${basePath}${relativeUrl}`);
expect(shortUrl).to.be(`http://localhost${basePath}/goto/${shareId}`);
});
});
});

View file

@ -27,7 +27,8 @@ export default function ({ getService }) {
before(() => esArchiver.load('saved_objects/basic'));
after(() => esArchiver.unload('saved_objects/basic'));
it('generates shortened urls', async () => {
// TODO remove deprecated '/shorten' API in master (7.0)
it('generates shortened urls with deprecated URL', async () => {
const resp = await supertest
.post('/shorten')
.set('content-type', 'application/json')
@ -38,14 +39,27 @@ export default function ({ getService }) {
expect(resp.text.length > 0).to.be(true);
});
it('generates shortened urls', async () => {
const resp = await supertest
.post('/api/shorten_url')
.set('content-type', 'application/json')
.send({ url: '/app/kibana#/visualize/create' })
.expect(200);
expect(resp.body).to.have.property('urlId');
expect(typeof resp.body.urlId).to.be('string');
expect(resp.body.urlId.length > 0).to.be(true);
});
it('redirects shortened urls', async () => {
const resp = await supertest
.post('/shorten')
.post('/api/shorten_url')
.set('content-type', 'application/json')
.send({ url: '/app/kibana#/visualize/create' });
const urlId = resp.body.urlId;
await supertest
.get(`/goto/${resp.text}`)
.get(`/goto/${urlId}`)
.expect(302)
.expect('location', '/app/kibana#/visualize/create');
});