示例#1
0
    QUnit.test('`lodash ' + command +'`', function(assert) {
      var done = assert.async(),
          start = _.after(2, _.once(done));

      build(command.split(' '), function(data) {
        var actualId,
            basename = path.basename(data.outputPath, '.js'),
            context = createContext('amd');

        context.define = function(id, factory) {
          actualId = id;
          context._ = factory();
        };

        context.define.amd = {};
        vm.runInContext(data.source, context);

        assert.strictEqual(actualId, expectedId, basename);
        assert.ok(_.isFunction(context._), basename);
        start();
      });
    });
示例#2
0
    QUnit.test('`lodash ' + command +'`', function(assert) {
      var counter = 0,
          dirs = _.includes(command, 'c.js'),
          done = assert.async(),
          expected = /(\w+)(?=\.js$)/.exec(command)[0];

      var start = _.after(2, _.once(function() {
        if (dirs) {
          fs.rmrfSync(outputPath);
        }
        process.chdir(cwd);
        done();
      }));

      process.chdir(__dirname);

      build(command.split(' '), function(data) {
        var basename = path.basename(data.outputPath, '.js');
        assert.strictEqual(basename, expected + (counter++ ? '.min' : ''), command);
        start();
      });
    });
示例#3
0
  render('filelist.json', (list) => {
    const files = JSON.parse(list),
          done = _.after(files.length, installModules);

    files.forEach((i) => render(`templates/${i}`, (data) => {
      mkdirp(path.dirname(i), dirErr => {
        if (dirErr) {
          console.log(chalk.bold.red(' \u2718 ') + i);
          throw dirErr;
        }

        fs.writeFile(i, data, writeErr => {
          if (writeErr) {
            console.log(i + chalk.bold.red(' \u2718 '));
            throw writeErr;
          }
          console.log(i + chalk.bold.green(' \u2714 '));
          done();
        });
      });
    }));
  });
示例#4
0
Packages.link = function (packages, callback) {
  shell.mkdir('-p', PACKAGE_DIR);

  var dirLinked = _.after(_.keys(packages).length, callback);

  _.forOwn(packages, function (def, packageName) {
    if (!def.path || !packageName) return;

    // Convert colons in package names to dashes for Windows
    packageName = packageName.replace(/:/g, '-');
    var dest = PACKAGE_DIR + '/' + packageName;
    shell.rm('-fr', dest);

    var src = resolvePath(def.path);
    checkPathExist(src, 'Cannot find package ' + packageName + ' at ' + src);

    shell.ln('-s', src, dest);
    checkPathExist(src, 'Link failed for ' + dest);

    dirLinked();
  });
};
示例#5
0
		"webpcss", "Process css file to generate addition css ruless to add webp compatble", function () {
    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      baseClass:".webp",
      replace_from:/\.(png|jpg|jpeg)/,
      replace_to:".webp"
    });

    var done = _.after(this.files.length, this.async());

    // Iterate over all specified file groups.
    this.files.forEach(function (f) {
      // Concat specified files.
      var src = f.src.filter(function (filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn("Source file \"" + filepath + "\" not found.");
          return false;
        } else {
          return true;
        }
      }).map(function (filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).join("\n");

      webpcss.transform(src, options)
        .then(function (res) {
          // Write the destination file.
          grunt.file.write(f.dest, res);

          // Print a success message.
          grunt.log.writeln("File \"" + f.dest + "\" created.");
          done();
        })
        .catch(done);
    });
  });
示例#6
0
    it("should all succeed, no output dir pre-exists", function (done) {
      var options = {
        source: inputFile,
        hostname: "localhost",
        port: port,
        selector: "#dynamic-content",
        outputDir: outputDir,
        outputDirClean: true,
        timeout: timeout
      };
      var twice = _.after(2, cleanupSuccess.bind(null, done));

      rimraf(outputDir);

      ss.run(optHelp.decorate(options), twice)
        .then(testSuccess.bind(null, twice))
        .catch(function (e) {
          checkActualFiles(e.notCompleted)
            .then(function () {
              cleanup(done, e);
            });
        });
    });
示例#7
0
文件: index.js 项目: mafintosh/yunodb
Yuno.prototype.add = function (docs, opts, cb) {
  var self = this
  if (_.isFunction(opts)) cb = opts
  if (_.isPlainObject(docs)) docs = [docs]

  var errs = []
  var docb = _.after(2, function () {
    cb(errs.length > 0 ? errs[0] : null, docs.length)
  })
  var done = function (err) {
    if (err) errs.push(err)
    docb()
  }

  this.docstore.batch(docs.map((d) => {
    return { type: 'put', key: '' + d[self.keyField], value: JSON.stringify(d) }
  }), done)

  this.index.add(docs.map((d) => {
    return { id: d[self.keyField], tokens: self.preprocessor.process(d) }
  }), done)
  // process the docs for search indexing
}
示例#8
0
var updateAppAdmins = module.exports.updateAppAdmins = function(adminUpdates, callback) {
    var errors = [];

    var adminsUpdated = _.after(_.keys(adminUpdates).length, function() {
        if (errors.length > 0) {
            return callback({'code': 500, 'msg': errors[0].message, 'errors': errors});
        }

        return callback();
    });

    _.each(adminUpdates, function(adminStatus, userId) {
        // TODO: Wrap this in a transaction
        DB.User.update({'isAdmin': adminStatus}, {'where': {'id': userId}}).complete(function(err) {
            if (err) {
                log().error({'err': err}, 'Failed to update an administrator of an app');
                errors.push(err);
            }

            return adminsUpdated();
        });
    });
};
示例#9
0
module.exports.close = function (cb) {
  var numResponses = 0
  , done
  ;
  
  if (server) {
    numResponses++;
  }
  
  if (db) {
    numResponses++;
  }
  
  done = _.after(numResponses, cb);
  
  if (server) {
    server.close(done);
  }
  
  if (db) {
    db.close(done);
  }
};
示例#10
0
    QUnit.test('`lodash exports=amd' + (command ? ' ' + command + '`' : '` using the default `moduleId`'), function(assert) {
      var done = assert.async(),
          start = _.after(2, _.once(done));

      build(['template=' + path.join(templatePath, '*.jst'), 'exports=amd'].concat(command || []), function(data) {
        var actualId,
            basename = path.basename(data.outputPath, '.js'),
            context = createContext('amd');

        context.define = function(requires, factory) {
          factory(_);
          actualId = requires[0];
        };

        context.define.amd = {};
        vm.runInContext(data.source, context);

        assert.strictEqual(actualId, expectedId, basename);

        delete _.templates;
        start();
      });
    });
示例#11
0
      it('always honors ZERO as primary key', function(_done) {
        var self = this
          , permutations = [
            0,
            '0',
            {where: {id: 0}},
            {where: {id: '0'}}
          ]
          , done = _.after(2 * permutations.length, _done);

        this.User.bulkCreate([{username: '******'}, {username: '******'}]).success(function() {
          permutations.forEach(function(perm) {
            self.User.find(perm).done(function(err, user) {
              expect(err).to.be.null;
              expect(user).to.be.null;
              done();
            }).on('sql', function(s) {
              expect(s.indexOf(0)).not.to.equal(-1);
              done();
            });
          });
        });
      });
示例#12
0
        before(function (done) {
            var trigger = _.after(3, done);
            Group.find({}).remove(function (err) {
                expect(err).to.be(null);
                trigger();
            });

            User.find({}).remove(function (err) {
                expect(err).to.be(null);
                var user1 = {
                    name: 'Full name',
                    email: '*****@*****.**',
                    username: '******',
                    password: '******',
                    provider: 'local'
                };
                _user1 = new User(user1);
                _user1.save(function (err) {
                    expect(err).to.be(null);
                    trigger();
                });

                var user2 = {
                    name: 'Full name',
                    email: '*****@*****.**',
                    username: '******',
                    password: '******',
                    provider: 'local'
                };
                _user2 = new User(user2);
                _user2.save(function (err) {
                    expect(err).to.be(null);
                    trigger();
                });

            });
        });
示例#13
0
文件: util.js 项目: bp323/grasshopper
        globalAdminClient.tenant.createTenant(tenantDisplayName, function(err, tenant) {
            assert.ok(!err);

            if (nrOfApps === 0) {
                return callback(tenant);
            }

            var args = [tenant];

            // Gets executed once all the applications have been created
            var done = _.after(nrOfApps, function() {
                return callback.apply(callback, args);
            });

            // Create a number of applications
            _.times(nrOfApps, function(n) {
                var appDisplayName = util.format('%s %d', tenantDisplayName, n);
                var host = util.format('%d.%s.grasshopper.local', n, tenantDisplayName);
                _generateTestApp(globalAdminClient, tenant.id, appDisplayName, host, function(app) {
                    args.push(app);
                    return done();
                });
            });
        });
