1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const express = require('express');
|
14 | const bodyParser = require('body-parser');
|
15 | const log = require('../log/logger');
|
16 |
|
17 | module.exports = (() => {
|
18 | let server = null;
|
19 |
|
20 | function healthCheck(req, res, next) {
|
21 | return res.status(200).json({ healthy: true });
|
22 | }
|
23 |
|
24 | function mapPostToElements(req, res, next) {
|
25 | res.locals.body = req.body.body;
|
26 | res.locals.hookType = req.body.hookType;
|
27 | res.locals.method = req.body.method;
|
28 | res.locals.query = req.body.query;
|
29 | res.locals.objectName = req.body.objectName;
|
30 | res.locals.entityId = req.body.entityId;
|
31 | res.locals.tempObjectStore = req.body.tempObjectStore || {};
|
32 | next();
|
33 | }
|
34 |
|
35 | function generateBaseTask(req, res, next) {
|
36 | let appMetadata;
|
37 | let requestHeaders;
|
38 | const response = {};
|
39 | const method = res.locals.method || req.method;
|
40 |
|
41 | if (req.body.response) {
|
42 | response.body = req.body.response.body;
|
43 | response.headers = req.body.response.headers;
|
44 | response.status = req.body.response.status;
|
45 | }
|
46 |
|
47 | try {
|
48 | appMetadata = JSON.parse(req.get('X-Kinvey-App-Metadata')) || {};
|
49 | } catch (e) {
|
50 | appMetadata = {};
|
51 | }
|
52 |
|
53 | try {
|
54 | requestHeaders = JSON.parse(req.get('X-Kinvey-Original-Request-Headers')) || {};
|
55 | } catch (e) {
|
56 | requestHeaders = {};
|
57 | }
|
58 |
|
59 | req.task = {
|
60 | appId: req.get('X-Kinvey-Environment-Id') || '',
|
61 | appMetadata,
|
62 | authKey: req.get('X-Auth-Key'),
|
63 | requestId: req.get('X-Kinvey-Request-Id') || '',
|
64 | method: method.toUpperCase(),
|
65 | request: {
|
66 | method,
|
67 | headers: requestHeaders,
|
68 | username: req.get('X-Kinvey-Username') || '',
|
69 | userId: req.get('X-Kinvey-User-Id') || ''
|
70 | },
|
71 | response: {
|
72 | status: req.get('X-Kinvey-Response-Status') || response.status || 0,
|
73 | headers: req.get('X-Kinvey-Response-Headers') || response.headers || {},
|
74 | body: req.get('X-Kinvey-Response-Body') || response.body || {}
|
75 | }
|
76 | };
|
77 | next();
|
78 | }
|
79 |
|
80 | function addFunctionsTaskAttributes(req, res, next) {
|
81 | req.task.taskType = 'functions';
|
82 | req.task.request.objectName = res.locals.objectName || '';
|
83 | req.task.taskName = req.params.handlerName;
|
84 | req.task.hookType = res.locals.hookType || 'customEndpoint';
|
85 | req.task.request.tempObjectStore = res.locals.tempObjectStore;
|
86 | next();
|
87 | }
|
88 |
|
89 | function addDataTaskAttributes(req, res, next) {
|
90 | req.task.taskType = 'data';
|
91 | req.task.request.serviceObjectName = req.params.serviceObjectName;
|
92 | next();
|
93 | }
|
94 |
|
95 | function appendId(req, res, next) {
|
96 | if (req.params && req.params.id) {
|
97 | req.task.request.entityId = req.params.id;
|
98 | } else if (res.locals.entityId) {
|
99 | req.task.request.entityId = res.locals.entityId;
|
100 | }
|
101 | next();
|
102 | }
|
103 |
|
104 | function appendQuery(req, res, next) {
|
105 | if (req.query && Object.keys(req.query).length > 0) {
|
106 | req.task.request.query = req.query;
|
107 | } else if (res.locals.query && Object.keys(res.locals.query).length > 0) {
|
108 | req.task.request.query = res.locals.query;
|
109 | }
|
110 | next();
|
111 | }
|
112 |
|
113 | function appendCount(req, res, next) {
|
114 | req.task.endpoint = '_count';
|
115 | next();
|
116 | }
|
117 |
|
118 | function appendBody(req, res, next) {
|
119 | req.task.request.body = res.locals.body || req.body;
|
120 | next();
|
121 | }
|
122 |
|
123 | function getDataBody(result) {
|
124 | return result.response.body;
|
125 | }
|
126 |
|
127 | function getFunctionsBody(result) {
|
128 | return {
|
129 | request: result.request,
|
130 | response: result.response
|
131 | };
|
132 | }
|
133 |
|
134 | function getAuthBody(result) {
|
135 | return result.response.body;
|
136 | }
|
137 |
|
138 | function getDiscoveryBody(result) {
|
139 | return result.discoveryObjects;
|
140 | }
|
141 |
|
142 | function sendTask(req, res, next) {
|
143 | const taskReceivedCallback = req.app.get('taskReceivedCallback');
|
144 | taskReceivedCallback(req.task, (err, result) => {
|
145 | if (err) {
|
146 | let errorResponse;
|
147 | try {
|
148 | errorResponse = JSON.parse(err);
|
149 | } catch (e) {
|
150 | errorResponse = err;
|
151 | }
|
152 | log.debug('Responding with error');
|
153 | log.error(errorResponse);
|
154 | res.set('X-Kinvey-Request-Continue', errorResponse.continue || true);
|
155 |
|
156 | if (errorResponse.headers) {
|
157 | res.set(errorResponse.headers);
|
158 | }
|
159 |
|
160 | return res.status(errorResponse.statusCode || 550).json(errorResponse.body);
|
161 | } else if (result) {
|
162 | try {
|
163 | result.response.body = JSON.parse(result.response.body);
|
164 | } catch (e) {
|
165 |
|
166 | log.debug('Responding with success');
|
167 | log.debug(result);
|
168 | }
|
169 |
|
170 | if (result.headers) {
|
171 | res.set(result.headers);
|
172 | }
|
173 |
|
174 | if (typeof result.continue !== 'undefined' && result.continue !== null) {
|
175 | res.set('X-Kinvey-Request-Continue', result.continue);
|
176 | }
|
177 |
|
178 | let body;
|
179 |
|
180 | switch (req.task.taskType) {
|
181 | case 'data':
|
182 | body = getDataBody(result);
|
183 | break;
|
184 | case 'functions':
|
185 | body = getFunctionsBody(result);
|
186 | break;
|
187 | case 'serviceDiscovery':
|
188 | body = getDiscoveryBody(result);
|
189 | break;
|
190 | case 'auth':
|
191 | body = getAuthBody(result);
|
192 | break;
|
193 | default:
|
194 | body = {};
|
195 | }
|
196 |
|
197 | return res.status(result.response.statusCode || 200).json(body);
|
198 | }
|
199 |
|
200 | log.debug('Responding with error - no result or error');
|
201 | const errorResponse = {
|
202 | error: 'InternalError',
|
203 | description: 'Bad Request: An Internal Error occurred',
|
204 | debug: ''
|
205 | };
|
206 |
|
207 | log.error(errorResponse);
|
208 | return res.status(500).json(errorResponse);
|
209 | });
|
210 | }
|
211 |
|
212 | function buildDiscoverTask(req, res, next) {
|
213 | req.task = {
|
214 | taskType: 'serviceDiscovery',
|
215 | request: {},
|
216 | response: {}
|
217 | };
|
218 |
|
219 | next();
|
220 | }
|
221 |
|
222 | function addAuthTaskAttributes(req, res, next) {
|
223 | req.task.taskType = 'auth';
|
224 | req.task.taskName = req.params.handlerName;
|
225 | req.task.request.body = {
|
226 | username: req.body.username,
|
227 | password: req.body.password,
|
228 | options: req.body.options
|
229 | };
|
230 |
|
231 | next();
|
232 | }
|
233 |
|
234 | function checkIfFlexFunction(req, res, next) {
|
235 | if (req.params.serviceObjectName === '_flexFunctions' || req.params.serviceObjectName === '_command') {
|
236 | next('route');
|
237 | } else {
|
238 | next();
|
239 | }
|
240 | }
|
241 |
|
242 | function startServer(taskReceivedCallback, startedCallback, options) {
|
243 | const app = express();
|
244 |
|
245 | app.set('taskReceivedCallback', taskReceivedCallback);
|
246 | app.use(bodyParser.json({ limit: options.requestBodyLimit || 26214400 }));
|
247 | app.use((req, res, next) => {
|
248 | req.task = {};
|
249 | next();
|
250 | });
|
251 |
|
252 | const router = express.Router();
|
253 |
|
254 |
|
255 | router.get('/healthcheck', healthCheck);
|
256 |
|
257 |
|
258 | router.get('/:serviceObjectName/_count', generateBaseTask, addDataTaskAttributes, appendQuery, appendCount, sendTask);
|
259 | router.get('/:serviceObjectName/:id?', generateBaseTask, addDataTaskAttributes, appendQuery, appendId, sendTask);
|
260 | router.post('/:serviceObjectName/', checkIfFlexFunction, generateBaseTask, addDataTaskAttributes, appendBody, sendTask);
|
261 | router.put('/:serviceObjectName/:id', generateBaseTask, addDataTaskAttributes, appendId, appendBody, sendTask);
|
262 | router.delete('/:serviceObjectName/:id?', generateBaseTask, addDataTaskAttributes, appendId, appendQuery, sendTask);
|
263 |
|
264 |
|
265 | router.post('/_flexFunctions/:handlerName', mapPostToElements, generateBaseTask, addFunctionsTaskAttributes, appendQuery, appendId, appendBody, sendTask);
|
266 | router.post('/_auth/:handlerName', generateBaseTask, addAuthTaskAttributes, sendTask);
|
267 |
|
268 | router.post('/_command/discover', buildDiscoverTask, sendTask);
|
269 |
|
270 | app.use('/', router);
|
271 |
|
272 | options.host = options.host || 'localhost';
|
273 | options.port = options.port || 10001;
|
274 |
|
275 | server = app.listen(options.port, options.host, () => {
|
276 | console.log(`Service listening on ${options.port}`);
|
277 | startedCallback();
|
278 | });
|
279 | }
|
280 |
|
281 | function stop(callback) {
|
282 | server.close(callback);
|
283 | }
|
284 |
|
285 | return {
|
286 | startServer,
|
287 | stop
|
288 | };
|
289 | })();
|