3a509646a69b771f2fc83aa54b1670ae2670ecb4
[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 return url.replace(/(\/api\/operational\/|\/api\/config\/)(.*)/, '$1project/' + projectId + '/$2');
64 }
65 return url;
66 }
67
68 var addProjectContextToRPCPayload = function(req, url, inputPayload) {
69 //NOTE: We need to go into the sessionStore because express-session
70 // does not reliably update the session.
71 // See https://github.com/expressjs/session/issues/450
72 var projectId = (req.session &&
73 req.sessionStore &&
74 req.sessionStore.sessions &&
75 req.sessionStore.sessions[req.session.id] &&
76 JSON.parse(req.sessionStore.sessions[req.session.id])['projectId']) ||
77 (null);
78 if (projectId) {
79 if (url.indexOf('/api/operations/')) {
80 inputPayload['project-name'] = projectId;
81 }
82 }
83 return inputPayload;
84 }
85
86
87 var validateResponse = function(callerName, error, response, body, resolve, reject) {
88 var res = {};
89
90 if (error) {
91 console.log('Problem with "', callerName, '": ', error);
92 res.statusCode = 500;
93 res.errorMessage = {
94 error: 'Problem with ' + callerName + ': ' + error
95 };
96 reject(res);
97 return false;
98 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
99 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
100 res.statusCode = response.statusCode;
101
102 // auth specific
103 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
104 res.errorMessage = {
105 error: 'Authentication needed' + body
106 };
107 reject(res);
108 return false;
109 }
110
111 res.errorMessage = {
112 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + typeof(body) == 'string' ? body : JSON.stringify(body),
113 body: body
114 };
115
116 reject(res);
117 return false;
118 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
119 resolve({
120 statusCode: response.statusCode,
121 data: {}
122 });
123 return false;
124 } else {
125 return true;
126 }
127 };
128
129
130 var checkAuthorizationHeader = function(req) {
131 return new Promise(function(resolve, reject) {
132 if (req.session && req.session.authorization == null) {
133 reject();
134 } else {
135 resolve();
136 }
137 });
138 };
139
140 if (process.env.LOG_REQUESTS) {
141 var logFile = process.env.REQUESTS_LOG_FILE;
142
143 if (logFile && logFile != '') {
144 validateResponse = function(callerName, error, response, body, resolve, reject) {
145 var res = {};
146
147 if (error) {
148 console.log('Problem with "', callerName, '": ', error);
149 res.statusCode = 500;
150 res.errorMessage = {
151 error: 'Problem with ' + callerName + ': ' + error
152 };
153 reject(res);
154 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error: ' + error);
155 return false;
156 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
157 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
158 res.statusCode = response.statusCode;
159
160 // auth specific
161 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
162 res.errorMessage = {
163 error: 'Authentication needed' + body
164 };
165 reject(res);
166 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
167 return false;
168 }
169
170 res.errorMessage = {
171 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + body
172 };
173
174 reject(res);
175 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
176 return false;
177 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
178 resolve();
179 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
180 return false;
181 } else {
182 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
183 return true;
184 }
185 };
186 }
187 }
188
189 /**
190 * Serve the error response back back to HTTP requester
191 * @param {Object} error - object of the format
192 * {
193 * statusCode - HTTP code to respond back with
194 * error - actual error JSON object to serve
195 * }
196 * @param {Function} res - a handle to the express response function
197 */
198 var sendErrorResponse = function(error, res) {
199 res.status(error.statusCode);
200 res.send(error);
201 }
202
203 /**
204 * Serve the success response back to HTTP requester
205 * @param {Object} response - object of the format
206 * {
207 * statusCode - HTTP code to respond back with
208 * data - actual data JSON object to serve
209 * }
210 * @param {Function} res - a handle to the express response function
211 */
212 var sendSuccessResponse = function(response, res) {
213 res.status(response.statusCode);
214 res.send(response.data);
215 }
216
217 var passThroughConstructor = function(app) {
218 app.get('/passthrough/:type/*', function(req, res) {
219 var url = req.params[0];
220 var type = req.params.type;
221 var api_server = req.query["api_server"];
222 var uri = confdPort(api_server) + APIVersion + '/' + url + '?deep';
223 // Check that type is valid
224 switch (type) {
225 case 'data':
226 ;
227 case 'collection':
228 break;
229 default:
230 res.send({});
231 }
232 new Promise(function(resolve, reject) {
233 request({
234 uri: projectContextUrl(req, uri),
235 method: 'GET',
236 headers: _.extend({}, CONSTANTS.HTTP_HEADERS.accept[type], {
237 'Authorization': req.session && req.session.authorization,
238 forever: CONSTANTS.FOREVER_ON,
239 rejectUnauthorized: false,
240 })
241 }, function(error, response, body) {
242 if (validateResponse('Passthrough: ' + url, error, response, body, resolve, reject)) {
243 resolve(JSON.parse(response.body))
244 };
245 });
246 }).then(function(data) {
247 res.send(data);
248 }, function(error) {
249 res.send({'error': error, uri: uri})
250 });;
251 });
252 }
253
254 var getPortForProtocol = function(protocol) {
255 switch (protocol) {
256 case 'http':
257 return 8000;
258 case 'https':
259 return 8443;
260 }
261 }
262
263 module.exports = {
264 /**
265 * Ensure confd port is on api_server variable.
266 **/
267 confdPort: confdPort,
268
269 validateResponse: validateResponse,
270
271 checkAuthorizationHeader: checkAuthorizationHeader,
272
273 request: requestWrapper.call(null, request),
274
275 sendErrorResponse: sendErrorResponse,
276
277 sendSuccessResponse: sendSuccessResponse,
278
279 passThroughConstructor: passThroughConstructor,
280
281 getPortForProtocol: getPortForProtocol,
282
283 projectContextUrl: projectContextUrl,
284
285 addProjectContextToRPCPayload: addProjectContextToRPCPayload
286 };