UNPKG

7.51 kBJavaScriptView Raw
1/**
2 * Module dependencies
3 */
4var Batch = require('batch');
5var mongoose = require('mongoose');
6var clone = require('component-clone');
7
8
9/**
10 * Expose `Apis`
11 */
12
13module.exports = Apis;
14
15
16/**
17 * Api
18 */
19function Api () {
20}
21
22
23/**
24 * Set `model`
25 */
26Api.prototype.setModel = function(model) {
27 this.model = model;
28 return this;
29};
30
31
32/**
33 * Set `paths` to output to json
34 */
35Api.prototype.setPaths = function(paths) {
36 var self = this;
37 var _paths = this.model.schema.paths;
38 // validate paths
39 paths.forEach(function(path){
40 if (!_paths[path]){
41 var e = self.model.modelName + ' has no path named ' + path;
42 throw new Error(e);
43 }
44 });
45 this.pathsString = paths.join(' ');
46 this.paths = paths;
47 return this;
48};
49
50
51/**
52 * Set `apis` obj
53 */
54Api.prototype.setApis = function(apis) {
55 this.apis = apis;
56 return this;
57};
58
59
60/**
61 * Filter paths on json
62 */
63Api.prototype.filterJSON = function(json) {
64 var obj = {};
65 obj._id = json._id;
66 this.paths.forEach(function(path){
67 // Ember.String.decamelize
68 // var STRING_DECAMELIZE_REGEXP = (/([a-z])([A-Z])/g);
69 // var key = path
70 // .replace(STRING_DECAMELIZE_REGEXP, '$1_$2')
71 // .toLowerCase();
72 //
73 // obj[key] = json[path];
74 obj[path] = json[path];
75 });
76 return obj;
77};
78
79
80/**
81 * Get URIS
82 */
83Api.prototype.getURIS = function(app) {
84 this
85 .setSingularName()
86 .setPluralName()
87 .setBelongsToName()
88 .setRemoveHook();
89 app.get('/' + this.pluralName, this.all.bind(this));
90 app.post('/' + this.pluralName, this.create.bind(this));
91 app.get('/' + this.pluralName + '/:id', this.one.bind(this));
92 app.put('/' + this.pluralName + '/:id', this.update.bind(this));
93 app.del('/' + this.pluralName + '/:id', this.remove.bind(this));
94 return app;
95};
96
97
98/**
99 * Set singular name
100 */
101Api.prototype.setSingularName = function() {
102 this.singularName = this.model.modelName.toLowerCase();
103 return this;
104};
105
106
107/**
108 * Set plural name
109 */
110Api.prototype.setPluralName = function() {
111 // this.pluralName = this.model.collection.name;
112 this.pluralName = this.singularName + 's';
113 return this;
114};
115
116
117/**
118 * Set belongsto name
119 */
120Api.prototype.setBelongsToName = function() {
121 this.belongsToName = this.singularName + '_id';
122 return this;
123};
124
125
126/**
127 * Destroy children
128 */
129Api.prototype.setRemoveHook = function() {
130 var self = this;
131 this.model.schema.pre('remove', function(next) {
132 var item = this;
133 var batch = new Batch();
134 Object.keys(self.apis.models).forEach(function(i){
135 var model = self.apis.models[i];
136 batch.push(function(fn) {
137 var query = {};
138 query[self.belongsToName] = item.id;
139 model.find(query).remove().exec(fn);
140 });
141 });
142 batch.end(function(err){
143 if (err) throw err;
144 next();
145 });
146 });
147 return this;
148
149};
150
151
152/**
153 * GET /`model`/
154 */
155Api.prototype.all = function(req, res, next) {
156
157 var self = this;
158 var conditions;
159 var fields;
160 var items;
161 var total;
162 var pages;
163 var skip;
164 var limit;
165
166 if (req.query){
167 conditions = req.query.conditions;
168 if (req.query.options){
169 skip = req.query.options.skip;
170 limit = req.query.options.limit;
171 }
172 }
173
174 var batch = new Batch;
175 batch.push(function(done){
176
177 if (!(skip != null && limit != null)) return done();
178
179 var options = clone(req.query.options);
180 delete options.skip;
181 delete options.limit;
182
183 self
184 .model
185 .find(conditions, fields, options)
186 .count(function(err, _total){
187 total = _total;
188 done(err);
189 });
190
191 });
192
193 batch.push(function(done){
194
195 var options = clone(req.query.options);
196 self
197 .model
198 .find(conditions, fields, options)
199 .exec(function(err, _items) {
200 items = _items;
201 done(err);
202 });
203
204 });
205
206 batch.end(function(err){
207 cb(err, items, total);
208 });
209
210
211 function cb(err){
212
213 if (err) return next(err);
214
215 if (!items) items = [];
216
217 var records = [];
218 var batch = new Batch();
219
220 batch.concurrency(1);
221
222 items.forEach(function(item) {
223 batch.push(function(fn){
224 item.__isReadable__(req, function(err) {
225 if (err) return fn();
226 records.push(self.filterJSON(item));
227 fn();
228 });
229 });
230 });
231
232 batch.end(function(err){
233
234 if (err && err !== false) return next(err);
235
236 var json = {};
237 json[self.pluralName] = records;
238
239 if (total != null){
240
241 var pages = Math.ceil(total / limit);
242
243 json.meta = {
244 pages: pages,
245 total: total
246 };
247
248 }
249
250 res.json(json);
251
252 });
253 };
254};
255
256
257/**
258 * POST /`model`/
259 */
260Api.prototype.create = function(req, res, next) {
261 if (req.body.query) {
262 req.query = req.body.query;
263 return this.all(req, res);
264 }
265 var self = this;
266 var item = new self.model({});
267 item.__isCreatable__(req, function(err) {
268 if (err) return next(err);
269 var data = req.body[self.singularName];
270 for (var k in data){
271 item[k] = data[k];
272 }
273 item.save(function(err, item) {
274 if (err) return next(err);
275 var json = {};
276 json[self.singularName] = self.filterJSON(item);
277 res.json(json);
278 });
279 });
280};
281
282
283/**
284 * GET /`model`/`id`/
285 */
286Api.prototype.one = function(req, res, next) {
287 var self = this;
288 self.model.findById(req.params.id, function(err, item) {
289 if (err) return next(err);
290 if (!item){
291 var err = new Error('404');
292 err.status = 404;
293 return next(err);
294 }
295 item.__isReadable__(req, function(err) {
296 if (err) return next(err);
297 var json = {};
298 json[self.singularName] = self.filterJSON(item);
299 res.json(json);
300 });
301 });
302};
303
304
305/**
306 * PUT /`model`/`id`/
307 */
308Api.prototype.update = function(req, res, next) {
309 var self = this;
310 self.model.findById(req.params.id, function(err, item) {
311 if (err) return next(err);
312 if (!item){
313 var err = new Error('404');
314 err.status = 404;
315 return next(err);
316 }
317 item.__isUpdatable__(req, function(err) {
318 if (err) return next(err);
319
320 var data = req.body[self.singularName];
321 for (var k in data){
322 item[k] = data[k];
323 }
324
325 item.save(function(err, item) {
326 if (err) return next(err);
327 var json = {};
328 json[self.singularName] = self.filterJSON(item);
329 res.json(json);
330 });
331
332 });
333 });
334};
335
336
337/**
338 * DELETE /`model`/`id`/
339 */
340Api.prototype.remove = function(req, res, next) {
341 var self = this;
342 self.model.findById(req.params.id, function(err, item) {
343 if (err) return next(err);
344 if (!item){
345 var err = new Error('404');
346 err.status = 404;
347 return next(err);
348 }
349 item.__isRemovable__(req, function(err) {
350 if (err) return next(err);
351 item.remove(function(err) {
352 if (err) return next(err);
353 res.json({});
354 });
355 });
356 });
357};
358
359
360/**
361 * Initialize new Apis for `models`
362 */
363function Apis (app, models) {
364 if (!(this instanceof Apis)) return new Apis(app, models);
365 var self = this;
366 this.models = models;
367 this.app = app;
368 Object.keys(models).forEach(function(modelName){
369 var model = models[modelName];
370 var api = new Api();
371 api.setModel(model);
372 api.setApis(self);
373 self[modelName] = api;
374 });
375 return this;
376}
377
378
379/**
380 * Get URIS
381 */
382Apis.prototype.create = function() {
383 var self = this;
384 Object.keys(self).forEach(function(name){
385 if (name == 'models' || name == 'app') return;
386 self[name].getURIS(app);
387 });
388 return app;
389};