示例#14
0
    QUnit.test('`lodash ' + command +'`', function(assert) {
      var done = assert.async();

      var start = _.after(2, _.once(function() {
        process.chdir(cwd);
        done();
      }));

      process.chdir(reWildcard.test(command) ? templatePath : __dirname);

      build([command], function(data) {
        var basename = path.basename(data.outputPath, '.js'),
            context = createContext();

        var object = {
          'a': { 'people': ['fred', 'barney', 'pebbles'] },
          'b': { 'name': 'fred' },
          'c': { 'name': 'es' }
        };

        context._ = _;
        vm.runInContext(data.source, context);

        assert.ok(_.includes(basename, 'lodash.templates'), basename);

        var actual = _.templates.a(object.a);
        assert.strictEqual(actual.replace(/[\r\n]+/g, ''), '<ul><li>fred</li><li>barney</li><li>pebbles</li></ul>', basename);

        assert.strictEqual(_.templates.b(object.b), 'hello fred!', basename);
        assert.strictEqual(_.templates.c(object.c), 'hello es', basename);
        assert.deepEqual(_.difference(['a', 'b', 'c', 'd', 'e'], _.keys(_.templates)), [], basename);

        delete _.templates;
        start();
      });
    });
示例#15
0
文件: index.js 项目: PhEEP/huepi
var displayResult = function(result) {
    console.log('inside displayResult');
    var lights = result.lights;
    var len = lights.length;
    var lightlist = [];
    var finished = _.after(len, function(data) {
        /* GET home page. */
        router.get('/', function(req, res, next) {
            res.render('index', {
                title: 'Huepi',
                lights: data
            });
        });
        // console.log(JSON.stringify(result, null, 2));
    });

    _.forEach(lights, function(value, key) {
        console.log(value);
        var o = {
            id: value.id,
            on: value.state.on,
            rgb: api.getRGBfromXY(value.state.xy[0], value.state.xy[1], value.state.bri),
            name: value.name
                // h: Math.floor(value.state.hue / 182),
                // s: Math.floor((value.state.sat / 255) * 100),
                // l: Math.floor((value.state.bri / 255) * 60)
        };
        lightlist.push(o);
        finished(lightlist);
        // api.lightStatusWithRGB(parseInt(value.id)).done(function(result) {
        //     console.log(result.state);
        //     o.rgb = result.state.rgb;
        //
        // }).bind(null, o);
    });
};
示例#16
0
    it('should be able to instantiate to distinct loggers', function (done) {
        var logger1 = Logger.init({
                adapters: [{
                    type: 'callback',
                    application: 'logger1',
                    machine: 'staging',
                    callback: callback1
                }]
            }),
            logger2 = Logger.init({
                adapters: [{
                    type: 'callback',
                    application: 'logger2',
                    machine: 'staging',
                    callback: callback2
                }]
            }),
            doneOne = _.after(4, done);


        logger1.debug('test1');
        logger2.debug('test2');
        logger1.debug('test1');
        logger2.debug('test2');


        function callback1(msg) {
            msg.application.should.equal('LOGGER1');
            doneOne();
        }

        function callback2(msg) {
            msg.application.should.equal('LOGGER2');
            doneOne();
        }
    });
function loadStationDetail(quality, callback) {
    var stationRecords = [];
    var stations = quality.stations;
    if (stations == null) {
        return callback(null, quality);
    }
    var done = _.after(stations.length, function() {
        quality.stations = stationRecords;
        //console.log('done load station detail : ' + JSON.stringify(quality));
        return callback(null, quality);
    });
    _.each(stations, function (station) {
        Station.findById(station)
            .select('-_id -__v')
            .exec(function (error, stationRecord) {
            if (error) {
                console.log("Fail to load summary : " + station);
            } else {
                stationRecords.push(stationRecord);
            }
            done();
        });
    });
}
示例#18
0
文件: gekko.js 项目: rvienna/gekko
var configureManagers = function(_next) {
  var amount = _.size(managers);
  if(!amount)
    return _next();

  var next = _.after(amount, _next);
  _.each(managers, function(conf) {
    conf.exchange = conf.exchange.toLowerCase();

    // make sure we the exchange is configured correctly
    // for trading.
    var invalid = exchangeChecker.cantTrade(conf);
    if(invalid)
      throw invalid;

    if(conf.exchange === 'bitstamp')
      throw 'Live trading currently broken at Bitstamp! :(';

    var manager = new Manager(conf);

    consultant.on('advice', manager.trade);
    manager.on('ready', next);
  });
}
示例#19
0
    QUnit.test('`lodash settings=...' + (command ? ' ' + command : '') + '`', function(assert) {
      var done = assert.async(),
          start = _.after(2, _.once(done));

      build(['template=' + path.join(templatePath, '*.tpl'), 'settings={interpolate:/{{([\\s\\S]+?)}}/}'].concat(command || []), function(data) {
        var actualId,
            basename = path.basename(data.outputPath, '.js'),
            context = createContext('amd');

        context.define = function(requires, factory) {
          factory(_);
          actualId = requires[0];
        };

        context.define.amd = {};
        vm.runInContext(data.source, context);

        assert.strictEqual(actualId, expectedId, basename);
        assert.strictEqual(_.templates.f({ 'name': 'mustache' }), 'hall\xe5 mustache!', basename);

        delete _.templates;
        start();
      });
    });
示例#20
0
 }, function(er, files) {
     var deferredComplete = _.after(files.length, complete);
     _.each(files, function(bowerJsonPath) {
         var dir = path.dirname(bowerJsonPath);
         var bowerComponentsDir = path.join(dir, "bower_components");
         gutil.log("Bower start:", bowerJsonPath);
         if (!fs.existsSync(bowerComponentsDir)) {
             fs.mkdirSync(bowerComponentsDir);
             gutil.log("  mkdir:", bowerComponentsDir);
         }
         var install = bower.commands.install(
             [],
             {},
             {cwd: dir, directory: "bower_components"}
         );
         install.on("log", function(log) {
             gutil.log("Bower/" + bowerJsonPath + ":" + log.message);
         });
         install.on("end", function() {
             gutil.log("Bower complete:", bowerJsonPath);
             deferredComplete();
         });
     });
 });
示例#21
0
    QUnit.test('recursive path `' + command + '`', function(assert) {
      var done = assert.async();

      var start = _.after(2, _.once(function() {
        if (!isWindows) {
          fs.unlinkSync(quotesTemplatePath);
        }
        process.chdir(cwd);
        done();
      }));

      if (index) {
        process.chdir(templatePath);
      }
      if (!isWindows) {
        // Manually create template `'".jst` to avoid issues in Windows.
        fs.writeFileSync(quotesTemplatePath, 'hello <%= name %>', 'utf8');
      }
      build([command], function(data) {
        var basename = path.basename(data.outputPath, '.js'),
            context = createContext();

        context._ = _;
        vm.runInContext(data.source, context);

        assert.strictEqual(_.templates.b({ 'name': 'fred' }), 'hello fred!', basename);
        assert.strictEqual(_.templates.c({ 'name': 'barney' }), 'hello barney', basename);
        assert.strictEqual(_.templates.c.c({ 'name': 'pebbles' }), 'hello pebbles!', basename);

        if (!isWindows) {
          assert.strictEqual(_.templates.c['\'"']({ 'name': 'quotes' }), 'hello quotes', basename);
        }
        delete _.templates;
        start();
      });
    });
示例#22
0
    it('always honors ZERO as primary key', function(_done) {
      var permutations = [
          0,
          '0',
          {where: {id: 0}},
          {where: {id: '0'}}
        ]
        , done = _.after(2 * permutations.length, _done);

      this.User.create({name: 'jack'}).success(function (jack) {
        this.User.create({name: 'jill'}).success(function (jill) {
          permutations.forEach(function(perm) {
            this.User.find(perm).done(function(err, user) {
              expect(err).toBeNull();
              expect(user).toBeNull();
              done();
            }).on('sql', function(s) {
              expect(s.indexOf(0)).not.toEqual(-1);
              done();
            })
          }.bind(this))
        }.bind(this))
      }.bind(this))
    })
