Merge pull request #253 from lehni/feature/async-boot-scripts-master
Add support for ES6 style async boot scripts
This commit is contained in:
commit
473e685a69
|
@ -81,23 +81,24 @@ function runScripts(app, list, callback) {
|
||||||
|
|
||||||
async.eachSeries(functions, function(f, done) {
|
async.eachSeries(functions, function(f, done) {
|
||||||
debug('Running script %s', f.path);
|
debug('Running script %s', f.path);
|
||||||
if (f.func.length >= 2) {
|
var cb = function(err) {
|
||||||
debug('Starting async function %s', f.path);
|
debug('Async function %s %s', err ? 'failed' : 'finished', f.path);
|
||||||
f.func(app, function(err) {
|
done(err);
|
||||||
debug('Async function finished %s', f.path);
|
// Make sure done() isn't called twice, e.g. if a script returns a
|
||||||
done(err);
|
// thenable object and also calls the passed callback.
|
||||||
});
|
cb = function() {};
|
||||||
} else {
|
};
|
||||||
debug('Starting sync function %s', f.path);
|
try {
|
||||||
var error;
|
var result = f.func(app, cb);
|
||||||
try {
|
if (result && typeof result.then === 'function') {
|
||||||
f.func(app);
|
result.then(function() { cb(); }, cb);
|
||||||
|
} else if (f.func.length < 2) {
|
||||||
debug('Sync function finished %s', f.path);
|
debug('Sync function finished %s', f.path);
|
||||||
} catch (err) {
|
done();
|
||||||
debug('Sync function failed %s', f.path, err);
|
|
||||||
error = err;
|
|
||||||
}
|
}
|
||||||
done(error);
|
} catch (err) {
|
||||||
|
debug('Sync function failed %s', f.path, err);
|
||||||
|
done(err);
|
||||||
}
|
}
|
||||||
}, callback);
|
}, callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,9 +306,15 @@ describe('executor', function() {
|
||||||
'barLoaded',
|
'barLoaded',
|
||||||
'barSyncLoaded',
|
'barSyncLoaded',
|
||||||
'fooLoaded',
|
'fooLoaded',
|
||||||
|
'promiseLoaded',
|
||||||
|
'thenableLoaded',
|
||||||
'barStarted',
|
'barStarted',
|
||||||
'barFinished',
|
'barFinished',
|
||||||
'barSyncExecuted',
|
'barSyncExecuted',
|
||||||
|
'promiseStarted',
|
||||||
|
'promiseFinished',
|
||||||
|
'thenableStarted',
|
||||||
|
'thenableFinished',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -322,60 +328,132 @@ describe('executor', function() {
|
||||||
'barLoaded',
|
'barLoaded',
|
||||||
'barSyncLoaded',
|
'barSyncLoaded',
|
||||||
'fooLoaded',
|
'fooLoaded',
|
||||||
|
'promiseLoaded',
|
||||||
|
'thenableLoaded',
|
||||||
'barStarted',
|
'barStarted',
|
||||||
'barFinished',
|
'barFinished',
|
||||||
'barSyncExecuted',
|
'barSyncExecuted',
|
||||||
|
'promiseStarted',
|
||||||
|
'promiseFinished',
|
||||||
|
'thenableStarted',
|
||||||
|
'thenableFinished',
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('for mixins', function() {
|
describe('with boot script returning a rejected promise', function() {
|
||||||
var options;
|
before(function() {
|
||||||
beforeEach(function() {
|
// Tell simple-app/boot/reject.js to return a rejected promise
|
||||||
appdir.writeFileSync('custom-mixins/example.js',
|
process.rejectPromise = true;
|
||||||
'module.exports = ' +
|
});
|
||||||
'function(Model, options) {}');
|
|
||||||
|
|
||||||
appdir.writeFileSync('custom-mixins/time-stamps.js',
|
after(function() {
|
||||||
'module.exports = ' +
|
delete process.rejectPromise;
|
||||||
'function(Model, options) {}');
|
});
|
||||||
|
|
||||||
appdir.writeConfigFileSync('custom-mixins/time-stamps.json', {
|
it('receives rejected promise as callback error',
|
||||||
name: 'Timestamping',
|
function(done) {
|
||||||
|
simpleAppInstructions(function(err, context) {
|
||||||
|
if (err) return done(err);
|
||||||
|
boot.execute(app, context.instructions, function(err) {
|
||||||
|
expect(err).to.exist.and.be.an.instanceOf(Error)
|
||||||
|
.with.property('message', 'reject');
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
options = {
|
describe('with boot script throwing an error', function() {
|
||||||
appRootDir: appdir.PATH,
|
before(function() {
|
||||||
};
|
// Tell simple-app/boot/throw.js to throw an error
|
||||||
|
process.throwError = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
delete process.throwError;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('receives thrown error as callback errors',
|
||||||
|
function(done) {
|
||||||
|
simpleAppInstructions(function(err, context) {
|
||||||
|
if (err) return done(err);
|
||||||
|
boot.execute(app, context.instructions, function(err) {
|
||||||
|
expect(err).to.exist.and.be.an.instanceOf(Error)
|
||||||
|
.with.property('message', 'throw');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with boot script returning a promise and calling callback',
|
||||||
|
function() {
|
||||||
|
before(function() {
|
||||||
|
process.promiseAndCallback = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('defines mixins from instructions - using `mixinDirs`',
|
after(function() {
|
||||||
function(done) {
|
delete process.promiseAndCallback;
|
||||||
options.mixinDirs = ['./custom-mixins'];
|
});
|
||||||
boot(app, options, function(err) {
|
|
||||||
if (err) return done(err);
|
|
||||||
var modelBuilder = app.registry.modelBuilder;
|
|
||||||
var registry = modelBuilder.mixins.mixins;
|
|
||||||
expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('defines mixins from instructions - using `mixinSources`',
|
it('should only call the callback once', function(done) {
|
||||||
function(done) {
|
simpleAppInstructions(function(err, context) {
|
||||||
options.mixinSources = ['./custom-mixins'];
|
if (err) return done(err);
|
||||||
boot(app, options, function(err) {
|
// Note: Mocha will fail this test if done() is called twice
|
||||||
if (err) return done(err);
|
boot.execute(app, context.instructions, done);
|
||||||
|
|
||||||
var modelBuilder = app.registry.modelBuilder;
|
|
||||||
var registry = modelBuilder.mixins.mixins;
|
|
||||||
expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('for mixins', function() {
|
||||||
|
var options;
|
||||||
|
beforeEach(function() {
|
||||||
|
appdir.writeFileSync('custom-mixins/example.js',
|
||||||
|
'module.exports = ' +
|
||||||
|
'function(Model, options) {}');
|
||||||
|
|
||||||
|
appdir.writeFileSync('custom-mixins/time-stamps.js',
|
||||||
|
'module.exports = ' +
|
||||||
|
'function(Model, options) {}');
|
||||||
|
|
||||||
|
appdir.writeConfigFileSync('custom-mixins/time-stamps.json', {
|
||||||
|
name: 'Timestamping',
|
||||||
|
});
|
||||||
|
|
||||||
|
options = {
|
||||||
|
appRootDir: appdir.PATH,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('defines mixins from instructions - using `mixinDirs`',
|
||||||
|
function(done) {
|
||||||
|
options.mixinDirs = ['./custom-mixins'];
|
||||||
|
boot(app, options, function(err) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var modelBuilder = app.registry.modelBuilder;
|
||||||
|
var registry = modelBuilder.mixins.mixins;
|
||||||
|
expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defines mixins from instructions - using `mixinSources`',
|
||||||
|
function(done) {
|
||||||
|
options.mixinSources = ['./custom-mixins'];
|
||||||
|
boot(app, options, function(err) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
var modelBuilder = app.registry.modelBuilder;
|
||||||
|
var registry = modelBuilder.mixins.mixins;
|
||||||
|
expect(Object.keys(registry)).to.eql(['Example', 'Timestamping']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with PaaS and npm env variables', function() {
|
describe('with PaaS and npm env variables', function() {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright IBM Corp. 2017. All Rights Reserved.
|
||||||
|
// Node module: loopback-boot
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
|
module.exports = function(app, callback) {
|
||||||
|
callback();
|
||||||
|
if (process.promiseAndCallback) {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright IBM Corp. 2017. All Rights Reserved.
|
||||||
|
// Node module: loopback-boot
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
|
process.bootFlags.push('promiseLoaded');
|
||||||
|
module.exports = function(app) {
|
||||||
|
process.bootFlags.push('promiseStarted');
|
||||||
|
return Promise.resolve({
|
||||||
|
then: function(onFulfill, onReject) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
process.bootFlags.push('promiseFinished');
|
||||||
|
onFulfill();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright IBM Corp. 2017. All Rights Reserved.
|
||||||
|
// Node module: loopback-boot
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
|
module.exports = function(app) {
|
||||||
|
if (process.rejectPromise) {
|
||||||
|
return Promise.reject(new Error('reject'));
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright IBM Corp. 2017. All Rights Reserved.
|
||||||
|
// Node module: loopback-boot
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
process.bootFlags.push('thenableLoaded');
|
||||||
|
module.exports = function(app) {
|
||||||
|
process.bootFlags.push('thenableStarted');
|
||||||
|
return {
|
||||||
|
then: function(onFulfill, onReject) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
process.bootFlags.push('thenableFinished');
|
||||||
|
onFulfill();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright IBM Corp. 2017. All Rights Reserved.
|
||||||
|
// Node module: loopback-boot
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(app) {
|
||||||
|
if (process.throwError) {
|
||||||
|
throw new Error('throw');
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue