UNPKG

13 kBJavaScriptView Raw
1var AV = require('avoscloud-sdk').AV;
2var underscore = require('underscore');
3var http = require('http');
4var https = require('https');
5var urlParser = require('url');
6var querystring = require('querystring');
7var util = require('util');
8var express = require('express');
9var path = require('path');
10var fs = require('fs');
11var _ = require('underscore');
12var cronJob = require('cron').CronJob;
13var qs = require('qs');
14
15var Global = {}
16
17var _ref, _ref1;
18if ((_ref = https.globalAgent) != null) {
19 if ((_ref1 = _ref.options) != null) {
20 _ref1.rejectUnauthorized = false;
21 }
22}
23
24
25function MockRequest(object, params, user){
26 this.object = object;
27 this.params = params || object;
28 this.user = user;
29}
30
31
32function MockResponse(options){
33 this._options = options;
34}
35
36MockResponse.prototype = {
37 success: function(data){
38 this._options.success(data);
39 },
40 error: function(err){
41 this._options.error(err);
42 }
43}
44
45exports.MockRequest = MockRequest
46exports.MockResponse = MockResponse
47
48//Mock functions in cloud code.
49
50//Mock http request
51var HOOK_PREFIX = "_hook_";
52var className = function(clazz) {
53 if (underscore.isString(clazz)) {
54 return HOOK_PREFIX + clazz;
55 }
56 if (clazz.className != null) {
57 return HOOK_PREFIX + clazz.className;
58 }
59 throw "Unknown class:" + clazz;
60};
61
62//Mock functions
63Global.funcs = {};
64AV.Cloud.define = function(name, func){
65 Global.funcs[name] = func;
66};
67AV.Cloud.beforeSave = function(name, func){
68 Global.funcs[className(name) + "_beforeSave"] = func;
69};
70AV.Cloud.afterSave = function(name, func){
71 Global.funcs[className(name) + "_afterSave"] = func;
72};
73AV.Cloud.afterUpdate = function(name, func){
74 Global.funcs[className(name) + "_afterUpdate"] = func;
75};
76AV.Cloud.beforeDelete = function(name, func){
77 Global.funcs[className(name) + "_beforeDelete"] = func;
78};
79AV.Cloud.afterDelete = function(name, func){
80 Global.funcs[className(name) + "_afterDelete"] = func;
81};
82
83function runFunc(name, req, res){
84 if(!Global.funcs[name])
85 throw "Could not find function:" + name;
86 Global.funcs[name].call(this, req, res);
87}
88function runBeforeSave(name, req, res){
89 runFunc(className(name) + "_beforeSave", req, res);
90}
91function runAfterSave(name, req, res){
92 runFunc(className(name) + "_afterSave", req, res);
93}
94function runAfterUpdate(name, req, res){
95 runFunc(className(name) + "_afterUpdate", req, res);
96}
97function runBeforeDelete(name, req, res){
98 runFunc(className(name) + "_beforeDelete", req, res);
99}
100function runAfterDelete(name, req, res){
101 runFunc(className(name) + "_afterDelete", req, res);
102}
103
104exports.runFunc = runFunc
105exports.runBeforeSave = runBeforeSave
106exports.runAfterSave = runAfterSave
107exports.runAfterUpdate = runAfterUpdate
108exports.runBeforeDelete = runBeforeDelete
109exports.runAfterDelete = runAfterDelete
110
111AV.Cloud.setInterval = function(name, interval, func){
112 if (!/[a-zA-Z0-9]+/.exec(name)) {
113 throw "The timer name must be an valid identifier.";
114 }
115 if ((typeof func) !== 'function') {
116 throw "The func must be a function.";
117 }
118 if ((typeof interval) !== 'number') {
119 throw "The interval must be a valid integer in seconds.";
120 }
121 new cronJob('*/'+interval+' * * * * *', func, null, true);
122};
123
124AV.Cloud.cronJob = function(name, cron, func) {
125
126 if (!/[a-zA-Z0-9]+/.exec(name)) {
127 throw "The timer name must be an valid identifier.";
128 }
129 if ((typeof func) !== 'function') {
130 throw "The func must be a function.";
131 }
132 if ((typeof cron) !== 'string') {
133 throw "The cron must be a valid string in the form of 'sec min hour dayOfMonth month dayOfWeek [year]'.";
134 }
135 if (cron.split(" ").length < 6) {
136 throw "The cron must be a valid string in the form of 'sec min hour dayOfMonth month dayOfWeek [year]'.";
137 }
138 new cronJob(cron, func, null, true);
139};
140
141HTTPResponse = (function() {
142 function HTTPResponse(buffer, headers, response, status, text) {
143 this.buffer = buffer != null ? buffer : null;
144 this.headers = headers != null ? headers : {};
145 this.response = response != null ? response : null;
146 this.status = status != null ? status : null;
147 this.text = text != null ? text : null;
148 }
149
150 return HTTPResponse;
151
152})();
153
154mimeTypes = [
155 {
156 pattern: /^text\/plain.*/i,
157 process: function(res) {
158 return res.text;
159 }
160 }, {
161 pattern: /^application\/json.*/i,
162 process: function(res) {
163 return JSON.parse(res.text);
164 }
165 }, {
166 pattern: /^application\/x-www-form-urlencoded/i,
167 process: function(res) {
168 return qs.parse(res.buffer);
169 }
170 }
171];
172
173trySetData = function(httpRes) {
174 var contentType, type;
175
176 contentType = httpRes.headers['content-type'];
177 type = _.find(mimeTypes, function(mimeType) {
178 return mimeType.pattern.exec(contentType);
179 });
180 if (type != null) {
181 try{
182 return httpRes.data = type.process(httpRes);
183 }catch(e){
184 httpRes.data = httpRes.buffer;
185 }
186 } else {
187 return httpRes.data = httpRes.buffer;
188 }
189};
190
191AV.Cloud.HTTPResponse = HTTPResponse;
192
193AV.Cloud.httpRequest = function(options) {
194 var body, headers, hostname, httpResponse, http_module, method, params, parsedRes, path, port, promise, request, requestOptions, search, url;
195
196 options = options || {};
197 options.agent = false;
198 url = options.url;
199 http_module = /^https.*/.exec(url) ? https : http;
200 promise = new AV.Promise();
201 params = options.params;
202 headers = options.headers || "";
203 method = options.method || "GET";
204 body = options.body;
205 parsedRes = urlParser.parse(url);
206 hostname = parsedRes.hostname;
207 port = parsedRes.port || 80;
208 path = parsedRes.path;
209 search = parsedRes.search;
210 if (params != null) {
211 path = search == null ? path + '?' : path + '&';
212 if (typeof params === 'string') {
213 params = querystring.parse(params);
214 }
215 params = querystring.stringify(params);
216 path = path + params;
217 }
218 delete options.params;
219 delete options.body;
220 delete options.url;
221 requestOptions = {
222 host: hostname,
223 port: port,
224 method: method,
225 headers: headers,
226 path: path
227 };
228 requestOptions = _.extend(requestOptions, options);
229 httpResponse = new HTTPResponse;
230 request = http_module.request(requestOptions, function(res) {
231 var chunkList, contentLength;
232 httpResponse.headers = res.headers || {};
233 httpResponse.status = res.statusCode;
234 httpResponse.text = '';
235 chunkList = [];
236 contentLength = 0;
237 res.on('data', function(chunk) {
238 httpResponse.text += chunk.toString();
239 contentLength += chunk.length;
240 return chunkList.push(chunk);
241 });
242 return res.on('end', function() {
243 var chunk, pos, _i, _len;
244
245 httpResponse.buffer = new Buffer(contentLength);
246 pos = 0;
247 for (_i = 0, _len = chunkList.length; _i < _len; _i++) {
248 chunk = chunkList[_i];
249 chunk.copy(httpResponse.buffer, pos);
250 pos += chunk.length;
251 }
252 trySetData(httpResponse);
253 if (httpResponse.status < 200 || httpResponse.status >= 400) {
254 return promise.reject(httpResponse);
255 } else {
256 return promise.resolve(httpResponse);
257 }
258 });
259 });
260 request.on('error', function(e) {
261 httpResponse.text = util.inspect(e);
262 httpResponse.status = 500;
263 return promise.reject(httpResponse);
264 });
265 request.end(body);
266 return promise._thenRunCallbacks(options);
267};
268
269Global.files = {}
270
271function watchFile(f, name){
272 if(Global.files[f])
273 return;
274 Global.files[f] = true;
275 fs.watchFile(f,{ persistent: true, interval: 2000 },function(curr, prev){
276 if(curr.mtime != prev.mtime){
277 console.log("File " + f + " is changed,reload it...");
278 requireFromFile(f, name);
279 }
280 });
281}
282
283//Mock express
284var Module = module.constructor;
285var paths = module.paths;
286function requireFromFile(path, filename) {
287 var src = fs.readFileSync(path, 'utf-8');
288 var m = new Module();
289 m.paths = module.paths;
290 m._compile("var AV = require('avoscloud-sdk').AV;var __production=0; \n" + src, filename);
291 watchFile(path,filename);
292 return m.exports;
293}
294
295
296Module.prototype.require = function(id) {
297 if(id.match(/^cloud\//)){
298 id = Global.rootPath + id;
299 return requireFromFile(require.resolve(id), id);
300 }
301 result = Module._load(id, this);
302 if(id == 'express'){
303 oldExpress = result;
304 result = function(){
305 if(Global.app!=null){
306 delete Global.app.routes.get;
307 delete Global.app.routes.post;
308 delete Global.app.routes.put;
309 delete Global.app.routes.delete;
310 delete Global.app.routes.options;
311 addSystemEndpoints(Global.app);
312 return Global.app;
313 }
314 var app = oldExpress();
315 app.__listen = app.listen;
316 app.listen = function(){
317 var configDir, jsonFile, publicDir, views;
318 jsonFile = require.resolve(Global.rootPath + 'config/global.json');
319 configDir = path.dirname(jsonFile);
320 views = path.resolve(configDir, '../' + (this.get('views')));
321 publicDir = path.resolve(configDir, '../public');
322 this.set('views', views);
323 this.use(oldExpress["static"](publicDir));
324 this.use(function(err, req, res, next) {
325 if (err != null) {
326 console.error("Error occured:" + err);
327 return res.send(err);
328 } else {
329 return next();
330 }
331 });
332 return this;
333 };
334 Global.app = app;
335 return app;
336 };
337 result = _.extend(result, oldExpress);
338 }
339 return result;
340};
341
342var createObject = function(req, res, cb){
343 var className = req.params.className;
344 var object = new AV.Object(className);
345 var body = req.body;
346 if(body.id != null && body.id != ''){
347 object = AV.Object.createWithoutData(className, body.id);
348 object.fetch().then(function(obj){
349 cb.call(this, object);
350 }, function(err){
351 res.send('Error : ' + err.message);
352 });
353 }else{
354 object._finishFetch(req.body, true);
355 cb.call(this, object);
356 }
357}
358
359function processRequest(type, req, res){
360 if(type == 'object'){
361 var func = req.params.func;
362 var className = req.params.className;
363 createObject(req, res, function(object){
364 var mockReq = new MockRequest(object);
365 var mockResp =new MockResponse({
366 success: function(data){
367 res.send("ok.");
368 },
369 error: function(err){
370 console.log("Error occured:" + err);
371 res.send("Error : " + err);
372 }
373 });
374 var target = null;
375 switch(func){
376 case "beforeSave":
377 target = runBeforeSave;
378 break;
379 case "afterSave":
380 target = runAfterSave;
381 break;
382 case "afterUpdate":
383 target = runAfterUpdate;
384 break;
385 case "beforeDelete":
386 target = runBeforeDelete;
387 break;
388 case "afterDelete":
389 target = runAfterDelete;
390 break;
391 default:
392 throw "Could not find function:" + func;
393 }
394 target.call(this, className, mockReq, mockResp);
395 });
396 }else{
397 var mockReq = new MockRequest(null, req.body);
398 var mockResp =new MockResponse({
399 success: function(data){
400 res.send(data);
401 },
402 error: function(err){
403 console.log("Error occured:" + err);
404 res.send("Error : " + err);
405 }
406 });
407 runFunc(req.params.name, mockReq, mockResp);
408 }
409}
410var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
411var extractFuncs = function(cb){
412 var funcs = [];
413 var classes = [];
414 for(var f in Global.funcs){
415 if(!new RegExp('^' + HOOK_PREFIX).exec(f)){
416 funcs.push(f);
417 }else{
418 var idx = f.lastIndexOf("_");
419 var className = f.substring(HOOK_PREFIX.length, idx);
420 var method = f.substring(idx + 1);
421 classes[className] = classes[className] || []
422 classes[className].push(method);
423 }
424 }
425 cb.call(this, funcs, classes);
426}
427function addSystemEndpoints(app){
428 //Added test endpoints.
429 app.post("/avos/:className/:func", function(req, res){
430 processRequest('object', req, res);
431 });
432 app.post("/avos/:name", function(req, res){
433 processRequest("function", req, res);
434 });
435 app.get("/avos", function(req, res){
436 res.sendfile(lib + "/index.html");
437 });
438 app.get("/avos/classes", function(req, res){
439 extractFuncs(function(funcs, classes){
440 res.send(_.keys(classes));
441 });
442 });
443 app.get("/avos/functions", function(req, res){
444 extractFuncs(function(funcs, classes){
445 var className = req.query.className;
446 if(className == null){
447 res.send(funcs);
448 }else{
449 res.send(classes[className] || []);
450 }
451 });
452 });
453}
454
455exports.runCloudCode = function(rootPath){
456 Global.rootPath = rootPath;
457 //initialize SDK.
458 if(!fs.existsSync(Global.rootPath + 'config/global.json'))
459 throw "Cloud not find config/global.json";
460 if(!fs.existsSync(Global.rootPath + 'cloud/main.js'))
461 throw "Cloud not find config/global.json";
462
463 var globalJSON = fs.readFileSync(Global.rootPath + 'config/global.json', 'utf-8')
464 var data = JSON.parse(globalJSON);
465 AV.initialize(data.applicationId, data.applicationKey);
466
467 //Load main.js
468 var cloudPath = path.resolve(Global.rootPath + 'cloud/main.js');
469 requireFromFile(cloudPath, 'cloud/main.js');
470 //Stratup mock server.
471 var app = Global.app;
472 if(!app){
473 app = express();
474 app.use(express.bodyParser());
475 }
476 var port = app.port || 3000;
477 process.on('uncaughtException', function (err) {
478 var msg = err;
479 var stack = null;
480 if(err.message){
481 msg = err.message;
482 }
483 if(err.stack){
484 stack = err.stack;
485 }
486 console.error((new Date).toUTCString() + ' uncaughtException:', msg);
487 console.error(stack);
488 });
489 addSystemEndpoints(app);
490 app.__listen(port, function() {
491 return console.log("Mock Server is listening on " + port + "\nPress CTRL-C to stop server.");
492 });
493}