示例#23
0
var generatePng = function(whiteboardElements, callback) {
  // Variables that will keep track of what the outer corners of the elements in the canvas are
  var left = Number.MAX_VALUE;
  var top = Number.MAX_VALUE;
  var right = Number.MIN_VALUE;
  var bottom = Number.MIN_VALUE;

  // A variable that will keep track of all the deserialized elements in the whiteboard
  var deserializedElements = [];

  var render = _.after(whiteboardElements.length, function() {
    // At this point we've figured out what the left-most and right-most element is. By subtracting
    // their X-coordinates we get the desired width of the canvas. The height can be calculated in
    // a similar way by using the Y-coordinates
    var width = right - left;
    var height = bottom - top;

    // Add a bit of padding so elements don't stick to the side
    width += (2 * WHITEBOARD_PADDING);
    height += (2 * WHITEBOARD_PADDING);

    // Create a canvas and pan it to the top-left corner
    var canvas = createCanvas(width, height);
    var pt = new fabric.Point(left - WHITEBOARD_PADDING, top - WHITEBOARD_PADDING);
    canvas.absolutePan(pt);

    // Don't render each element when it's added, rather render the entire Canvas once all elements
    // have been added. This is significantly faster
    canvas.renderOnAddRemove = false;

    // Once all elements have been added to the canvas, restore
    // the layer order and convert to PNG
    var finishRender = _.after(deserializedElements.length, function() {
      // Ensure each element is placed at the right index. This can only happen
      // once all elements have been added to the canvas
      canvas.getObjects().sort(function(elementA, elementB) {
        return elementA.index - elementB.index;
      });

      // Render the canvas
      canvas.renderAll();

      // Convert the canvas to a PNG file and return the data
      return canvas.nodeCanvas.toBuffer(callback);
    });

    // Add each element to the canvas
    _.each(deserializedElements, function(deserializedElement) {
      canvas.add(deserializedElement);
      finishRender();
    });
  });

  _.each(whiteboardElements, function(whiteboardElement) {
    // Canvas doesn't seem to deal terribly well with text elements that specify a prioritized list
    // of font family names. It seems that the only way to render custom fonts is to only specify one
    if (whiteboardElement.fontFamily) {
      whiteboardElement.fontFamily = 'HelveticaNeue-Light';
    }

    // Deserialize the element, get its boundary and check how large
    // the canvas should be to display the element entirely
    deserializeElement(whiteboardElement, function(deserializedElement) {
      var bound = deserializedElement.getBoundingRect();

      left = Math.min(left, bound.left);
      top = Math.min(top, bound.top);
      right = Math.max(right, bound.left + bound.width);
      bottom = Math.max(bottom, bound.top + bound.height);

      // Retain a reference to the deserialized elements. This allows for moving each element
      // to the right index once all alements have been added to the canvas
      deserializedElements.push(deserializedElement);

      render();
    });
  });
};
示例#24
0
          _.forEach(models, (definition, model) => {
            definition.globalName = _.upperFirst(_.camelCase(definition.globalId));

            // Make sure the model has a connection.
            // If not, use the default connection.
            if (_.isEmpty(definition.connection)) {
              definition.connection = strapi.config.currentEnvironment.database.defaultConnection;
            }

            // Make sure this connection exists.
            if (!_.has(strapi.config.connections, definition.connection)) {
              strapi.log.error('The connection `' + definition.connection + '` specified in the `' + model + '` model does not exist.');
              strapi.stop();
            }

            // Add some informations about ORM & client connection
            definition.orm = 'mongoose';
            definition.client = _.get(strapi.config.connections[definition.connection], 'client');
            definition.associations = [];

            // Register the final model for Mongoose.
            definition.loadedModel = _.cloneDeep(definition.attributes);

            // Initialize the global variable with the
            // capitalized model name.
            if (!plugin) {
              global[definition.globalName] = {};
            }

            if (_.isEmpty(definition.attributes)) {
              // Generate empty schema
              _.set(strapi.config.hook.settings.mongoose, 'collections.' + mongooseUtils.toCollectionName(definition.globalName) + '.schema', new instance.Schema({}));

              return loadedAttributes();
            }

            // Call this callback function after we are done parsing
            // all attributes for relationships-- see below.
            const done = _.after(_.size(definition.attributes), () => {
              // Generate schema without virtual populate
              const schema = new instance.Schema(_.omitBy(definition.loadedModel, model => {
                return model.type === 'virtual';
              }));

              _.set(strapi.config.hook.settings.mongoose, 'collections.' + mongooseUtils.toCollectionName(definition.globalName) + '.schema', schema);

              loadedAttributes();
            });

            // Add every relationships to the loaded model for Bookshelf.
            // Basic attributes don't need this-- only relations.
            _.forEach(definition.attributes, (details, name) => {
              const verbose = _.get(utilsModels.getNature(details, name, undefined, model.toLowerCase()), 'verbose') || '';

              // Build associations key
              utilsModels.defineAssociations(model.toLowerCase(), definition, details, name);

              if (_.isEmpty(verbose)) {
                definition.loadedModel[name].type = utils(instance).convertType(details.type);
              }

              switch (verbose) {
                case 'hasOne': {
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  definition.loadedModel[name] = {
                    type: instance.Schema.Types.ObjectId,
                    ref
                  };
                  break;
                }
                case 'hasMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  if (FK) {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via,
                      justOne: false
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = [{
                      type: instance.Schema.Types.ObjectId,
                      ref
                    }];
                  }
                  break;
                }
                case 'belongsTo': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  if (FK && FK.nature !== 'oneToOne' && FK.nature !== 'manyToOne' && FK.nature !== 'oneWay' && FK.nature !== 'oneToMorph') {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via,
                      justOne: true
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = {
                      type: instance.Schema.Types.ObjectId,
                      ref
                    };
                  }

                  break;
                }
                case 'belongsToMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  // One-side of the relationship has to be a virtual field to be bidirectional.
                  if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = [{
                      type: instance.Schema.Types.ObjectId,
                      ref
                    }];
                  }
                  break;
                }
                case 'morphOne': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref,
                    via: `${FK.via}.ref`,
                    justOne: true
                  };

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;
                  break;
                }
                case 'morphMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref,
                    via: `${FK.via}.ref`
                  };

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;
                  break;
                }
                case 'belongsToMorph': {
                  definition.loadedModel[name] = {
                    kind: String,
                    [details.filter]: String,
                    ref: {
                      type: instance.Schema.Types.ObjectId,
                      refPath: `${name}.kind`
                    }
                  };
                  break;
                }
                case 'belongsToManyMorph': {
                  definition.loadedModel[name] = [{
                    kind: String,
                    [details.filter]: String,
                    ref: {
                      type: instance.Schema.Types.ObjectId,
                      refPath: `${name}.kind`
                    }
                  }];
                  break;
                }
                default:
                  break;
              }

              done();
            });
          });
示例#25
0
文件: test_queue.js 项目: curit/bull
 return new Promise(function (resolve) {
   var resolveAfterAllJobs = _.after(jobs.length, resolve);
   queue.on('completed', resolveAfterAllJobs);
 });
