blob: db64723612ce14212e2ea3247c117f94b550cee4 [file] [log] [blame] [raw]
RabsRincon6ef87b52018-02-27 14:58:21 +01001// Copyright (c) 2017, Matt Godbolt
Matt Godbolt1c3e94d2017-12-11 20:05:58 -06002// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// * Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23// POSSIBILITY OF SUCH DAMAGE.
24
25const express = require('express'),
RabsRincon7c18ffe2018-06-15 15:04:55 +020026 _ = require('underscore'),
Matt Godbolt1c3e94d2017-12-11 20:05:58 -060027 AsmDocsApi = require('./asm-docs-api'),
RabsRinconb8c8c652018-04-20 22:07:40 +020028 FormatterHandler = require('./formatting'),
Partouf652fd6b2018-10-01 20:03:51 +020029 utils = require('../utils'),
Partouf27daaff2018-10-02 22:04:49 +020030 logger = require('../logger').logger,
Partouf652fd6b2018-10-01 20:03:51 +020031 clientStateNormalizer = require('../clientstate-normalizer').ClientStateNormalizer;
Matt Godbolt1c3e94d2017-12-11 20:05:58 -060032
33class ApiHandler {
Partouf652fd6b2018-10-01 20:03:51 +020034 constructor(compileHandler, ceProps, storageHandler) {
Matt Godbolt1c3e94d2017-12-11 20:05:58 -060035 this.compilers = [];
partouf2ef40c92018-01-01 22:30:26 +010036 this.languages = [];
RabsRincon84298422018-04-08 03:05:38 +020037 this.usedLangIds = [];
Partouf8bb5e7d2018-10-15 02:32:31 +020038 this.options = null;
Partouf652fd6b2018-10-01 20:03:51 +020039 this.storageHandler = storageHandler;
Matt Godbolt1c3e94d2017-12-11 20:05:58 -060040 this.handle = express.Router();
41 this.handle.use((req, res, next) => {
42 res.header("Access-Control-Allow-Origin", "*");
43 res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
44 next();
45 });
partouf2ef40c92018-01-01 22:30:26 +010046 this.handle.get('/compilers', this.handleCompilers.bind(this));
47 this.handle.get('/compilers/:language', this.handleCompilers.bind(this));
48 this.handle.get('/languages', this.handleLanguages.bind(this));
RabsRincona4923112019-01-28 08:22:54 +010049 this.handle.get('/libraries/:language', this.handleLangLibraries.bind(this));
50 this.handle.get('/libraries', this.handleAllLibraries.bind(this));
RabsRinconb8c8c652018-04-20 22:07:40 +020051
Matt Godbolt88a1e682017-12-21 04:26:57 -060052 const asmDocsHandler = new AsmDocsApi.Handler();
53 this.handle.get('/asm/:opcode', asmDocsHandler.handle.bind(asmDocsHandler));
Partouf865d2172018-10-26 13:33:42 +020054
Matt Godboltd3591222017-12-22 07:03:29 -060055 this.handle.post('/compiler/:compiler/compile', compileHandler.handle.bind(compileHandler));
Partouf01db8372019-01-21 20:17:50 +010056 this.handle.post('/popularArguments/:compiler', compileHandler.handlePopularArguments.bind(compileHandler));
Partouf69a43f72019-08-13 22:15:53 +020057 this.handle.post('/optimizationArguments/:compiler',
58 compileHandler.handleOptimizationArguments.bind(compileHandler));
59
60 this.handle.get('/popularArguments/:compiler', compileHandler.handlePopularArguments.bind(compileHandler));
61 this.handle.get('/optimizationArguments/:compiler',
62 compileHandler.handleOptimizationArguments.bind(compileHandler));
RabsRinconb8c8c652018-04-20 22:07:40 +020063
64 const formatter = new FormatterHandler(ceProps);
65 this.handle.post('/format/:tool', formatter.formatHandler.bind(formatter));
RabsRinconbcdfd462018-04-20 23:23:01 +020066 this.handle.get('/formats', formatter.listHandler.bind(formatter));
Partouf652fd6b2018-10-01 20:03:51 +020067
Partouf27daaff2018-10-02 22:04:49 +020068 this.handle.get('/shortlinkinfo/:id', this.shortlinkInfoHandler.bind(this));
Partouf652fd6b2018-10-01 20:03:51 +020069 }
70
71 shortlinkInfoHandler(req, res, next) {
72 const id = req.params.id;
73 this.storageHandler.expandId(id)
74 .then(result => {
75 const config = JSON.parse(result.config);
76
77 if (config.content) {
78 const normalizer = new clientStateNormalizer();
79 normalizer.fromGoldenLayout(config);
80
81 res.set('Content-Type', 'application/json');
82 res.end(JSON.stringify(normalizer.normalized));
83 } else {
84 res.set('Content-Type', 'application/json');
85 res.end(JSON.stringify(config));
86 }
87 })
88 .catch(err => {
89 logger.warn(`Exception thrown when expanding ${id}: `, err);
90 next({
91 statusCode: 404,
92 message: `ID "${id}" could not be found`
93 });
94 });
Matt Godbolt1c3e94d2017-12-11 20:05:58 -060095 }
96
partouf2ef40c92018-01-01 22:30:26 +010097 handleLanguages(req, res) {
Partouf8bb5e7d2018-10-15 02:32:31 +020098 const availableLanguages = this.usedLangIds.map(val => {
99 let lang = this.languages[val];
100 let newLangObj = Object.assign({}, lang);
101 if (this.options) {
102 newLangObj.defaultCompiler = this.options.options.defaultCompiler[lang.id];
103 }
104 return newLangObj;
105 });
partouf2ef40c92018-01-01 22:30:26 +0100106
107 if (req.accepts(['text', 'json']) === 'json') {
108 res.set('Content-Type', 'application/json');
109 res.end(JSON.stringify(availableLanguages));
110 } else {
111 res.set('Content-Type', 'text/plain');
112 const title = 'Id';
113 const maxLength = _.max(_.pluck(_.pluck(availableLanguages, 'id').concat([title]), 'length'));
114 res.write(utils.padRight(title, maxLength) + ' | Name\n');
115 res.end(availableLanguages.map(lang => utils.padRight(lang.id, maxLength) + ' | ' + lang.name)
116 .join("\n"));
Partouf8bb5e7d2018-10-15 02:32:31 +0200117 }
118 }
119
Partouf7ba94cc2018-10-15 02:39:11 +0200120 getLibrariesAsArray(languageId) {
121 const libsForLanguageObj = this.options.options.libs[languageId];
Partouf55560382018-10-15 02:36:39 +0200122 if (!libsForLanguageObj) return [];
123
124 return Object.keys(libsForLanguageObj).map((key) => {
Partouf7ba94cc2018-10-15 02:39:11 +0200125 const language = libsForLanguageObj[key];
126 const versionArr = Object.keys(language.versions).map((key) => {
127 let versionObj = Object.assign({}, language.versions[key]);
Partouf55560382018-10-15 02:36:39 +0200128 versionObj.id = key;
129 return versionObj;
130 });
131
132 return {
133 id: key,
Partouf7ba94cc2018-10-15 02:39:11 +0200134 name: language.name,
135 description: language.description,
136 url: language.url,
Partouf55560382018-10-15 02:36:39 +0200137 versions: versionArr
138 };
139 });
140 }
141
RabsRincona4923112019-01-28 08:22:54 +0100142 handleLangLibraries(req, res, next) {
Partouf8bb5e7d2018-10-15 02:32:31 +0200143 if (this.options) {
144 if (req.params.language) {
145 res.set('Content-Type', 'application/json');
Partouf55560382018-10-15 02:36:39 +0200146 const libsForLanguageArr = this.getLibrariesAsArray(req.params.language);
Partouf8bb5e7d2018-10-15 02:32:31 +0200147 res.end(JSON.stringify(libsForLanguageArr));
148 } else {
149 next({
150 statusCode: 404,
151 message: "Language is required"
152 });
153 }
154 } else {
155 next({
156 statusCode: 500,
157 message: "Internal error"
158 });
partouf2ef40c92018-01-01 22:30:26 +0100159 }
160 }
161
RabsRincona4923112019-01-28 08:22:54 +0100162 handleAllLibraries(req, res, next) {
163 if (this.options) {
164 res.set('Content-Type', 'application/json');
165 res.end(JSON.stringify(this.options.options.libs));
166 } else {
167 next({
168 statusCode: 500,
169 message: "Internal error"
170 });
171 }
172 }
173
partouf2ef40c92018-01-01 22:30:26 +0100174 handleCompilers(req, res) {
175 let filteredCompilers = this.compilers;
176 if (req.params.language) {
partouf045c1722018-01-02 02:33:04 +0100177 filteredCompilers = this.compilers.filter((val) => val.lang === req.params.language);
partouf2ef40c92018-01-01 22:30:26 +0100178 }
179
180 if (req.accepts(['text', 'json']) === 'json') {
181 res.set('Content-Type', 'application/json');
182 res.end(JSON.stringify(filteredCompilers));
183 } else {
184 res.set('Content-Type', 'text/plain');
185 const title = 'Compiler Name';
186 const maxLength = _.max(_.pluck(_.pluck(filteredCompilers, 'id').concat([title]), 'length'));
187 res.write(utils.padRight(title, maxLength) + ' | Description\n');
188 res.end(
189 filteredCompilers.map(compiler => utils.padRight(compiler.id, maxLength) + ' | ' + compiler.name)
190 .join("\n"));
191 }
192 }
193
Matt Godbolt1c3e94d2017-12-11 20:05:58 -0600194 setCompilers(compilers) {
195 this.compilers = compilers;
RabsRincon84298422018-04-08 03:05:38 +0200196 this.usedLangIds = _.uniq(_.pluck(this.compilers, 'lang'));
Matt Godbolt1c3e94d2017-12-11 20:05:58 -0600197 }
partouf2ef40c92018-01-01 22:30:26 +0100198
199 setLanguages(languages) {
200 this.languages = languages;
201 }
Partouf8bb5e7d2018-10-15 02:32:31 +0200202
203 setOptions(options) {
204 this.options = options;
205 }
Matt Godbolt1c3e94d2017-12-11 20:05:58 -0600206}
207
Matt Godboltd3591222017-12-22 07:03:29 -0600208module.exports.Handler = ApiHandler;