1 | | // Generated by CoffeeScript 1.4.0 |
2 | 1 | (function() { |
3 | 1 | var ObjectId, async, mongojs, mongoose, _, |
4 | | __slice = [].slice; |
5 | | |
6 | 1 | async = require('async'); |
7 | | |
8 | 1 | _ = require('underscore'); |
9 | | |
10 | 1 | mongoose = require('mongoose'); |
11 | | |
12 | 1 | mongojs = require('mongojs'); |
13 | | |
14 | 1 | ObjectId = mongoose.Schema.ObjectId; |
15 | | |
16 | 1 | _.mixin(require('underscore.plus')); |
17 | | |
18 | 1 | exports.create = function() { |
19 | 17 | var api, db, defModel, getKeys, getManyToMany, getMetaFields, getOwnedModels, getOwners, internalListSub, massage, massageCore, massageOne, massaged, meta, model, models, nullablesValidation, preRemoveCascadeNonNullable, preRemoveCascadeNullable, preprocFilter, propagate, specTransform; |
20 | 17 | db = null; |
21 | 17 | api = {}; |
22 | 17 | models = {}; |
23 | 17 | meta = {}; |
24 | 17 | propagate = function(callback, f) { |
25 | 9 | return function() { |
26 | 9 | var args, err; |
27 | 9 | err = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; |
28 | 9 | if (err) { |
29 | 0 | return callback(err); |
30 | | } else { |
31 | 9 | return f.apply(this, args); |
32 | | } |
33 | | }; |
34 | | }; |
35 | 17 | model = function(name, schema) { |
36 | 25 | var ss; |
37 | 25 | ss = new mongoose.Schema(schema, { |
38 | | strict: true |
39 | | }); |
40 | 25 | ss.set('versionKey', false); |
41 | 25 | return mongoose.model(name, ss); |
42 | | }; |
43 | 17 | api.isValidId = function(id) { |
44 | 2 | try { |
45 | 2 | mongoose.mongo.ObjectID(id); |
46 | 1 | return true; |
47 | | } catch (ex) { |
48 | 1 | return false; |
49 | | } |
50 | | }; |
51 | 17 | preprocFilter = function(filter) { |
52 | 31 | var x; |
53 | 31 | x = _.extend({}, filter); |
54 | 31 | if (x.id) { |
55 | 8 | x._id = x.id; |
56 | | } |
57 | 31 | delete x.id; |
58 | 31 | return x; |
59 | | }; |
60 | 17 | massageOne = function(x) { |
61 | 52 | if (!(x != null)) { |
62 | 0 | return x; |
63 | | } |
64 | 52 | x.id = x._id; |
65 | 52 | delete x._id; |
66 | 52 | return x; |
67 | | }; |
68 | 17 | massageCore = function(r2) { |
69 | 54 | if (Array.isArray(r2)) { |
70 | 19 | return r2.map(massageOne); |
71 | | } else { |
72 | 35 | return massageOne(r2); |
73 | | } |
74 | | }; |
75 | 17 | massage = function(r2) { |
76 | 54 | return massageCore(JSON.parse(JSON.stringify(r2))); |
77 | | }; |
78 | 17 | massaged = function(f) { |
79 | 34 | return function(err, data) { |
80 | 34 | if (err) { |
81 | 2 | return f(err); |
82 | | } else { |
83 | 32 | return f(null, massage(data)); |
84 | | } |
85 | | }; |
86 | | }; |
87 | 17 | api.connect = function(databaseUrl, callback) { |
88 | 10 | mongoose.connect(databaseUrl); |
89 | 10 | db = mongojs.connect(databaseUrl, api.getModels()); |
90 | 10 | return db[Object.keys(models)[0]].find(function(err) { |
91 | 10 | return callback(err); |
92 | | }); |
93 | | }; |
94 | 17 | api.close = function(callback) { |
95 | 10 | mongoose.connection.close(); |
96 | 10 | return callback(); |
97 | | }; |
98 | 17 | api.list = function(model, filter, callback) { |
99 | 11 | var rr; |
100 | 11 | filter = preprocFilter(filter); |
101 | 11 | rr = models[model].find(filter); |
102 | 11 | if (meta[model].defaultSort != null) { |
103 | 1 | rr = rr.sort(_.object([[meta[model].defaultSort, 'asc']])); |
104 | | } |
105 | 11 | return rr.exec(massaged(callback)); |
106 | | }; |
107 | 17 | api.getOne = function(model, config, callback) { |
108 | 14 | var filter; |
109 | 14 | filter = preprocFilter(config.filter); |
110 | 14 | return models[model].findOne(filter, function(err, data) { |
111 | 14 | if (err) { |
112 | 1 | if (err.toString() === 'Error: Invalid ObjectId') { |
113 | 1 | callback(new Error('No such id')); |
114 | | } else { |
115 | 0 | callback(err); |
116 | | } |
117 | 13 | } else if (!(data != null)) { |
118 | 1 | return callback(new Error('No match')); |
119 | | } else { |
120 | 12 | return callback(null, massage(data)); |
121 | | } |
122 | | }); |
123 | | }; |
124 | 17 | api.delOne = function(model, filter, callback) { |
125 | 2 | filter = preprocFilter(filter); |
126 | 2 | return models[model].findOne(filter, function(err, d) { |
127 | 2 | if (err) { |
128 | 1 | if (err.toString() === 'Error: Invalid ObjectId') { |
129 | 1 | return callback(new Error('No such id')); |
130 | | } else { |
131 | 0 | return callback(err); |
132 | | } |
133 | 1 | } else if (!(d != null)) { |
134 | 0 | return callback(new Error('No such id')); |
135 | | } else { |
136 | 1 | return d.remove(function(err) { |
137 | 1 | return callback(err, !err ? massage(d) : void 0); |
138 | | }); |
139 | | } |
140 | | }); |
141 | | }; |
142 | 17 | getKeys = function(data, target, prefix) { |
143 | 2 | var valids; |
144 | 2 | if (target == null) { |
145 | 1 | target = []; |
146 | | } |
147 | 2 | if (prefix == null) { |
148 | 1 | prefix = ''; |
149 | | } |
150 | 2 | valids = ['Array', 'String', 'Boolean', 'Date', 'Number', 'Null']; |
151 | 2 | Object.keys(data).forEach(function(key) { |
152 | 6 | if (valids.some(function(x) { |
153 | 20 | return _(data[key])['is' + x](); |
154 | | })) { |
155 | 5 | return target.push(prefix + key); |
156 | | } else { |
157 | 1 | return getKeys(data[key], target, prefix + key + '.'); |
158 | | } |
159 | | }); |
160 | 2 | return target; |
161 | | }; |
162 | 17 | api.putOne = function(modelName, data, filter, callback) { |
163 | 1 | var inputFields, inputFieldsValid, invalidFields, validField; |
164 | 1 | filter = preprocFilter(filter); |
165 | 1 | model = models[modelName]; |
166 | 1 | inputFieldsValid = getKeys(data); |
167 | 1 | inputFields = Object.keys(data); |
168 | 1 | validField = Object.keys(model.schema.paths); |
169 | 1 | invalidFields = _.difference(inputFieldsValid, validField); |
170 | 1 | if (invalidFields.length > 0) { |
171 | 0 | callback(new Error("Invalid fields: " + invalidFields.join(', '))); |
172 | 0 | return; |
173 | | } |
174 | 1 | return model.findOne(filter, propagate(callback, function(d) { |
175 | 1 | if (!(d != null)) { |
176 | 0 | callback(new Error("No such id")); |
177 | 0 | return; |
178 | | } |
179 | 1 | inputFields.forEach(function(key) { |
180 | 4 | return d[key] = data[key]; |
181 | | }); |
182 | 1 | return d.save(function(err) { |
183 | 1 | return callback(err, err ? null : massage(d)); |
184 | | }); |
185 | | })); |
186 | | }; |
187 | 17 | api.post = function(model, indata, callback) { |
188 | 23 | var owners, ownersOwners, ownersRaw, saveFunc; |
189 | 23 | saveFunc = function(data) { |
190 | 23 | return new models[model](data).save(function(err) { |
191 | 23 | var fieldMatch, valueMatch; |
192 | 23 | if (err && err.code === 11000) { |
193 | 0 | fieldMatch = err.err.match(/\$([a-zA-Z]+)_1/); |
194 | 0 | valueMatch = err.err.match(/"([a-zA-Z]+)"/); |
195 | 0 | if (fieldMatch && valueMatch) { |
196 | 0 | return callback(new Error("Duplicate value '" + valueMatch[1] + "' for " + fieldMatch[1])); |
197 | | } else { |
198 | 0 | return callback(new Error("Unique constraint violated")); |
199 | | } |
200 | | } else { |
201 | 23 | return massaged(callback).apply(this, arguments); |
202 | | } |
203 | | }); |
204 | | }; |
205 | 23 | ownersRaw = getOwners(model); |
206 | 23 | owners = _(ownersRaw).pluck('plur'); |
207 | 23 | ownersOwners = _(owners.map(function(x) { |
208 | 7 | return getOwners(x); |
209 | | })).flatten(); |
210 | 23 | if (ownersOwners.length === 0) { |
211 | 20 | return saveFunc(indata); |
212 | | } else { |
213 | 3 | return api.getOne(owners[0], { |
214 | | id: indata[ownersRaw[0].sing] |
215 | | }, function(err, ownerdata) { |
216 | 3 | var metaFields, paths; |
217 | 3 | paths = models[owners[0]].schema.paths; |
218 | 3 | metaFields = Object.keys(paths).filter(function(key) { |
219 | 14 | return !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner']; |
220 | | }); |
221 | 3 | metaFields.forEach(function(key) { |
222 | 4 | return indata[key] = ownerdata[key]; |
223 | | }); |
224 | 3 | return saveFunc(indata); |
225 | | }); |
226 | | } |
227 | | }; |
228 | 17 | internalListSub = function(model, outer, id, filter, callback) { |
229 | 3 | var finalFilter; |
230 | 3 | if (!(callback != null)) { |
231 | 3 | callback = filter; |
232 | 3 | filter = {}; |
233 | | } |
234 | 3 | if ((filter[outer] != null) && filter[outer].toString() !== id.toString()) { |
235 | 0 | callback(new Error('No such id')); |
236 | 0 | return; |
237 | | } |
238 | 3 | filter = preprocFilter(filter); |
239 | 3 | finalFilter = _.extend({}, filter, _.object([[outer, id]])); |
240 | 3 | return models[model].find(finalFilter, callback); |
241 | | }; |
242 | 17 | api.delMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) { |
243 | 1 | var inverseName, mm, secondaryModel; |
244 | 1 | mm = getManyToMany(primaryModel).filter(function(x) { |
245 | 1 | return x.name === propertyName; |
246 | | })[0]; |
247 | 1 | if (mm === null) { |
248 | 0 | callback(new Error('Invalid manyToMany-property')); |
249 | 0 | return; |
250 | | } |
251 | 1 | secondaryModel = mm.ref; |
252 | 1 | inverseName = mm.inverseName; |
253 | 1 | return async.forEach([ |
254 | | { |
255 | | model: primaryModel, |
256 | | id: primaryId, |
257 | | property: propertyName, |
258 | | secondaryId: secondaryId |
259 | | }, { |
260 | | model: secondaryModel, |
261 | | id: secondaryId, |
262 | | property: inverseName, |
263 | | secondaryId: primaryId |
264 | | } |
265 | | ], function(item, callback) { |
266 | 2 | return models[item.model].findById(item.id, propagate(callback, function(data) { |
267 | 2 | var conditions, options, update; |
268 | 2 | conditions = { |
269 | | _id: item.id |
270 | | }; |
271 | 2 | update = { |
272 | | $pull: _.object([[item.property, item.secondaryId]]) |
273 | | }; |
274 | 2 | options = {}; |
275 | 2 | return models[item.model].update(conditions, update, options, function(err, numAffected) { |
276 | 2 | return callback(err); |
277 | | }); |
278 | | })); |
279 | | }, callback); |
280 | | }; |
281 | 17 | api.postMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) { |
282 | 2 | var inverseName, mm, secondaryModel; |
283 | 2 | mm = getManyToMany(primaryModel).filter(function(x) { |
284 | 2 | return x.name === propertyName; |
285 | | })[0]; |
286 | 2 | if (mm === null) { |
287 | 0 | callback(new Error('Invalid manyToMany-property')); |
288 | 0 | return; |
289 | | } |
290 | 2 | secondaryModel = mm.ref; |
291 | 2 | inverseName = mm.inverseName; |
292 | 2 | return models[primaryModel].findById(primaryId, propagate(callback, function(data) { |
293 | 2 | return models[secondaryModel].findById(secondaryId, propagate(callback, function(data2) { |
294 | 2 | if (-1 === data[propertyName].indexOf(secondaryId)) { |
295 | 2 | data[propertyName].push(secondaryId); |
296 | | } |
297 | 2 | if (-1 === data2[inverseName].indexOf(primaryId)) { |
298 | 2 | data2[inverseName].push(primaryId); |
299 | | } |
300 | 2 | return data.save(propagate(callback, function() { |
301 | 2 | return data2.save(function(err) { |
302 | 2 | return callback(err, {}); |
303 | | }); |
304 | | })); |
305 | | })); |
306 | | })); |
307 | | }; |
308 | 17 | api.getMany = function(primaryModel, primaryId, propertyName, callback) { |
309 | 8 | return models[primaryModel].findOne({ |
310 | | _id: primaryId |
311 | | }).populate(propertyName).exec(function(err, story) { |
312 | 8 | return callback(err, massage(story[propertyName])); |
313 | | }); |
314 | | }; |
315 | 17 | specTransform = function(allspec, modelName, tgt, src, keys) { |
316 | 37 | return keys.forEach(function(key) { |
317 | 50 | var obj; |
318 | 50 | if (typeof src[key] === 'string') { |
319 | 10 | obj = {}; |
320 | 10 | obj[key] = { |
321 | | type: src[key] |
322 | | }; |
323 | 10 | return specTransform(allspec, modelName, tgt, obj, [key]); |
324 | 40 | } else if (!(src[key].type != null)) { |
325 | 0 | throw new Error("must assign a type: " + JSON.stringify(keys)); |
326 | 40 | } else if (src[key].type === 'mixed') { |
327 | 2 | return tgt[key] = { |
328 | | type: mongoose.Schema.Types.Mixed |
329 | | }; |
330 | 38 | } else if (src[key].type === 'nested') { |
331 | 1 | tgt[key] = {}; |
332 | 1 | return specTransform(allspec, modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type')); |
333 | 37 | } else if (src[key].type === 'string') { |
334 | 28 | tgt[key] = { |
335 | | type: String, |
336 | | required: !!src[key].required |
337 | | }; |
338 | 28 | if (src[key]["default"] != null) { |
339 | 18 | tgt[key]["default"] = src[key]["default"]; |
340 | | } |
341 | 28 | if (src[key].index != null) { |
342 | 0 | tgt[key].index = src[key].index; |
343 | | } |
344 | 28 | if (src[key].unique != null) { |
345 | 0 | tgt[key].unique = src[key].unique; |
346 | | } |
347 | 28 | if (src[key].validate != null) { |
348 | 1 | return tgt[key].validate = function(value, callback) { |
349 | 2 | return src[key].validate(api, value, callback); |
350 | | }; |
351 | | } |
352 | 9 | } else if (src[key].type === 'number') { |
353 | 3 | tgt[key] = { |
354 | | type: Number, |
355 | | required: !!src[key].required |
356 | | }; |
357 | 3 | if (src[key]["default"] != null) { |
358 | 0 | tgt[key]["default"] = src[key]["default"]; |
359 | | } |
360 | 3 | if (src[key].index != null) { |
361 | 0 | tgt[key].index = src[key].index; |
362 | | } |
363 | 3 | if (src[key].unique != null) { |
364 | 0 | return tgt[key].unique = src[key].unique; |
365 | | } |
366 | 6 | } else if (src[key].type === 'date') { |
367 | 1 | tgt[key] = { |
368 | | type: Date, |
369 | | required: !!src[key].required |
370 | | }; |
371 | 1 | if (src[key]["default"] != null) { |
372 | 0 | tgt[key]["default"] = src[key]["default"]; |
373 | | } |
374 | 1 | if (src[key].index != null) { |
375 | 0 | tgt[key].index = src[key].index; |
376 | | } |
377 | 1 | if (src[key].unique != null) { |
378 | 0 | return tgt[key].unique = src[key].unique; |
379 | | } |
380 | 5 | } else if (src[key].type === 'boolean') { |
381 | 1 | tgt[key] = { |
382 | | type: Boolean, |
383 | | required: !!src[key].required |
384 | | }; |
385 | 1 | if (src[key]["default"] != null) { |
386 | 0 | tgt[key]["default"] = src[key]["default"]; |
387 | | } |
388 | 1 | if (src[key].index != null) { |
389 | 0 | tgt[key].index = src[key].index; |
390 | | } |
391 | 1 | if (src[key].unique != null) { |
392 | 0 | return tgt[key].unique = src[key].unique; |
393 | | } |
394 | 4 | } else if (src[key].type === 'hasOne') { |
395 | 0 | return tgt[key] = { |
396 | | ref: src[key].model, |
397 | | 'x-validation': src[key].validation |
398 | | }; |
399 | 4 | } else if (src[key].type === 'hasMany') { |
400 | 3 | tgt[key] = [ |
401 | | { |
402 | | type: ObjectId, |
403 | | ref: src[key].model, |
404 | | inverseName: src[key].inverseName || key |
405 | | } |
406 | | ]; |
407 | 3 | return allspec[src[key].model][src[key].inverseName || key] = [ |
408 | | { |
409 | | type: ObjectId, |
410 | | ref: modelName, |
411 | | inverseName: key |
412 | | } |
413 | | ]; |
414 | | } else { |
415 | 1 | throw new Error("Invalid type: " + src[key].type); |
416 | | } |
417 | | }); |
418 | | }; |
419 | 17 | api.defModels = function(models) { |
420 | 14 | var allspec, rest; |
421 | 14 | allspec = {}; |
422 | 14 | Object.keys(models).forEach(function(modelName) { |
423 | 26 | return allspec[modelName] = {}; |
424 | | }); |
425 | 14 | rest = {}; |
426 | 14 | Object.keys(models).forEach(function(modelName) { |
427 | 26 | var inspec, owners, spec; |
428 | 26 | spec = allspec[modelName]; |
429 | 26 | owners = models[modelName].owners || {}; |
430 | 26 | inspec = models[modelName].fields || {}; |
431 | 26 | specTransform(allspec, modelName, spec, inspec, Object.keys(inspec)); |
432 | 25 | return rest[modelName] = { |
433 | | owners: owners, |
434 | | spec: spec |
435 | | }; |
436 | | }); |
437 | 13 | return Object.keys(models).forEach(function(modelName) { |
438 | 25 | return defModel(modelName, models[modelName], rest[modelName].spec, rest[modelName].owners); |
439 | | }); |
440 | | }; |
441 | 17 | defModel = function(name, conf, spec, owners) { |
442 | 25 | Object.keys(owners).forEach(function(ownerName) { |
443 | 9 | return spec[ownerName] = { |
444 | | type: ObjectId, |
445 | | ref: owners[ownerName], |
446 | | required: true, |
447 | | 'x-owner': true |
448 | | }; |
449 | | }); |
450 | 25 | Object.keys(owners).forEach(function(ownerName) { |
451 | 9 | var paths; |
452 | 9 | paths = models[owners[ownerName]].schema.paths; |
453 | 9 | return Object.keys(paths).filter(function(p) { |
454 | 24 | return paths[p].options['x-owner'] || paths[p].options['x-indirect-owner']; |
455 | | }).forEach(function(p) { |
456 | 4 | return spec[p] = { |
457 | | type: ObjectId, |
458 | | ref: paths[p].options.ref, |
459 | | required: true, |
460 | | 'x-indirect-owner': true |
461 | | }; |
462 | | }); |
463 | | }); |
464 | 25 | Object.keys(spec).forEach(function(fieldName) { |
465 | 53 | if (spec[fieldName].ref != null) { |
466 | 13 | return spec[fieldName].type = ObjectId; |
467 | | } |
468 | | }); |
469 | 25 | meta[name] = { |
470 | | defaultSort: conf.defaultSort |
471 | | }; |
472 | 25 | models[name] = model(name, spec); |
473 | 25 | models[name].schema.pre('save', nullablesValidation(models[name].schema)); |
474 | 25 | models[name].schema.pre('remove', function(next) { |
475 | 3 | return preRemoveCascadeNonNullable(models[name], this._id.toString(), next); |
476 | | }); |
477 | 25 | return models[name].schema.pre('remove', function(next) { |
478 | 3 | return preRemoveCascadeNullable(models[name], this._id.toString(), next); |
479 | | }); |
480 | | }; |
481 | 17 | getMetaFields = function(modelName) { |
482 | 3 | var metaFields, paths, typeFunc, typeMap; |
483 | 3 | typeMap = { |
484 | | ObjectID: 'string', |
485 | | String: 'string', |
486 | | Number: 'number', |
487 | | Boolean: 'boolean', |
488 | | Date: 'date' |
489 | | }; |
490 | 3 | paths = models[modelName].schema.paths; |
491 | 3 | typeFunc = function(x) { |
492 | 0 | if (x === Boolean) { |
493 | 0 | return 'boolean'; |
494 | | } |
495 | 0 | if (x === Date) { |
496 | 0 | return 'date'; |
497 | | } |
498 | | }; |
499 | 3 | metaFields = Object.keys(paths).filter(function(key) { |
500 | 10 | return !Array.isArray(paths[key].options.type); |
501 | | }).map(function(key) { |
502 | 8 | return { |
503 | | name: (key === '_id' ? 'id' : key), |
504 | | readonly: key === '_id' || !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'], |
505 | | required: !!paths[key].options.required, |
506 | | type: typeMap[paths[key].instance] || typeFunc(paths[key].options.type) || 'unknown' |
507 | | }; |
508 | | }); |
509 | 3 | return _.sortBy(metaFields, 'name'); |
510 | | }; |
511 | 17 | getOwners = function(modelName) { |
512 | 33 | var outers, paths; |
513 | 33 | paths = models[modelName].schema.paths; |
514 | 33 | outers = Object.keys(paths).filter(function(x) { |
515 | 112 | return paths[x].options['x-owner']; |
516 | | }).map(function(x) { |
517 | 11 | return { |
518 | | plur: paths[x].options.ref, |
519 | | sing: x |
520 | | }; |
521 | | }); |
522 | 33 | return outers; |
523 | | }; |
524 | 17 | getOwnedModels = function(ownerModelName) { |
525 | 6 | return _.flatten(Object.keys(models).map(function(modelName) { |
526 | 21 | var paths; |
527 | 21 | paths = models[modelName].schema.paths; |
528 | 21 | return Object.keys(paths).filter(function(x) { |
529 | 78 | return paths[x].options.type === ObjectId && paths[x].options.ref === ownerModelName && paths[x].options['x-owner']; |
530 | | }).map(function(x) { |
531 | 3 | return { |
532 | | name: modelName, |
533 | | field: x |
534 | | }; |
535 | | }); |
536 | | })); |
537 | | }; |
538 | 17 | getManyToMany = function(modelName) { |
539 | 6 | var manyToMany, paths; |
540 | 6 | paths = models[modelName].schema.paths; |
541 | 6 | manyToMany = Object.keys(paths).filter(function(x) { |
542 | 19 | return Array.isArray(paths[x].options.type); |
543 | | }).map(function(x) { |
544 | 5 | return { |
545 | | inverseName: paths[x].options.type[0].inverseName, |
546 | | ref: paths[x].options.type[0].ref, |
547 | | name: x |
548 | | }; |
549 | | }); |
550 | 6 | return manyToMany; |
551 | | }; |
552 | 17 | api.getMeta = function(modelName) { |
553 | 3 | return { |
554 | | fields: getMetaFields(modelName), |
555 | | owns: getOwnedModels(modelName), |
556 | | owners: getOwners(modelName), |
557 | | manyToMany: getManyToMany(modelName) |
558 | | }; |
559 | | }; |
560 | 17 | api.getModels = function() { |
561 | 12 | return Object.keys(models); |
562 | | }; |
563 | 17 | nullablesValidation = function(schema) { |
564 | 25 | return function(next) { |
565 | 35 | var nonNullOuters, outers, paths, self; |
566 | 35 | self = this; |
567 | 35 | paths = schema.paths; |
568 | 35 | outers = Object.keys(paths).filter(function(x) { |
569 | 116 | return paths[x].options.type === ObjectId && typeof paths[x].options.ref === 'string' && !paths[x].options['x-owner']; |
570 | | }).map(function(x) { |
571 | 4 | return { |
572 | | plur: paths[x].options.ref, |
573 | | sing: x, |
574 | | validation: paths[x].options['x-validation'] |
575 | | }; |
576 | | }); |
577 | 35 | nonNullOuters = outers.filter(function(x) { |
578 | 4 | return self[x.sing] != null; |
579 | | }); |
580 | 35 | return async.forEach(nonNullOuters, function(o, callback) { |
581 | 4 | return api.getOne(o.plur, { |
582 | | id: self[o.sing] |
583 | | }, function(err, data) { |
584 | 4 | if (err || !data) { |
585 | 0 | return callback(new Error("Invalid pointer")); |
586 | 4 | } else if (o.validation) { |
587 | 0 | return o.validation(self, data, function(err) { |
588 | 0 | return callback(err ? new Error(err) : void 0); |
589 | | }); |
590 | | } else { |
591 | 4 | return callback(); |
592 | | } |
593 | | }); |
594 | | }, next); |
595 | | }; |
596 | | }; |
597 | 17 | preRemoveCascadeNonNullable = function(owner, id, next) { |
598 | 3 | var flattenedModels; |
599 | 3 | flattenedModels = getOwnedModels(owner.modelName); |
600 | 3 | return async.forEach(flattenedModels, function(mod, callback) { |
601 | 2 | return internalListSub(mod.name, mod.field, id, function(err, data) { |
602 | 2 | return async.forEach(data, function(item, callback) { |
603 | 2 | return item.remove(callback); |
604 | | }, callback); |
605 | | }); |
606 | | }, next); |
607 | | }; |
608 | 17 | preRemoveCascadeNullable = function(owner, id, next) { |
609 | 3 | var flattenedModels, ownedModels; |
610 | 3 | ownedModels = Object.keys(models).map(function(modelName) { |
611 | 12 | var paths; |
612 | 12 | paths = models[modelName].schema.paths; |
613 | 12 | return Object.keys(paths).filter(function(x) { |
614 | 48 | return paths[x].options.type === ObjectId && paths[x].options.ref === owner.modelName && !paths[x].options['x-owner']; |
615 | | }).map(function(x) { |
616 | 1 | return { |
617 | | name: modelName, |
618 | | field: x |
619 | | }; |
620 | | }); |
621 | | }); |
622 | 3 | flattenedModels = _.flatten(ownedModels); |
623 | 3 | return async.forEach(flattenedModels, function(mod, callback) { |
624 | 1 | return internalListSub(mod.name, mod.field, id, function(err, data) { |
625 | 1 | return async.forEach(data, function(item, callback) { |
626 | 0 | item[mod.field] = null; |
627 | 0 | item.save(); |
628 | 0 | return callback(); |
629 | | }, callback); |
630 | | }); |
631 | | }, next); |
632 | | }; |
633 | 17 | return api; |
634 | | }; |
635 | | |
636 | | }).call(this); |