示例#26
0
        const mountModels = (models, target, plugin = false) => {
          if (!target) return;

          const loadedAttributes = _.after(_.size(models), () => {
            _.forEach(models, (definition, model) => {
              try {
                let collection = strapi.config.hook.settings.mongoose.collections[mongooseUtils.toCollectionName(definition.globalName)];

                // Set the default values to model settings.
                _.defaults(definition, {
                  primaryKey: '_id'
                });

                // Initialize lifecycle callbacks.
                const preLifecycle = {
                  validate: 'beforeCreate',
                  findOneAndUpdate: 'beforeUpdate',
                  findOneAndRemove: 'beforeDestroy',
                  remove: 'beforeDestroy',
                  update: 'beforeUpdate',
                  find: 'beforeFetchAll',
                  findOne: 'beforeFetch',
                  save: 'beforeSave'
                };

                /*
                  Override populate path for polymorphic association.
                  It allows us to make Upload.find().populate('related')
                  instead of Upload.find().populate('related.item')
                */

                const morphAssociations = definition.associations.filter(association => association.nature.toLowerCase().indexOf('morph') !== -1);

                if (morphAssociations.length > 0) {
                  morphAssociations.forEach(association => {
                    Object.keys(preLifecycle)
                      .filter(key => key.indexOf('find') !== -1)
                      .forEach(key => {
                        collection.schema.pre(key,  function (next) {
                          if (this._mongooseOptions.populate && this._mongooseOptions.populate[association.alias]) {
                            if (association.nature === 'oneToManyMorph' || association.nature === 'manyToManyMorph') {
                              this._mongooseOptions.populate[association.alias].match = {
                                [`${association.via}.${association.filter}`]: association.alias,
                                [`${association.via}.kind`]: definition.globalId
                              };

                              // Select last related to an entity.
                              this._mongooseOptions.populate[association.alias].options = {
                                sort: '-createdAt'
                              };
                            } else {
                              this._mongooseOptions.populate[association.alias].path = `${association.alias}.ref`;
                            }
                          }
                          next();
                        });
                      });
                  });
                }

                _.forEach(preLifecycle, (fn, key) => {
                  if (_.isFunction(target[model.toLowerCase()][fn])) {
                    collection.schema.pre(key, function (next) {
                      target[model.toLowerCase()][fn](this).then(next).catch(err => strapi.log.error(err));
                    });
                  }
                });

                const postLifecycle = {
                  validate: 'afterCreate',
                  findOneAndRemove: 'afterDestroy',
                  remove: 'afterDestroy',
                  update: 'afterUpdate',
                  find: 'afterFetchAll',
                  findOne: 'afterFetch',
                  save: 'afterSave'
                };

                // Mongoose doesn't allow post 'remove' event on model.
                // See https://github.com/Automattic/mongoose/issues/3054
                _.forEach(postLifecycle, (fn, key) => {
                  if (_.isFunction(target[model.toLowerCase()][fn])) {
                    collection.schema.post(key, function (doc, next) {
                      target[model.toLowerCase()][fn](this, doc).then(next).catch(err =>  {
                        strapi.log.error(err);
                        next(err);
                      });
                    });
                  }
                });

                // Add virtual key to provide populate and reverse populate
                _.forEach(_.pickBy(definition.loadedModel, model => {
                  return model.type === 'virtual';
                }), (value, key) => {
                  collection.schema.virtual(key.replace('_v', ''), {
                    ref: value.ref,
                    localField: '_id',
                    foreignField: value.via,
                    justOne: value.justOne || false
                  });
                });

                // Use provided timestamps if the elemnets in the array are string else use default.
                if (_.isArray(_.get(definition, 'options.timestamps'))) {
                  const timestamps = {
                    createdAt: _.isString(_.get(definition, 'options.timestamps[0]')) ? _.get(definition, 'options.timestamps[0]') : 'createdAt',
                    updatedAt: _.isString(_.get(definition, 'options.timestamps[1]')) ? _.get(definition, 'options.timestamps[1]') : 'updatedAt'
                  };
                  collection.schema.set('timestamps', timestamps);
                } else {
                  collection.schema.set('timestamps', _.get(definition, 'options.timestamps') === true);
                }
                collection.schema.set('minimize', _.get(definition, 'options.minimize', false) === true);

                collection.schema.options.toObject = collection.schema.options.toJSON = {
                  virtuals: true,
                  transform: function (doc, returned, opts) {
                    // Remover $numberDecimal nested property.
                    Object.keys(returned)
                      .filter(key => returned[key] instanceof mongoose.Types.Decimal128)
                      .forEach((key, index) => {
                        // Parse to float number.
                        returned[key] = parseFloat(returned[key].toString());
                      });

                    morphAssociations.forEach(association => {
                      if (Array.isArray(returned[association.alias]) && returned[association.alias].length > 0) {
                        // Reformat data by bypassing the many-to-many relationship.
                        switch (association.nature) {
                          case 'oneMorphToOne':
                            returned[association.alias] = returned[association.alias][0].ref;
                            break;
                          case 'manyMorphToMany':
                          case 'manyMorphToOne':

                            returned[association.alias] = returned[association.alias].map(obj => obj.ref);
                            break;
                          default:

                        }
                      }
                    });
                  }
                };

                // Instantiate model.
                const Model = instance.model(definition.globalId, collection.schema, definition.collectionName);

                if (!plugin) {
                  global[definition.globalName] = Model;
                }

                // Expose ORM functions through the `target` object.
                target[model] = _.assign(Model, target[model]);

                // Push attributes to be aware of model schema.
                target[model]._attributes = definition.attributes;
                target[model].updateRelations = relations.update;
              } catch (err) {
                strapi.log.error('Impossible to register the `' + model + '` model.');
                strapi.log.error(err);
                strapi.stop();
              }
            });
          });

          // Parse every authenticated model.
          _.forEach(models, (definition, model) => {
            definition.globalName = _.upperFirst(_.camelCase(definition.globalId));

            // Make sure the model has a connection.
            // If not, use the default connection.
            if (_.isEmpty(definition.connection)) {
              definition.connection = strapi.config.currentEnvironment.database.defaultConnection;
            }

            // Make sure this connection exists.
            if (!_.has(strapi.config.connections, definition.connection)) {
              strapi.log.error('The connection `' + definition.connection + '` specified in the `' + model + '` model does not exist.');
              strapi.stop();
            }

            // Add some informations about ORM & client connection
            definition.orm = 'mongoose';
            definition.client = _.get(strapi.config.connections[definition.connection], 'client');
            definition.associations = [];

            // Register the final model for Mongoose.
            definition.loadedModel = _.cloneDeep(definition.attributes);

            // Initialize the global variable with the
            // capitalized model name.
            if (!plugin) {
              global[definition.globalName] = {};
            }

            if (_.isEmpty(definition.attributes)) {
              // Generate empty schema
              _.set(strapi.config.hook.settings.mongoose, 'collections.' + mongooseUtils.toCollectionName(definition.globalName) + '.schema', new instance.Schema({}));

              return loadedAttributes();
            }

            // Call this callback function after we are done parsing
            // all attributes for relationships-- see below.
            const done = _.after(_.size(definition.attributes), () => {
              // Generate schema without virtual populate
              const schema = new instance.Schema(_.omitBy(definition.loadedModel, model => {
                return model.type === 'virtual';
              }));

              _.set(strapi.config.hook.settings.mongoose, 'collections.' + mongooseUtils.toCollectionName(definition.globalName) + '.schema', schema);

              loadedAttributes();
            });

            // Add every relationships to the loaded model for Bookshelf.
            // Basic attributes don't need this-- only relations.
            _.forEach(definition.attributes, (details, name) => {
              const verbose = _.get(utilsModels.getNature(details, name, undefined, model.toLowerCase()), 'verbose') || '';

              // Build associations key
              utilsModels.defineAssociations(model.toLowerCase(), definition, details, name);

              if (_.isEmpty(verbose)) {
                definition.loadedModel[name].type = utils(instance).convertType(details.type);
              }

              switch (verbose) {
                case 'hasOne': {
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  definition.loadedModel[name] = {
                    type: instance.Schema.Types.ObjectId,
                    ref
                  };
                  break;
                }
                case 'hasMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  if (FK) {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via,
                      justOne: false
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = [{
                      type: instance.Schema.Types.ObjectId,
                      ref
                    }];
                  }
                  break;
                }
                case 'belongsTo': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  if (FK && FK.nature !== 'oneToOne' && FK.nature !== 'manyToOne' && FK.nature !== 'oneWay' && FK.nature !== 'oneToMorph') {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via,
                      justOne: true
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = {
                      type: instance.Schema.Types.ObjectId,
                      ref
                    };
                  }

                  break;
                }
                case 'belongsToMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  // One-side of the relationship has to be a virtual field to be bidirectional.
                  if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) {
                    definition.loadedModel[name] = {
                      type: 'virtual',
                      ref,
                      via: FK.via
                    };

                    // Set this info to be able to see if this field is a real database's field.
                    details.isVirtual = true;
                  } else {
                    definition.loadedModel[name] = [{
                      type: instance.Schema.Types.ObjectId,
                      ref
                    }];
                  }
                  break;
                }
                case 'morphOne': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;

                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref,
                    via: `${FK.via}.ref`,
                    justOne: true
                  };

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;
                  break;
                }
                case 'morphMany': {
                  const FK = _.find(definition.associations, {alias: name});
                  const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;

                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref,
                    via: `${FK.via}.ref`
                  };

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;
                  break;
                }
                case 'belongsToMorph': {
                  definition.loadedModel[name] = {
                    kind: String,
                    [details.filter]: String,
                    ref: {
                      type: instance.Schema.Types.ObjectId,
                      refPath: `${name}.kind`
                    }
                  };
                  break;
                }
                case 'belongsToManyMorph': {
                  definition.loadedModel[name] = [{
                    kind: String,
                    [details.filter]: String,
                    ref: {
                      type: instance.Schema.Types.ObjectId,
                      refPath: `${name}.kind`
                    }
                  }];
                  break;
                }
                default:
                  break;
              }

              done();
            });
          });
        };
