1 |
|
2 |
|
3 |
|
4 | var Batch = require('batch');
|
5 | var mongoose = require('mongoose');
|
6 | var clone = require('component-clone');
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | module.exports = Apis;
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | function Api () {
|
20 | }
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | Api.prototype.setModel = function(model) {
|
27 | this.model = model;
|
28 | return this;
|
29 | };
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | Api.prototype.setPaths = function(paths) {
|
36 | var self = this;
|
37 | var _paths = this.model.schema.paths;
|
38 |
|
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 |
|
53 |
|
54 | Api.prototype.setApis = function(apis) {
|
55 | this.apis = apis;
|
56 | return this;
|
57 | };
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 | Api.prototype.filterJSON = function(json) {
|
64 | var obj = {};
|
65 | obj._id = json._id;
|
66 | this.paths.forEach(function(path){
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | obj[path] = json[path];
|
75 | });
|
76 | return obj;
|
77 | };
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | Api.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 |
|
100 |
|
101 | Api.prototype.setSingularName = function() {
|
102 | this.singularName = this.model.modelName.toLowerCase();
|
103 | return this;
|
104 | };
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | Api.prototype.setPluralName = function() {
|
111 |
|
112 | this.pluralName = this.singularName + 's';
|
113 | return this;
|
114 | };
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | Api.prototype.setBelongsToName = function() {
|
121 | this.belongsToName = this.singularName + '_id';
|
122 | return this;
|
123 | };
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | Api.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 |
|
154 |
|
155 | Api.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 |
|
259 |
|
260 | Api.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 |
|
285 |
|
286 | Api.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 |
|
307 |
|
308 | Api.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 |
|
339 |
|
340 | Api.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 |
|
362 |
|
363 | function 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 |
|
381 |
|
382 | Apis.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 | };
|