Skip to content

Commit

Permalink
Migrate url shortener service (elastic#50896)
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 committed Dec 11, 2019
1 parent 0ce0245 commit 28983f0
Show file tree
Hide file tree
Showing 16 changed files with 630 additions and 87 deletions.
128 changes: 128 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# GitHub CODEOWNERS definition
# Identify which groups will be pinged by changes to different parts of the codebase.
# For more info, see https://help.github.com/articles/about-codeowners/

# App
/x-pack/legacy/plugins/lens/ @elastic/kibana-app
/x-pack/legacy/plugins/graph/ @elastic/kibana-app
/src/plugins/share/ @elastic/kibana-app
/src/legacy/server/url_shortening/ @elastic/kibana-app
/src/legacy/server/sample_data/ @elastic/kibana-app

# App Architecture
/src/plugins/data/ @elastic/kibana-app-arch
/src/plugins/embeddable/ @elastic/kibana-app-arch
/src/plugins/expressions/ @elastic/kibana-app-arch
/src/plugins/kibana_react/ @elastic/kibana-app-arch
/src/plugins/kibana_utils/ @elastic/kibana-app-arch
/src/plugins/navigation/ @elastic/kibana-app-arch
/src/plugins/ui_actions/ @elastic/kibana-app-arch
/src/plugins/visualizations/ @elastic/kibana-app-arch
/x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch
/src/legacy/core_plugins/data/ @elastic/kibana-app-arch
/src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch
/src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch
/src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch
/src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch
/src/legacy/core_plugins/kibana/server/field_formats/ @elastic/kibana-app-arch
/src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch
/src/legacy/core_plugins/kibana/server/routes/api/suggestions/ @elastic/kibana-app-arch
/src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch
/src/legacy/server/index_patterns/ @elastic/kibana-app-arch

# APM
/x-pack/legacy/plugins/apm/ @elastic/apm-ui
/x-pack/test/functional/apps/apm/ @elastic/apm-ui
/src/legacy/core_plugins/apm_oss/ @elastic/apm-ui

# Beats
/x-pack/legacy/plugins/beats_management/ @elastic/beats

# Canvas
/x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas

# Logs & Metrics UI
/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/legacy/plugins/integrations_manager/ @elastic/epm

# Machine Learning
/x-pack/legacy/plugins/ml/ @elastic/ml-ui
/x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui
/x-pack/test/functional/services/machine_learning/ @elastic/ml-ui
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
# ML team owns the transform plugin, ES team added here for visibility
# because the plugin lives in Kibana's Elasticsearch management section.
/x-pack/legacy/plugins/transform/ @elastic/ml-ui @elastic/es-ui
/x-pack/test/functional/apps/transform/ @elastic/ml-ui
/x-pack/test/functional/services/transform_ui/ @elastic/ml-ui
/x-pack/test/functional/services/transform.ts @elastic/ml-ui

# Operations
/src/dev/ @elastic/kibana-operations
/src/setup_node_env/ @elastic/kibana-operations
/src/optimize/ @elastic/kibana-operations
/packages/*eslint*/ @elastic/kibana-operations
/packages/*babel*/ @elastic/kibana-operations
/packages/kbn-dev-utils*/ @elastic/kibana-operations
/packages/kbn-es/ @elastic/kibana-operations
/packages/kbn-pm/ @elastic/kibana-operations
/packages/kbn-test/ @elastic/kibana-operations
/src/legacy/server/keystore/ @elastic/kibana-operations
/src/legacy/server/pid/ @elastic/kibana-operations
/src/legacy/server/sass/ @elastic/kibana-operations
/src/legacy/server/utils/ @elastic/kibana-operations
/src/legacy/server/warnings/ @elastic/kibana-operations

# Platform
/src/core/ @elastic/kibana-platform
/config/kibana.yml @elastic/kibana-platform
/x-pack/plugins/features/ @elastic/kibana-platform
/x-pack/plugins/licensing/ @elastic/kibana-platform
/packages/kbn-config-schema/ @elastic/kibana-platform
/src/legacy/server/config/ @elastic/kibana-platform
/src/legacy/server/csp/ @elastic/kibana-platform
/src/legacy/server/http/ @elastic/kibana-platform
/src/legacy/server/i18n/ @elastic/kibana-platform
/src/legacy/server/logging/ @elastic/kibana-platform
/src/legacy/server/saved_objects/ @elastic/kibana-platform
/src/legacy/server/status/ @elastic/kibana-platform

# Security
/x-pack/legacy/plugins/security/ @elastic/kibana-security
/x-pack/legacy/plugins/spaces/ @elastic/kibana-security
/x-pack/plugins/spaces/ @elastic/kibana-security
/x-pack/legacy/plugins/encrypted_saved_objects/ @elastic/kibana-security
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
/src/legacy/server/csp/ @elastic/kibana-security
/x-pack/plugins/security/ @elastic/kibana-security
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security

# Kibana Stack Services
/src/dev/i18n @elastic/kibana-stack-services
/packages/kbn-analytics/ @elastic/kibana-stack-services
/src/legacy/core_plugins/ui_metric/ @elastic/kibana-stack-services
/src/plugins/usage_collection/ @elastic/kibana-stack-services
/x-pack/legacy/plugins/telemetry @elastic/kibana-stack-services
/x-pack/legacy/plugins/alerting @elastic/kibana-stack-services
/x-pack/legacy/plugins/actions @elastic/kibana-stack-services
/x-pack/legacy/plugins/task_manager @elastic/kibana-stack-services

# Design
**/*.scss @elastic/kibana-design

# Elasticsearch UI
/src/legacy/core_plugins/console/ @elastic/es-ui
/src/plugins/es_ui_shared/ @elastic/es-ui
/x-pack/legacy/plugins/console_extensions/ @elastic/es-ui
/x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui
/x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/legacy/plugins/index_management/ @elastic/es-ui
/x-pack/legacy/plugins/license_management/ @elastic/es-ui
/x-pack/legacy/plugins/remote_clusters/ @elastic/es-ui
/x-pack/legacy/plugins/rollup/ @elastic/es-ui
/x-pack/legacy/plugins/searchprofiler/ @elastic/es-ui
/x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui
/x-pack/legacy/plugins/watcher/ @elastic/es-ui

# Kibana TSVB external contractors
/src/legacy/core_plugins/metrics/ @elastic/kibana-tsvb-external
2 changes: 0 additions & 2 deletions src/legacy/server/url_shortening/routes/create_routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@

import { shortUrlLookupProvider } from './lib/short_url_lookup';
import { createGotoRoute } from './goto';
import { createShortenUrlRoute } from './shorten_url';


export function createRoutes(server) {
const shortUrlLookup = shortUrlLookupProvider(server);

server.route(createGotoRoute({ server, shortUrlLookup }));
server.route(createShortenUrlRoute({ shortUrlLookup }));
}
8 changes: 1 addition & 7 deletions src/legacy/server/url_shortening/routes/goto.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,12 @@ import { shortUrlAssertValid } from './lib/short_url_assert_valid';

export const createGotoRoute = ({ server, shortUrlLookup }) => ({
method: 'GET',
path: '/goto/{urlId}',
path: '/goto_LP/{urlId}',
handler: async function (request, h) {
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) {
return h.redirect(request.getBasePath() + url);
}

const app = server.getHiddenUiAppById('stateSessionStorageRedirect');
return h.renderApp(app, {
redirectUrl: url,
Expand Down
24 changes: 0 additions & 24 deletions src/legacy/server/url_shortening/routes/lib/short_url_lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import crypto from 'crypto';
import { get } from 'lodash';

export function shortUrlLookupProvider(server) {
Expand All @@ -34,29 +33,6 @@ export function shortUrlLookupProvider(server) {
}

return {
async generateUrlId(url, req) {
const id = crypto.createHash('md5').update(url).digest('hex');
const savedObjectsClient = req.getSavedObjectsClient();
const { isConflictError } = savedObjectsClient.errors;

try {
const doc = await savedObjectsClient.create('url', {
url,
accessCount: 0,
createDate: new Date(),
accessDate: new Date()
}, { id });

return doc.id;
} catch (error) {
if (isConflictError(error)) {
return id;
}

throw error;
}
},

async getUrl(id, req) {
const doc = await req.getSavedObjectsClient().get('url', id);
updateMetadata(doc, req);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,43 +48,6 @@ describe('shortUrlLookupProvider', () => {
sandbox.restore();
});

describe('generateUrlId', () => {
it('returns the document id', async () => {
const id = await shortUrl.generateUrlId(URL, req);
expect(id).toEqual(ID);
});

it('provides correct arguments to savedObjectsClient', async () => {
await shortUrl.generateUrlId(URL, req);

sinon.assert.calledOnce(savedObjectsClient.create);
const [type, attributes, options] = savedObjectsClient.create.getCall(0).args;

expect(type).toEqual(TYPE);
expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate', 'createDate', 'url']);
expect(attributes.url).toEqual(URL);
expect(options.id).toEqual(ID);
});

it('passes persists attributes', async () => {
await shortUrl.generateUrlId(URL, req);

sinon.assert.calledOnce(savedObjectsClient.create);
const [type, attributes] = savedObjectsClient.create.getCall(0).args;

expect(type).toEqual(TYPE);
expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate', 'createDate', 'url']);
expect(attributes.url).toEqual(URL);
});

it('gracefully handles version conflict', async () => {
const error = savedObjectsClient.errors.decorateConflictError(new Error());
savedObjectsClient.create.throws(error);
const id = await shortUrl.generateUrlId(URL, req);
expect(id).toEqual(ID);
});
});

describe('getUrl', () => {
beforeEach(() => {
const attributes = { accessCount: 2, url: URL };
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/share/kibana.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "share",
"version": "kibana",
"server": false,
"server": true,
"ui": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,9 @@
* under the License.
*/

import { handleShortUrlError } from './lib/short_url_error';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
import { PluginInitializerContext } from '../../../core/server';
import { SharePlugin } from './plugin';

export const createShortenUrlRoute = ({ shortUrlLookup }) => ({
method: 'POST',
path: '/api/shorten_url',
handler: async function (request) {
try {
shortUrlAssertValid(request.payload.url);
const urlId = await shortUrlLookup.generateUrlId(request.payload.url, request);
return { urlId };
} catch (err) {
throw handleShortUrlError(err);
}
}
});
export function plugin(initializerContext: PluginInitializerContext) {
return new SharePlugin(initializerContext);
}
37 changes: 37 additions & 0 deletions src/plugins/share/server/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server';
import { createRoutes } from './routes/create_routes';

export class SharePlugin implements Plugin {
constructor(private readonly initializerContext: PluginInitializerContext) {}

public async setup(core: CoreSetup) {
createRoutes(core, this.initializerContext.logger.get());
}

public start() {
this.initializerContext.logger.get().debug('Starting plugin');
}

public stop() {
this.initializerContext.logger.get().debug('Stopping plugin');
}
}
32 changes: 32 additions & 0 deletions src/plugins/share/server/routes/create_routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 { CoreSetup, Logger } from 'kibana/server';

import { shortUrlLookupProvider } from './lib/short_url_lookup';
import { createGotoRoute } from './goto';
import { createShortenUrlRoute } from './shorten_url';

export function createRoutes({ http }: CoreSetup, logger: Logger) {
const shortUrlLookup = shortUrlLookupProvider({ logger });
const router = http.createRouter();

createGotoRoute({ router, shortUrlLookup, http });
createShortenUrlRoute({ router, shortUrlLookup });
}
Loading

0 comments on commit 28983f0

Please sign in to comment.