示例#27
0
    initialize: cb => {
      if (_.isEmpty(strapi.models) || !_.pickBy(strapi.config.connections, {
        connector: 'strapi-redis'
      })) {
        return cb();
      }

      const connections = _.pickBy(strapi.config.connections, {
        connector: 'strapi-redis'
      });

      if(_.size(connections) === 0) {
        cb();
      }

      const done = _.after(_.size(connections), () => {
        cb();
      });

      // For each connection in the config register a new Knex connection.
      _.forEach(connections, (connection, name) => {
        // Apply defaults
        _.defaults(connection.settings, strapi.config.hook.settings.redis);

        try {
          const redis = new Redis(_.defaultsDeep({
            port: _.get(connection.settings, 'port'),
            host: _.get(connection.settings, 'host'),
            options: {
              db: _.get(connection.options, 'database') || 0
            }
          }, strapi.config.hook.settings.redis));

          redis.on('error', err => {
            strapi.log.error(err);
            process.exit(0);
            return;
          });

          // Utils function.
          // Behavior: Try to retrieve data from Redis, if null
          // execute callback and set the value in Redis for this serial key.
          redis.cache = async ({ expired = 60 * 60, serial }, cb, type) => {
            if (_.isEmpty(serial)) {
              strapi.log.warn(
                `Be careful, you're using cache() function of strapi-redis without serial`
              );

              const traces = stackTrace.get();

              strapi.log.warn(
                `> [${traces[1].getLineNumber()}] ${traces[1]
                  .getFileName()
                  .replace(strapi.config.appPath, '')}`
              );

              return await cb();
            }

            let cache = await redis.get(serial);

            if (!cache) {
              cache = await cb();

              if (
                cache &&
                _.get(connection, 'options.disabledCaching') !== true
              ) {
                switch (type) {
                  case 'json':
                    redis.set(serial, JSON.stringify(cache), 'ex', expired);
                    break;
                  case 'int':
                  default:
                    redis.set(serial, cache, 'ex', expired);
                    break;
                }

              }
            }

            switch (type) {
              case 'int':
                return parseInt(cache);
              case 'float':
                return _.toNumber(cache);
              case 'json':
                try {
                  return _.isObject(cache) ? cache : JSON.parse(cache);
                } catch (e) {
                  return cache;
                }
              default:
                return cache;
            }
          };

          // Define as new connection.
          strapi.connections[name] = redis;

          // Expose global
          if (_.get(connection, 'options.global') !== false) {
            global[_.get(connection, 'options.globalName') || 'redis'] = redis;
          }

          if (_.get(connection, 'options.debug') === true) {
            redis.monitor((err, monitor) => {
              // Entering monitoring mode.
              monitor.on('monitor', (time, args) => {
                console.log(time + ': ' + util.inspect(args));
              });
            });
          }

          redis.on('ready', () => {
            done();
          });
        } catch (e) {
          cb(e);

          return false;
        }
      });
    }
