Revert "BUG-410 -- update RIFT platform"
[osm/UI.git] / skyquake / framework / core / api_utils / utils.js
1 /*
2 *
3 * Copyright 2016 RIFT.IO Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 // Helper Functions
19
20
21 /**
22 * Utils for use across the api_server.
23 * @module framework/core/utils
24 */
25
26 var fs = require('fs');
27 var request = require('request');
28 var Promise = require('promise');
29 var CONSTANTS = require('./constants.js');
30 var CONFD_PORT = '8008';
31 var APIVersion = '/v1';
32 var _ = require('lodash');
33
34 var requestWrapper = function(request) {
35 if (process.env.HTTP_PROXY && process.env.HTTP_PROXY != '') {
36 request = request.defaults({
37 'proxy': process.env.HTTP_PROXY
38 });
39 }
40 return request;
41 }
42
43 var confdPort = function(api_server) {
44 try {
45 api_server = api_server.replace(api_server.match(/[0-9](:[0-9]+)/)[1], '')
46 } catch (e) {
47
48 }
49 return api_server + ':' + CONFD_PORT;
50 };
51
52 var projectContextUrl = function(req, url) {
53 //NOTE: We need to go into the sessionStore because express-session
54 // does not reliably update the session.
55 // See https://github.com/expressjs/session/issues/450
56 var projectId = (req.session &&
57 req.sessionStore &&
58 req.sessionStore.sessions &&
59 req.sessionStore.sessions[req.session.id] &&
60 JSON.parse(req.sessionStore.sessions[req.session.id])['projectId']) ||
61 (null);
62 if (projectId) {
63 projectId = encodeURIComponent(projectId);
64 return url.replace(/(\/api\/operational\/|\/api\/config\/)(.*)/, '$1project/' + projectId + '/$2');
65 }
66 return url;
67 }
68
69 var addProjectContextToRPCPayload = function(req, url, inputPayload) {
70 //NOTE: We need to go into the sessionStore because express-session
71 // does not reliably update the session.
72 // See https://github.com/expressjs/session/issues/450
73 var projectId = (req.session &&
74 req.sessionStore &&
75 req.sessionStore.sessions &&
76 req.sessionStore.sessions[req.session.id] &&
77 JSON.parse(req.sessionStore.sessions[req.session.id])['projectId']) ||
78 (null);
79 if (projectId) {
80 if (url.indexOf('/api/operations/')) {
81 inputPayload['project-name'] = projectId;
82 }
83 }
84 return inputPayload;
85 }
86
87
88 var validateResponse = function(callerName, error, response, body, resolve, reject) {
89 var res = {};
90
91 if (error) {
92 console.log('Problem with "', callerName, '": ', error);
93 res.statusCode = 500;
94 res.errorMessage = {
95 error: 'Problem with ' + callerName + ': ' + error
96 };
97 reject(res);
98 return false;
99 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
100 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
101 res.statusCode = response.statusCode;
102
103 // auth specific
104 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
105 res.errorMessage = {
106 error: 'Authentication needed' + body
107 };
108 reject(res);
109 return false;
110 }
111
112 res.errorMessage = {
113 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + typeof(body) == 'string' ? body : JSON.stringify(body),
114 body: body
115 };
116
117 reject(res);
118 return false;
119 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
120 resolve({
121 statusCode: response.statusCode,
122 data: {}
123 });
124 return false;
125 } else {
126 return true;
127 }
128 };
129
130
131 var checkAuthorizationHeader = function(req) {
132 return new Promise(function(resolve, reject) {
133 if (req.session && req.session.authorization == null) {
134 reject();
135 } else {
136 resolve();
137 }
138 });
139 };
140
141 if (process.env.LOG_REQUESTS) {
142 var logFile = process.env.REQUESTS_LOG_FILE;
143
144 if (logFile && logFile != '') {
145 validateResponse = function(callerName, error, response, body, resolve, reject) {
146 var res = {};
147
148 if (error) {
149 console.log('Problem with "', callerName, '": ', error);
150 res.statusCode = 500;
151 res.errorMessage = {
152 error: 'Problem with ' + callerName + ': ' + error
153 };
154 reject(res);
155 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error: ' + error);
156 return false;
157 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
158 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
159 res.statusCode = response.statusCode;
160
161 // auth specific
162 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
163 res.errorMessage = {
164 error: 'Authentication needed' + body
165 };
166 reject(res);
167 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
168 return false;
169 }
170
171 res.errorMessage = {
172 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + body
173 };
174
175 reject(res);
176 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
177 return false;
178 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
179 resolve();
180 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
181 return false;
182 } else {
183 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
184 return true;
185 }
186 };
187 }
188 }
189
190 /**
191 * Serve the error response back back to HTTP requester
192 * @param {Object} error - object of the format
193 * {
194 * statusCode - HTTP code to respond back with
195 * error - actual error JSON object to serve
196 * }
197 * @param {Function} res - a handle to the express response function
198 */
199 var sendErrorResponse = function(error, res) {
200 if (!error.statusCode) {
201 console.error('Status Code has not been set in error object: ', error);
202 }
203 res.status(error.statusCode);
204 res.send(error);
205 }
206
207 /**
208 * Serve the success response back to HTTP requester
209 * @param {Object} response - object of the format
210 * {
211 * statusCode - HTTP code to respond back with
212 * data - actual data JSON object to serve
213 * }
214 * @param {Function} res - a handle to the express response function
215 */
216 var sendSuccessResponse = function(response, res) {
217 res.status(response.statusCode);
218 res.send(response.data);
219 }
220
221 var passThroughConstructor = function(app) {
222 app.get('/passthrough/:type/*', function(req, res) {
223 var url = req.params[0];
224 var type = req.params.type;
225 var api_server = req.query["api_server"];
226 var uri = confdPort(api_server) + APIVersion + '/' + url + '?deep';
227 // Check that type is valid
228 switch (type) {
229 case 'data':
230 ;
231 case 'collection':
232 break;
233 default:
234 res.send({});
235 }
236 new Promise(function(resolve, reject) {
237 request({
238 uri: projectContextUrl(req, uri),
239 method: 'GET',
240 headers: _.extend({}, CONSTANTS.HTTP_HEADERS.accept[type], {
241 'Authorization': req.session && req.session.authorization,
242 forever: CONSTANTS.FOREVER_ON,
243 rejectUnauthorized: false,
244 })
245 }, function(error, response, body) {
246 if (validateResponse('Passthrough: ' + url, error, response, body, resolve, reject)) {
247 resolve(JSON.parse(response.body))
248 };
249 });
250 }).then(function(data) {
251 res.send(data);
252 }, function(error) {
253 res.send({'error': error, uri: uri})
254 });;
255 });
256 }
257
258 var getPortForProtocol = function(protocol) {
259 switch (protocol) {
260 case 'http':
261 return 8000;
262 case 'https':
263 return 8443;
264 }
265 }
266
267 var buildRedirectURL = function(req, globalConfiguration, plugin, extra) {
268 var api_server = req.query['api_server'] || (req.protocol + '://' + globalConfiguration.get().api_server);
269 var download_server = req.query['dev_download_server'] || globalConfiguration.get().dev_download_server;
270 var url = '/';
271 url += plugin;
272 url += '/?api_server=' + api_server;
273 url += download_server ? '&dev_download_server=' + download_server : '';
274 url += extra || '';
275 return url;
276 }
277
278 var getHostNameFromURL = function(url) {
279 var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([^?#]*)(\?[^#]*|)(#.*|)$/);
280 return match && match[3];
281 }
282
283 var dataToJsonSansPropNameNamespace = function(s) {
284 var a = JSON.parse(s);
285 var b = JSON.stringify(a);
286 var c = b.replace(/{"[-\w]+:/g, '{"');
287 var d = c.replace(/,"[-\w]+:/g, ',"');
288 var j = JSON.parse(d);
289 return j;
290 }
291
292 module.exports = {
293 /**
294 * Ensure confd port is on api_server variable.
295 **/
296 confdPort: confdPort,
297
298 validateResponse: validateResponse,
299
300 checkAuthorizationHeader: checkAuthorizationHeader,
301
302 request: requestWrapper.call(null, request),
303
304 sendErrorResponse: sendErrorResponse,
305
306 sendSuccessResponse: sendSuccessResponse,
307
308 passThroughConstructor: passThroughConstructor,
309
310 getPortForProtocol: getPortForProtocol,
311
312 projectContextUrl: projectContextUrl,
313
314 addProjectContextToRPCPayload: addProjectContextToRPCPayload,
315
316 buildRedirectURL: buildRedirectURL,
317
318 getHostNameFromURL: getHostNameFromURL,
319
320 dataToJsonSansPropNameNamespace: dataToJsonSansPropNameNamespace
321 };