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