From 17b2bb8a58c4c3adbd0bc33b5ca46c280e226157 Mon Sep 17 00:00:00 2001 From: Matt Godbolt Date: Mon, 15 Feb 2021 12:52:46 -0600 Subject: [PATCH] Move /shortener to /api/shortener (#2426) We still support /shortener (though will scrape logs to see when we can remove). The route under /api/shortener will have the CORS headers set up properly for things like @foonathan's lexy visualiser. --- app.js | 7 ++---- docs/API.md | 4 ++-- lib/handlers/api.js | 7 +++++- lib/handlers/route-api.js | 5 +++-- lib/storage/remote.js | 2 +- static/sharing.js | 2 +- test/handlers/api-tests.js | 44 ++++++++++++++++++-------------------- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/app.js b/app.js index 6cbcb1f19..c7a914a6a 100755 --- a/app.js +++ b/app.js @@ -64,7 +64,6 @@ import { languages as allLanguages } from './lib/languages'; import { logger, logToPapertrail, suppressConsoleLog } from './lib/logger'; import { ClientOptionsHandler } from './lib/options-handler'; import * as props from './lib/properties'; -import { getShortenerTypeByKey } from './lib/shortener'; import { sources } from './lib/sources'; import { loadSponsorsFromString } from './lib/sponsors'; import { getStorageTypeByKey } from './lib/storage'; @@ -630,9 +629,6 @@ async function main() { // Based on combined format, but: GDPR compliant IP, no timestamp & no unused fields for our usecase const morganFormat = isDevMode() ? 'dev' : ':gdpr_ip ":method :url" :status'; - const shortenerType = getShortenerTypeByKey(clientOptionsHandler.options.urlShortenService); - const shortener = new shortenerType(storageHandler); - /* * This is a workaround to make cross origin monaco web workers function * in spite of the monaco webpack plugin hijacking the MonacoEnvironment global. @@ -731,7 +727,8 @@ async function main() { .use(bodyParser.json({limit: ceProps('bodyParserLimit', maxUploadSize)})) .use('/source', sourceHandler.handle.bind(sourceHandler)) .use('/g', oldGoogleUrlHandler) - .post('/shortener', shortener.handle.bind(shortener)); + // Deprecated old route for this -- TODO remove in late 2021 + .post('/shortener', routeApi.apiHandler.shortener.handle.bind(routeApi.apiHandler.shortener)); noscriptHandler.InitializeRoutes({limit: ceProps('bodyParserLimit', maxUploadSize)}); routeApi.InitializeRoutes(); diff --git a/docs/API.md b/docs/API.md index 81064f0cf..cd49d3353 100644 --- a/docs/API.md +++ b/docs/API.md @@ -208,7 +208,7 @@ If JSON is present in the request's `Accept` header, the compilation results } ``` -### `POST /shortener` - saves given state *forever* to a shortlink and returns the unique id for the link +### `POST /api/shortener` - saves given state *forever* to a shortlink and returns the unique id for the link The body of this post should be in the format of a [ClientState](https://github.com/compiler-explorer/compiler-explorer/blob/main/lib/clientstate.js) Be sure that the Content-Type of your post is application/json @@ -266,7 +266,7 @@ If there were multiple editors during the saved session, you can retreive them b ### `GET /clientstate/` - Opens the website in a given state -This call is to open the website with a given state (without having to store the state first with /shortener) +This call is to open the website with a given state (without having to store the state first with /api/shortener) Instead of sending the ClientState JSON in the post body, it will have to be encoded with base64 and attached directly onto the URL. diff --git a/lib/handlers/api.js b/lib/handlers/api.js index e2eeb8694..82538e161 100644 --- a/lib/handlers/api.js +++ b/lib/handlers/api.js @@ -28,13 +28,14 @@ import _ from 'underscore'; import { ClientStateNormalizer } from '../clientstate-normalizer'; import { logger } from '../logger'; +import { getShortenerTypeByKey } from '../shortener'; import * as utils from '../utils'; import { AsmDocsHandler } from './asm-docs-api'; import { Formatter } from './formatting'; export class ApiHandler { - constructor(compileHandler, ceProps, storageHandler) { + constructor(compileHandler, ceProps, storageHandler, urlShortenService) { this.compilers = []; this.languages = []; this.usedLangIds = []; @@ -78,6 +79,10 @@ export class ApiHandler { this.handle.get('/formats', formatter.listHandler.bind(formatter)); this.handle.get('/shortlinkinfo/:id', this.shortlinkInfoHandler.bind(this)); + + const shortenerType = getShortenerTypeByKey(urlShortenService); + this.shortener = new shortenerType(storageHandler); + this.handle.post('/shortener', this.shortener.handle.bind(this.shortener)); } shortlinkInfoHandler(req, res, next) { diff --git a/lib/handlers/route-api.js b/lib/handlers/route-api.js index e6a1c1552..91538455c 100644 --- a/lib/handlers/route-api.js +++ b/lib/handlers/route-api.js @@ -37,7 +37,8 @@ export class RouteAPI { this.renderGoldenLayout = config.renderGoldenLayout; this.storageHandler = config.storageHandler; - this.apiHandler = new ApiHandler(config.compileHandler, config.ceProps, config.storageHandler); + this.apiHandler = new ApiHandler(config.compileHandler, config.ceProps, config.storageHandler, + config.clientOptionsHandler.options.urlShortenService); } InitializeRoutes() { @@ -113,7 +114,7 @@ export class RouteAPI { goldenifier.fromClientState(state); return goldenifier.golden; } - + unstoredStateHandler(req, res) { const state = JSON.parse(Buffer.from(req.params.clientstatebase64, 'base64').toString()); const config = this.getGoldenLayoutFromClientState(new ClientState(state)); diff --git a/lib/storage/remote.js b/lib/storage/remote.js index 170aeebb5..2dbcda7c8 100644 --- a/lib/storage/remote.js +++ b/lib/storage/remote.js @@ -49,7 +49,7 @@ export class StorageRemote extends StorageBase { async handler(req, res) { let resp; try { - resp = await this.post('/shortener', { + resp = await this.post('/api/shortener', { json: true, body: req.body, }); diff --git a/static/sharing.js b/static/sharing.js index 94095c5e8..3fee1064e 100644 --- a/static/sharing.js +++ b/static/sharing.js @@ -294,7 +294,7 @@ function getShortLink(config, root, done) { }); $.ajax({ type: 'POST', - url: window.location.origin + root + 'shortener', + url: window.location.origin + root + 'api/shortener', dataType: 'json', // Expected contentType: 'application/json', // Sent data: data, diff --git a/test/handlers/api-tests.js b/test/handlers/api-tests.js index 3adb8e3e7..52b3c0ba7 100644 --- a/test/handlers/api-tests.js +++ b/test/handlers/api-tests.js @@ -25,6 +25,7 @@ import express from 'express'; import { ApiHandler } from '../../lib/handlers/api'; +import { StorageNull } from '../../lib/storage'; import { chai } from '../utils'; const languages = { @@ -85,30 +86,27 @@ describe('API handling', () => { before(() => { app = express(); - const apiHandler = new ApiHandler({ - handle: res => { - res.send('compile'); + const apiHandler = new ApiHandler( + { + handle: res => res.send('compile'), + handlePopularArguments: res => res.send('ok'), + handleOptimizationArguments: res => res.send('ok'), + }, (key, def) => { + switch (key) { + case 'formatters': + return 'formatt:badformatt'; + case 'formatter.formatt.exe': + return 'echo'; + case 'formatter.formatt.version': + return 'Release'; + case 'formatter.formatt.name': + return 'FormatT'; + default: + return def; + } }, - handlePopularArguments: res => { - res.send('ok'); - }, - handleOptimizationArguments: res => { - res.send('ok'); - }, - }, (key, def) => { - switch (key) { - case 'formatters': - return 'formatt:badformatt'; - case 'formatter.formatt.exe': - return 'echo'; - case 'formatter.formatt.version': - return 'Release'; - case 'formatter.formatt.name': - return 'FormatT'; - default: - return def; - } - }); + new StorageNull('/', {}), + 'default'); app.use('/api', apiHandler.handle); apiHandler.setCompilers(compilers); apiHandler.setLanguages(languages);