示例#28
0
          _.forEach(models, (definition, model) => {
            definition.globalName = _.upperFirst(_.camelCase(definition.globalId));

            _.defaults(definition, {
              primaryKey: 'id'
            });

            // Define local GLOBALS to expose every models in this file.
            GLOBALS[definition.globalId] = {};

            // Add some informations about ORM & client connection & tableName
            definition.orm = 'bookshelf';
            definition.client = _.get(connection.settings, 'client');

            // Register the final model for Bookshelf.
            const loadedModel = _.assign({
                tableName: definition.collectionName,
                hasTimestamps: _.get(definition, 'options.timestamps') === true,
                idAttribute: _.get(definition, 'options.idAttribute', 'id'),
                associations: [],
                defaults: Object.keys(definition.attributes).reduce((acc, current) => {
                  if (definition.attributes[current].type && definition.attributes[current].default) {
                    acc[current] = definition.attributes[current].default;
                  }

                  return acc;
                }, {})
              }, definition.options);

            if (_.isString(_.get(connection, 'options.pivot_prefix'))) {
              loadedModel.toJSON = function(options = {}) {
                const { shallow = false, omitPivot = false } = options;
                const attributes = this.serialize(options);

                if (!shallow) {
                  const pivot = this.pivot && !omitPivot && this.pivot.attributes;

                  // Remove pivot attributes with prefix.
                  _.keys(pivot).forEach(key => delete attributes[`${PIVOT_PREFIX}${key}`]);

                  // Add pivot attributes without prefix.
                  const pivotAttributes = _.mapKeys(pivot, (value, key) => `${connection.options.pivot_prefix}${key}`);

                  return Object.assign({}, attributes, pivotAttributes);
                }

                return attributes;
              };
            }

            // Initialize the global variable with the
            // capitalized model name.
            if (!plugin) {
              global[definition.globalName] = {};
            }

            // Call this callback function after we are done parsing
            // all attributes for relationships-- see below.
            const done = _.after(_.size(definition.attributes), () => {
              try {
                // External function to map key that has been updated with `columnName`
                const mapper = (params = {}) =>
                  _.mapKeys(params, (value, key) => {
                    const attr = definition.attributes[key] || {};

                    return _.isPlainObject(attr) &&
                      _.isString(attr['columnName'])
                      ? attr['columnName']
                      : key;
                  });

                // Update serialize to reformat data for polymorphic associations.
                loadedModel.serialize = function(options) {
                  const attrs = _.clone(this.attributes);

                  if (options && options.shallow) {
                    return attrs;
                  }

                  const relations = this.relations;

                  // Extract association except polymorphic.
                  const associations = definition.associations
                    .filter(association => association.nature.toLowerCase().indexOf('morph') === -1);
                  // Extract polymorphic association.
                  const polymorphicAssociations = definition.associations
                    .filter(association => association.nature.toLowerCase().indexOf('morph') !== -1);

                  polymorphicAssociations.map(association => {
                    // Retrieve relation Bookshelf object.
                    const relation = relations[association.alias];

                    if (relation) {
                      // Extract raw JSON data.
                      attrs[association.alias] = relation.toJSON ? relation.toJSON(options) : relation;

                      // Retrieve opposite model.
                      const model = association.plugin ?
                        strapi.plugins[association.plugin].models[association.collection || association.model]:
                        strapi.models[association.collection || association.model];

                      // Reformat data by bypassing the many-to-many relationship.
                      switch (association.nature) {
                        case 'oneToManyMorph':
                          attrs[association.alias] = attrs[association.alias][model.collectionName];
                          break;
                        case 'manyToManyMorph':
                          attrs[association.alias] = attrs[association.alias].map(rel => rel[model.collectionName]);
                          break;
                        case 'oneMorphToOne':
                          attrs[association.alias] = attrs[association.alias].related;
                          break;
                        case 'manyMorphToOne':
                        case 'manyMorphToMany':
                          attrs[association.alias] = attrs[association.alias].map(obj => obj.related);
                          break;
                        default:
                      }
                    }
                  });

                  associations.map(association => {
                    const relation = relations[association.alias];

                    if (relation) {
                      // Extract raw JSON data.
                      attrs[association.alias] = relation.toJSON ? relation.toJSON(options) : relation;
                    }
                  });

                  return attrs;
                }

                // Initialize lifecycle callbacks.
                loadedModel.initialize = function() {
                  const lifecycle = {
                    creating: 'beforeCreate',
                    created: 'afterCreate',
                    destroying: 'beforeDestroy',
                    destroyed: 'afterDestroy',
                    updating: 'beforeUpdate',
                    updated: 'afterUpdate',
                    fetching: 'beforeFetch',
                    'fetching:collection': 'beforeFetchCollection',
                    fetched: 'afterFetch',
                    'fetched:collection': 'afterFetchCollection',
                    saving: 'beforeSave',
                    saved: 'afterSave'
                  };

                  _.forEach(lifecycle, (fn, key) => {
                    if (_.isFunction(target[model.toLowerCase()][fn])) {
                      this.on(key, target[model.toLowerCase()][fn]);
                    }
                  });

                  // Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
                  // Apply only during fetching.
                  this.on('fetching fetching:collection', (instance, attrs, options) => {
                    if (_.isArray(options.withRelated)) {
                      options.withRelated = options.withRelated.map(path => {
                        const association = definition.associations
                          .filter(association => association.nature.toLowerCase().indexOf('morph') !== -1)
                          .filter(association => association.alias === path || association.via === path)[0];

                        if (association) {
                          // Override on polymorphic path only.
                          if (_.isString(path) && path === association.via) {
                            return `related.${association.via}`;
                          } else if (_.isString(path) && path === association.alias) {
                            // MorphTo side.
                            if (association.related) {
                              return `${association.alias}.related`;
                            }

                            // oneToMorph or manyToMorph side.
                            // Retrieve collection name because we are using it to build our hidden model.
                            const model = association.plugin ?
                              strapi.plugins[association.plugin].models[association.collection || association.model]:
                              strapi.models[association.collection || association.model];

                            return {
                              [`${association.alias}.${model.collectionName}`]: function(query) {
                                query.orderBy('created_at', 'desc');
                              }
                            };
                          }
                        }

                        return path;
                      });
                    }

                    return _.isFunction(
                      target[model.toLowerCase()]['beforeFetchCollection']
                    )
                      ? target[model.toLowerCase()]['beforeFetchCollection']
                      : Promise.resolve();
                  });

                  this.on('saving', (instance, attrs, options) => {
                    instance.attributes = mapper(instance.attributes);
                    attrs = mapper(attrs);

                    return _.isFunction(
                      target[model.toLowerCase()]['beforeSave']
                    )
                      ? target[model.toLowerCase()]['beforeSave']
                      : Promise.resolve();
                  });
                };

                loadedModel.hidden = _.keys(
                  _.keyBy(
                    _.filter(definition.attributes, (value, key) => {
                      if (
                        value.hasOwnProperty('columnName') &&
                        !_.isEmpty(value.columnName) &&
                        value.columnName !== key
                      ) {
                        return true;
                      }
                    }),
                    'columnName'
                  )
                );

                GLOBALS[definition.globalId] = ORM.Model.extend(loadedModel);

                if (!plugin) {
                  // Only expose as real global variable the models which
                  // are not scoped in a plugin.
                  global[definition.globalId] = GLOBALS[definition.globalId];
                }

                // Expose ORM functions through the `strapi.models[xxx]`
                // or `strapi.plugins[xxx].models[yyy]` object.
                target[model] = _.assign(GLOBALS[definition.globalId], target[model]);

                // Push attributes to be aware of model schema.
                target[model]._attributes = definition.attributes;

                databaseUpdate.push(new Promise(async (resolve) => {
                  // Equilize database tables
                  const handler = async (table, attributes) => {
                    const tableExist = await ORM.knex.schema.hasTable(table);

                    const getType = (attribute, name) => {
                      let type;

                      if (!attribute.type) {
                        // Add integer value if there is a relation
                        const relation = definition.associations.find((association) => {
                          return association.alias === name;
                        });

                        switch (relation.nature) {
                          case 'oneToOne':
                          case 'manyToOne':
                            type = definition.client === 'pg' ? 'integer' : 'int';
                            break;
                          default:
                            return null;
                        }
                      } else {
                        switch (attribute.type) {
                          case 'text':
                          case 'json':
                            type = 'text';
                            break;
                          case 'string':
                          case 'enumeration':
                          case 'password':
                          case 'email':
                            type = 'varchar(255)';
                            break;
                          case 'integer':
                          case 'biginteger':
                            type = definition.client === 'pg' ? 'integer' : 'int';
                            break;
                          case 'float':
                            type = definition.client === 'pg' ? 'double precision' : 'double';
                            break;
                          case 'decimal':
                            type = 'decimal';
                            break;
                          case 'date':
                          case 'time':
                          case 'datetime':
                          case 'timestamp':
                            type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP';
                            break;
                          case 'timestampUpdate':
                            type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
                            break;
                          case 'boolean':
                            type = 'boolean';
                            break;
                          default:
                        }
                      }

                      return type;
                    };

                    // Apply field type of attributes definition
                    const generateColumns = (attrs, start) => {
                      return Object.keys(attrs).reduce((acc, attr) => {
                        const attribute = attributes[attr];

                        const type = getType(attribute, attr);

                        if (type) {
                          acc.push(`${quote}${attr}${quote} ${type}`);
                        }

                        return acc;
                      }, start);
                    };

                    if (!tableExist) {
                      const columns = generateColumns(attributes, [`id ${definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY`]).join(',\n\r');

                      // Create table
                      await ORM.knex.raw(`
                        CREATE TABLE ${quote}${table}${quote} (
                          ${columns}
                        )
                      `);
                    } else {
                      const columns = Object.keys(attributes);

                      // Fetch existing column
                      const columnsExist = await Promise.all(columns.map(attribute =>
                        ORM.knex.schema.hasColumn(table, attribute)
                      ));

                      const columnsToAdd = {};

                      // Get columns to add
                      columnsExist.forEach((columnExist, index) => {
                        const attribute = attributes[columns[index]];

                        if (!columnExist) {
                          columnsToAdd[columns[index]] = attribute;
                        }
                      });

                      // Generate and execute query to add missing column
                      if (Object.keys(columnsToAdd).length > 0) {
                        const columns = generateColumns(columnsToAdd, []);
                        const queries = columns.reduce((acc, attribute) => {
                          acc.push(`ALTER TABLE ${quote}${table}${quote} ADD ${attribute};`)
                          return acc;
                        }, []).join('\n\r');

                        await ORM.knex.raw(queries);
                      }

                      // Execute query to update column type
                      await Promise.all(columns.map(attribute =>
                        new Promise(async (resolve) => {
                          const type = getType(attributes[attribute], attribute);

                          if (type) {
                            const changeType = definition.client === 'pg'
                              ? `ALTER COLUMN ${quote}${attribute}${quote} TYPE ${type} USING ${quote}${attribute}${quote}::${type}`
                              : `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} `;

                            const changeRequired = definition.client === 'pg'
                              ? `ALTER COLUMN ${quote}${attribute}${quote} ${attributes[attribute].required ? 'SET' : 'DROP'} NOT NULL`
                              : `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} ${attributes[attribute].required ? 'NOT' : ''} NULL`;

                            await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeType}`);
                            await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeRequired}`);
                          }

                          resolve();
                        })
                      ));
                    }
                  };

                  const quote = definition.client === 'pg' ? '"' : '`';

                  // Add created_at and updated_at field if timestamp option is true
                  if (loadedModel.hasTimestamps) {
                    definition.attributes['created_at'] = {
                      type: 'timestamp'
                    };
                    definition.attributes['updated_at'] = {
                      type: 'timestampUpdate'
                    };
                  }

                  // Equilize tables
                  await handler(loadedModel.tableName, definition.attributes);

                  // Equilize polymorphic releations
                  const morphRelations = definition.associations.find((association) => {
                    return association.nature.toLowerCase().includes('morphto');
                  });

                  if (morphRelations) {
                    const attributes = {
                      [`${loadedModel.tableName}_id`]: {
                        type: 'integer'
                      },
                      [`${morphRelations.alias}_id`]: {
                        type: 'integer'
                      },
                      [`${morphRelations.alias}_type`]: {
                        type: 'text'
                      },
                      [definition.attributes[morphRelations.alias].filter]: {
                        type: 'text'
                      }
                    };

                    await handler(`${loadedModel.tableName}_morph`, attributes);
                  }

                  // Equilize many to many releations
                  const manyRelations = definition.associations.find((association) => {
                    return association.nature === 'manyToMany';
                  });

                  if (manyRelations && manyRelations.dominant) {
                    const collection = manyRelations.plugin ?
                      strapi.plugins[manyRelations.plugin].models[manyRelations.collection]:
                      strapi.models[manyRelations.collection];

                    const attributes = {
                      [`${pluralize.singular(manyRelations.collection)}_id`]: {
                        type: 'integer'
                      },
                      [`${pluralize.singular(definition.globalId.toLowerCase())}_id`]: {
                        type: 'integer'
                      }
                    };

                    const table = _.get(manyRelations, 'collectionName') ||
                      _.map(
                        _.sortBy(
                          [
                            collection.attributes[
                              manyRelations.via
                            ],
                            manyRelations
                          ],
                          'collection'
                        ),
                        table => {
                          return _.snakeCase(
                            pluralize.plural(table.collection) +
                              ' ' +
                              pluralize.plural(table.via)
                          );
                        }
                      ).join('__');

                    await handler(table, attributes);
                  }

                  // Remove from attributes (auto handled by bookshlef and not displayed on ctb)
                  if (loadedModel.hasTimestamps) {
                    delete definition.attributes['created_at'];
                    delete definition.attributes['updated_at'];
                  }

                  resolve();
                }));
              } catch (err) {
                strapi.log.error('Impossible to register the `' + model + '` model.');
                strapi.log.error(err);
                strapi.stop();
              }
            });

            if (_.isEmpty(definition.attributes)) {
              done();
            }

            // Add every relationships to the loaded model for Bookshelf.
            // Basic attributes don't need this-- only relations.
            _.forEach(definition.attributes, (details, name) => {
              const verbose = _.get(
                utilsModels.getNature(details, name, undefined, model.toLowerCase()),
                'verbose'
              ) || '';

              // Build associations key
              utilsModels.defineAssociations(
                model.toLowerCase(),
                definition,
                details,
                name
              );

              let globalId;
              const globalName = details.model || details.collection || '';

              // Exclude polymorphic association.
              if (globalName !== '*') {
                globalId = details.plugin ?
                  _.get(strapi.plugins,`${details.plugin}.models.${globalName.toLowerCase()}.globalId`):
                  _.get(strapi.models, `${globalName.toLowerCase()}.globalId`);
              }

              switch (verbose) {
                case 'hasOne': {
                  const FK = details.plugin ?
                    _.findKey(
                      strapi.plugins[details.plugin].models[details.model].attributes,
                      details => {
                        if (
                          details.hasOwnProperty('model') &&
                          details.model === model &&
                          details.hasOwnProperty('via') &&
                          details.via === name
                        ) {
                          return details;
                        }
                      }
                    ):
                    _.findKey(
                      strapi.models[details.model].attributes,
                      details => {
                        if (
                          details.hasOwnProperty('model') &&
                          details.model === model &&
                          details.hasOwnProperty('via') &&
                          details.via === name
                        ) {
                          return details;
                        }
                      }
                    );

                  const columnName = details.plugin ?
                    _.get(strapi.plugins, `${details.plugin}.models.${details.model}.attributes.${FK}.columnName`, FK):
                    _.get(strapi.models, `${details.model}.attributes.${FK}.columnName`, FK);

                  loadedModel[name] = function() {
                    return this.hasOne(
                      GLOBALS[globalId],
                      columnName
                    );
                  };
                  break;
                }
                case 'hasMany': {
                  const columnName = details.plugin ?
                    _.get(strapi.plugins, `${details.plugin}.models.${globalId.toLowerCase()}.attributes.${details.via}.columnName`, details.via):
                    _.get(strapi.models[globalId.toLowerCase()].attributes, `${details.via}.columnName`, details.via);

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;

                  loadedModel[name] = function() {
                    return this.hasMany(GLOBALS[globalId], columnName);
                  };
                  break;
                }
                case 'belongsTo': {
                  loadedModel[name] = function() {
                    return this.belongsTo(
                      GLOBALS[globalId],
                      _.get(details, 'columnName', name)
                    );
                  };
                  break;
                }
                case 'belongsToMany': {
                  const collection = details.plugin ?
                    strapi.plugins[details.plugin].models[details.collection]:
                    strapi.models[details.collection];

                  const collectionName = _.get(details, 'collectionName') ||
                    _.map(
                      _.sortBy(
                        [
                          collection.attributes[
                            details.via
                          ],
                          details
                        ],
                        'collection'
                      ),
                      table => {
                        return _.snakeCase(
                          pluralize.plural(table.collection) +
                            ' ' +
                            pluralize.plural(table.via)
                        );
                      }
                    ).join('__');

                  const relationship = _.clone(
                    collection.attributes[details.via]
                  );

                  // Force singular foreign key
                  relationship.attribute = pluralize.singular(
                    relationship.collection
                  );
                  details.attribute = pluralize.singular(details.collection);

                  // Define PK column
                  details.column = utils.getPK(model, strapi.models);
                  relationship.column = utils.getPK(
                    details.collection,
                    strapi.models
                  );

                  // Sometimes the many-to-many relationships
                  // is on the same keys on the same models (ex: `friends` key in model `User`)
                  if (
                    details.attribute + '_' + details.column ===
                    relationship.attribute + '_' + relationship.column
                  ) {
                    relationship.attribute = pluralize.singular(details.via);
                  }

                  // Set this info to be able to see if this field is a real database's field.
                  details.isVirtual = true;

                  loadedModel[name] = function() {
                    if (
                      _.isArray(_.get(details, 'withPivot')) &&
                      !_.isEmpty(details.withPivot)
                    ) {
                      return this.belongsToMany(
                        GLOBALS[globalId],
                        collectionName,
                        relationship.attribute + '_' + relationship.column,
                        details.attribute + '_' + details.column
                      ).withPivot(details.withPivot);
                    }

                    return this.belongsToMany(
                      GLOBALS[globalId],
                      collectionName,
                      relationship.attribute + '_' + relationship.column,
                      details.attribute + '_' + details.column
                    );
                  };
                  break;
                }
                case 'morphOne': {
                  const model = details.plugin ?
                    strapi.plugins[details.plugin].models[details.model]:
                    strapi.models[details.model];

                  const globalId = `${model.collectionName}_morph`;

                  loadedModel[name] =  function() {
                    return this
                      .morphOne(GLOBALS[globalId], details.via, `${definition.collectionName}`)
                      .query(qb => {
                        qb.where(_.get(model, `attributes.${details.via}.filter`, 'field'), name);
                      });
                  }
                  break;
                }
                case 'morphMany': {
                  const collection = details.plugin ?
                    strapi.plugins[details.plugin].models[details.collection]:
                    strapi.models[details.collection];

                  const globalId = `${collection.collectionName}_morph`;

                  loadedModel[name] =  function() {
                    return this
                      .morphMany(GLOBALS[globalId], details.via, `${definition.collectionName}`)
                      .query(qb => {
                        qb.where(_.get(collection, `attributes.${details.via}.filter`, 'field'), name);
                      });
                  }
                  break;
                }
                case 'belongsToMorph':
                case 'belongsToManyMorph': {
                  const association = definition.associations
                    .find(association => association.alias === name);

                  const morphValues = association.related.map(id => {
                    let models = Object.values(strapi.models).filter(model => model.globalId === id);

                    if (models.length === 0) {
                      models = Object.keys(strapi.plugins).reduce((acc, current) => {
                        const models = Object.values(strapi.plugins[current].models).filter(model => model.globalId === id);

                        if (acc.length === 0 && models.length > 0) {
                          acc = models;
                        }

                        return acc;
                      }, []);
                    }

                    if (models.length === 0) {
                      strapi.log.error('Impossible to register the `' + model + '` model.');
                      strapi.log.error('The collection name cannot be found for the morphTo method.');
                      strapi.stop();
                    }

                    return models[0].collectionName;
                  });

                  // Define new model.
                  const options = {
                    tableName: `${definition.collectionName}_morph`,
                    [definition.collectionName]: function () {
                      return this
                        .belongsTo(
                          GLOBALS[definition.globalId],
                          `${definition.collectionName}_id`
                        );
                    },
                    related: function () {
                      return this
                        .morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]]));
                    }
                  };

                  GLOBALS[options.tableName] = ORM.Model.extend(options);

                  // Set polymorphic table name to the main model.
                  target[model].morph = GLOBALS[options.tableName];

                  // Hack Bookshelf to create a many-to-many polymorphic association.
                  // Upload has many Upload_morph that morph to different model.
                  loadedModel[name] = function () {
                    if (verbose === 'belongsToMorph') {
                      return this.hasOne(
                        GLOBALS[options.tableName],
                        `${definition.collectionName}_id`
                      );
                    }

                    return this.hasMany(
                      GLOBALS[options.tableName],
                      `${definition.collectionName}_id`
                    );
                  };
                  break;
                }
                default: {
                  break;
                }
              }

              done();
            });
          });
示例#29
0
      db.on('open', () => {
        // Initialize collections
        _.set(strapi, 'mongoose.collections', {});

        const loadedAttributes = _.after(_.size(strapi.models), function () {
          _.forEach(strapi.models, function (definition, model) {
            try {
              let collection = strapi.mongoose.collections[mongooseUtils.toCollectionName(definition.globalName)];

              // Initialize lifecycle callbacks.
              const preLifecycle = {
                validate: 'beforeCreate',
                save: 'beforeCreate',
                remove: 'beforeDestroy',
                update: 'beforeUpdate',
                find: 'beforeFetch',
                save: 'beforeSave'
              };

              _.forEach(preLifecycle, function (fn, key) {
                if (_.isFunction(strapi.models[model.toLowerCase()][fn])) {
                  collection.schema.pre(key, strapi.models[model.toLowerCase()][fn]);
                }
              });

              const postLifecycle = {
                validate: 'afterCreate',
                save: 'afterCreate',
                remove: 'afterDestroy',
                update: 'afterUpdate',
                find: 'afterFetch',
                save: 'afterSave'
              };

              _.forEach(postLifecycle, function (fn, key) {
                if (_.isFunction(strapi.models[model.toLowerCase()][fn])) {
                  collection.schema.post(key, strapi.models[model.toLowerCase()][fn]);
                }
              });

              // Add virtual key to provide populate and reverse populate
              _.forEach(_.pickBy(definition.loadedModel, model => {
                return model.type === 'virtual'
              }), (value, key) => {
                collection.schema.virtual(key.replace('_v', ''), {
                  ref: value.ref,
                  localField: '_id',
                  foreignField: value.via,
                  justOne: value.justOne || false
                });
              });

              collection.schema.set('toObject', {
                virtuals: true
              });

              collection.schema.set('toJSON', {
                virtuals: true
              });

              global[definition.globalName] = mongoose.model(definition.globalName, collection.schema);

              // Push model to strapi global variables.
              collection = global[definition.globalName];

              // Push attributes to be aware of model schema.
              collection._attributes = definition.attributes;
            } catch (err) {
              strapi.log.error('Impossible to register the `' + model + '` model.');
              strapi.log.error(err);
              strapi.stop();
            }
          });

          cb();
        });

        // Parse every registered model.
        _.forEach(strapi.models, function (definition, model) {
          definition.globalName = _.upperFirst(_.camelCase(definition.globalId));

          // Make sure the model has a connection.
          // If not, use the default connection.
          if (_.isEmpty(definition.connection)) {
            definition.connection = strapi.config.defaultConnection;
          }

          // Make sure this connection exists.
          if (!_.has(strapi.config.connections, definition.connection)) {
            strapi.log.error('The connection `' + definition.connection + '` specified in the `' + model + '` model does not exist.');
            strapi.stop();
          }

          // Add some informations about ORM & client connection
          definition.orm = 'mongoose';
          definition.client = _.get(strapi.config.connections[definition.connection], 'client');

          // Register the final model for Bookshelf.
          definition.loadedModel = _.cloneDeep(definition.attributes);

          // Initialize the global variable with the
          // capitalized model name.
          global[definition.globalName] = {};

          if (_.isEmpty(definition.attributes)) {
            // Generate empty schema
            _.set(strapi.mongoose.collections, mongooseUtils.toCollectionName(definition.globalName) + '.schema', mongoose.Schema({}));

            return loadedAttributes();
          }

          // Call this callback function after we are done parsing
          // all attributes for relationships-- see below.
          const done = _.after(_.size(definition.attributes), function () {
            // Generate schema without virtual populate
            _.set(strapi.mongoose.collections, mongooseUtils.toCollectionName(definition.globalName) + '.schema', mongoose.Schema(_.omitBy(definition.loadedModel, model => {
              return model.type === 'virtual';
            })));

            loadedAttributes();
          });

          // Add every relationships to the loaded model for Bookshelf.
          // Basic attributes don't need this-- only relations.
          _.forEach(definition.attributes, function (details, name) {
            const verbose = _.get(utilsModels.getNature(details, name), 'verbose') || '';

            // Build associations key
            if (!_.isEmpty(verbose)) {
              utilsModels.defineAssociations(globalName, definition, details, name);
            } else {
              definition.loadedModel[name].type = utils(mongoose).convertType(details.type);
            }

            let FK;

            switch (verbose) {
              case 'hasOne':
                definition.loadedModel[name] = {
                  type: mongoose.Schema.Types.ObjectId,
                  ref: _.capitalize(details.model)
                };
                break;

              case 'hasMany':
                FK = _.find(definition.associations, { alias : name });

                if (FK) {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(details.collection),
                    via: FK.via
                  };
                } else {
                  definition.loadedModel[name] = [{
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.collection)
                  }];
                }
                break;

              case 'belongsTo':
                FK = _.find(definition.associations, { alias : name });

                if (FK && FK.nature === 'oneToOne') {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(details.model),
                    via: FK.via,
                    justOne: true
                  };
                } else {
                  definition.loadedModel[name] = {
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.model)
                  };
                }
                break;

              case 'belongsToMany':
                FK = _.find(definition.associations, { alias : name });

                if (FK && _.isUndefined(details.via)) {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(FK.collection),
                    via: utilsModels.getVia(name, details)
                  };
                } else {
                  definition.loadedModel[name] = [{
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.collection)
                  }];
                }
                break;

              default:
                break;
            }

            done();
          });
        });
      });
