Merge pull request #253 from lehni/feature/async-boot-scripts-master

Add support for ES6 style async boot scripts
This commit is contained in:
Miroslav Bajtoš 2017-12-11 10:35:22 +01:00 committed by GitHub
commit 473e685a69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 211 additions and 51 deletions

View File

@ -81,23 +81,24 @@ function runScripts(app, list, callback) {
async.eachSeries(functions, function(f, done) {
debug('Running script %s', f.path);
if (f.func.length >= 2) {
debug('Starting async function %s', f.path);
f.func(app, function(err) {
debug('Async function finished %s', f.path);
var cb = function(err) {
debug('Async function %s %s', err ? 'failed' : 'finished', f.path);
done(err);
});
} else {
debug('Starting sync function %s', f.path);
var error;
// Make sure done() isn't called twice, e.g. if a script returns a
// thenable object and also calls the passed callback.
cb = function() {};
};
try {
f.func(app);
var result = f.func(app, cb);
if (result && typeof result.then === 'function') {
result.then(function() { cb(); }, cb);
} else if (f.func.length < 2) {
debug('Sync function finished %s', f.path);
done();
}
} catch (err) {
debug('Sync function failed %s', f.path, err);
error = err;
}
done(error);
done(err);
}
}, callback);
}

View File

@ -306,9 +306,15 @@ describe('executor', function() {
'barLoaded',
'barSyncLoaded',
'fooLoaded',
'promiseLoaded',
'thenableLoaded',
'barStarted',
'barFinished',
'barSyncExecuted',
'promiseStarted',
'promiseFinished',
'thenableStarted',
'thenableFinished',
]);
});
});
@ -322,14 +328,87 @@ describe('executor', function() {
'barLoaded',
'barSyncLoaded',
'fooLoaded',
'promiseLoaded',
'thenableLoaded',
'barStarted',
'barFinished',
'barSyncExecuted',
'promiseStarted',
'promiseFinished',
'thenableStarted',
'thenableFinished',
]);
done();
});
});
});
});
describe('with boot script returning a rejected promise', function() {
before(function() {
// Tell simple-app/boot/reject.js to return a rejected promise
process.rejectPromise = true;
});
after(function() {
delete process.rejectPromise;
});
it('receives rejected promise as callback error',
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();
});
});
});
});
describe('with boot script throwing an error', function() {
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;
});
after(function() {
delete process.promiseAndCallback;
});
it('should only call the callback once', function(done) {
simpleAppInstructions(function(err, context) {
if (err) return done(err);
// Note: Mocha will fail this test if done() is called twice
boot.execute(app, context.instructions, done);
});
});
}
);
describe('for mixins', function() {
var options;
@ -376,7 +455,6 @@ describe('executor', function() {
});
});
});
});
describe('with PaaS and npm env variables', function() {
beforeEach(function cleanEnvironment() {

View File

@ -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();
}
};

View File

@ -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();
});
},
});
};

14
test/fixtures/simple-app/boot/reject.js vendored Normal file
View File

@ -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'));
}
};

View File

@ -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();
});
},
};
};

12
test/fixtures/simple-app/boot/throw.js vendored Normal file
View File

@ -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');
}
};