示例#30
0
        _.forEach(strapi.models, function (definition, model) {
          definition.globalName = _.upperFirst(_.camelCase(definition.globalId));

          // Make sure the model has a connection.
          // If not, use the default connection.
          if (_.isEmpty(definition.connection)) {
            definition.connection = strapi.config.defaultConnection;
          }

          // Make sure this connection exists.
          if (!_.has(strapi.config.connections, definition.connection)) {
            strapi.log.error('The connection `' + definition.connection + '` specified in the `' + model + '` model does not exist.');
            strapi.stop();
          }

          // Add some informations about ORM & client connection
          definition.orm = 'mongoose';
          definition.client = _.get(strapi.config.connections[definition.connection], 'client');

          // Register the final model for Bookshelf.
          definition.loadedModel = _.cloneDeep(definition.attributes);

          // Initialize the global variable with the
          // capitalized model name.
          global[definition.globalName] = {};

          if (_.isEmpty(definition.attributes)) {
            // Generate empty schema
            _.set(strapi.mongoose.collections, mongooseUtils.toCollectionName(definition.globalName) + '.schema', mongoose.Schema({}));

            return loadedAttributes();
          }

          // Call this callback function after we are done parsing
          // all attributes for relationships-- see below.
          const done = _.after(_.size(definition.attributes), function () {
            // Generate schema without virtual populate
            _.set(strapi.mongoose.collections, mongooseUtils.toCollectionName(definition.globalName) + '.schema', mongoose.Schema(_.omitBy(definition.loadedModel, model => {
              return model.type === 'virtual';
            })));

            loadedAttributes();
          });

          // Add every relationships to the loaded model for Bookshelf.
          // Basic attributes don't need this-- only relations.
          _.forEach(definition.attributes, function (details, name) {
            const verbose = _.get(utilsModels.getNature(details, name), 'verbose') || '';

            // Build associations key
            if (!_.isEmpty(verbose)) {
              utilsModels.defineAssociations(globalName, definition, details, name);
            } else {
              definition.loadedModel[name].type = utils(mongoose).convertType(details.type);
            }

            let FK;

            switch (verbose) {
              case 'hasOne':
                definition.loadedModel[name] = {
                  type: mongoose.Schema.Types.ObjectId,
                  ref: _.capitalize(details.model)
                };
                break;

              case 'hasMany':
                FK = _.find(definition.associations, { alias : name });

                if (FK) {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(details.collection),
                    via: FK.via
                  };
                } else {
                  definition.loadedModel[name] = [{
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.collection)
                  }];
                }
                break;

              case 'belongsTo':
                FK = _.find(definition.associations, { alias : name });

                if (FK && FK.nature === 'oneToOne') {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(details.model),
                    via: FK.via,
                    justOne: true
                  };
                } else {
                  definition.loadedModel[name] = {
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.model)
                  };
                }
                break;

              case 'belongsToMany':
                FK = _.find(definition.associations, { alias : name });

                if (FK && _.isUndefined(details.via)) {
                  definition.loadedModel[name] = {
                    type: 'virtual',
                    ref: _.capitalize(FK.collection),
                    via: utilsModels.getVia(name, details)
                  };
                } else {
                  definition.loadedModel[name] = [{
                    type: mongoose.Schema.Types.ObjectId,
                    ref: _.capitalize(details.collection)
                  }];
                }
                break;

              default:
                break;
            }

            done();
          });
        });