first commit for general discussion
This commit is contained in:
parent
011dc1f6b8
commit
844147517e
|
@ -19,6 +19,7 @@ var classify = require('underscore.string/classify');
|
||||||
var camelize = require('underscore.string/camelize');
|
var camelize = require('underscore.string/camelize');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `App` object represents a Loopback application.
|
* The `App` object represents a Loopback application.
|
||||||
|
@ -404,30 +405,133 @@ app.enableAuth = function(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
app._verifyAuthModelRelations = function() {
|
app._verifyAuthModelRelations = function() {
|
||||||
|
let self = this;
|
||||||
|
|
||||||
// Allow unit-tests (but also LoopBack users) to disable the warnings
|
// Allow unit-tests (but also LoopBack users) to disable the warnings
|
||||||
if (this.get('_verifyAuthModelRelations') === false) return;
|
const warnOnBadSetup = self.get('_disableAuthModelRelationsWarnings') !== true;
|
||||||
|
// Prevent the app from being killed although the configuration is inconsistent
|
||||||
|
const dismissRejection = self.get('_dismissBadUserConfigRejection') !== true;
|
||||||
|
|
||||||
const AccessToken = this.registry.findModel('AccessToken');
|
const AccessToken = self.registry.findModel('AccessToken');
|
||||||
const User = this.registry.findModel('User');
|
const User = self.registry.findModel('User');
|
||||||
this.models().forEach(Model => {
|
const models = self.models();
|
||||||
if (Model === AccessToken || Model.prototype instanceof AccessToken) {
|
|
||||||
scheduleVerification(Model, verifyAccessTokenRelations);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Model === User || Model.prototype instanceof User) {
|
verifyUserModelsSetup().then(()=> {
|
||||||
scheduleVerification(Model, verifyUserRelations);
|
verifyRelationsSetup();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function verifyUserModelsSetup() {
|
||||||
|
let userModels = models.filter(model => {
|
||||||
|
return (model === User || model.prototype instanceof User) ||
|
||||||
|
(model === AccessToken || model.prototype instanceof AccessToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we use Promise.reflect() here to let all the tests go without throwing,
|
||||||
|
// this way we can get all the logs for all models we are checking
|
||||||
|
return Promise.map(userModels, (model) => {
|
||||||
|
return scheduleVerification(model, hasMultipleUserModelsConfig).reflect();
|
||||||
|
})
|
||||||
|
.then((inspections) => {
|
||||||
|
// proceed to next checkups if no error
|
||||||
|
let hasErrors = inspections.filter(inspection => {
|
||||||
|
return inspection.isRejected();
|
||||||
|
});
|
||||||
|
if (!hasErrors.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else log and eventually kill the app
|
||||||
|
console.warn('Application setup is inconsistent: some models are set to use ' +
|
||||||
|
'the Multiple user models configuration while some other models are set to ' +
|
||||||
|
'use the Single user model configuration. The model config is listed below.');
|
||||||
|
|
||||||
|
// detailed logs
|
||||||
|
inspections.forEach(inspection => {
|
||||||
|
let modelSetup = (inspection.value() || inspection.reason()).modelSetup;
|
||||||
|
console.warn(
|
||||||
|
'Model %j of type %j is set to use the %j user model config',
|
||||||
|
modelSetup.model,
|
||||||
|
modelSetup.instanceOf,
|
||||||
|
modelSetup.hasMultipleUserModelsConfig ? 'Multiple' : 'Single'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete self.hasMultipleUserModelsConfig;
|
||||||
|
|
||||||
|
if (!dismissRejection) {
|
||||||
|
throw new Error('Application setup is inconsistent, please see the log for more ' +
|
||||||
|
'information');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyRelationsSetup() {
|
||||||
|
models.forEach(Model => {
|
||||||
|
if (Model === AccessToken || Model.prototype instanceof AccessToken) {
|
||||||
|
scheduleVerification(Model, verifyAccessTokenRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Model === User || Model.prototype instanceof User) {
|
||||||
|
scheduleVerification(Model, verifyUserRelations);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// instant or scheduled Model verifications, as a promise
|
||||||
function scheduleVerification(Model, verifyFn) {
|
function scheduleVerification(Model, verifyFn) {
|
||||||
if (Model.dataSource) {
|
return new Promise((resolve, reject) => {
|
||||||
verifyFn(Model);
|
if (Model.dataSource) {
|
||||||
} else {
|
try {
|
||||||
Model.on('attached', () => verifyFn(Model));
|
resolve(verifyFn(Model));
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Model.on('attached', () => {
|
||||||
|
scheduleVerification(Model, verifyFn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMultipleUserModelsConfig(model) {
|
||||||
|
let hasMultipleUserModelsConfig, isInstanceOfUser;
|
||||||
|
|
||||||
|
// check is the model is set to use mutiple users config (with polymorphic relations)
|
||||||
|
if (model === User || model.prototype instanceof User) {
|
||||||
|
isInstanceOfUser = true;
|
||||||
|
const hasManyTokens = model.relations && model.relations.accessTokens;
|
||||||
|
hasMultipleUserModelsConfig = !!hasManyTokens.polymorphic;
|
||||||
|
} else if (model === AccessToken || model.prototype instanceof AccessToken) {
|
||||||
|
const belongsToUser = model.relations && model.relations.user;
|
||||||
|
// the test on belongsToUser is required as we allow AccessToken model not
|
||||||
|
// to define the relation with custom user model for backward compatibility
|
||||||
|
// see https://github.com/strongloop/loopback/pull/3227
|
||||||
|
hasMultipleUserModelsConfig = !!(belongsToUser && belongsToUser.polymorphic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let modelSetup = {
|
||||||
|
model: model.modelName,
|
||||||
|
instanceOf: isInstanceOfUser ? 'User' : 'AccesToken',
|
||||||
|
hasMultipleUserModelsConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if model config is consistent with already parsed models' config
|
||||||
|
if (self.hasMultipleUserModelsConfig === undefined) {
|
||||||
|
self.hasMultipleUserModelsConfig = hasMultipleUserModelsConfig;
|
||||||
|
} else if (self.hasMultipleUserModelsConfig !== hasMultipleUserModelsConfig) {
|
||||||
|
let error = new Error('User models setup is inconsistent');
|
||||||
|
error.modelSetup = modelSetup;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no error, return the modelSetup for further logging purposes
|
||||||
|
return {modelSetup};
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyAccessTokenRelations(Model) {
|
function verifyAccessTokenRelations(Model) {
|
||||||
|
if (!warnOnBadSetup) return;
|
||||||
|
|
||||||
const belongsToUser = Model.relations && Model.relations.user;
|
const belongsToUser = Model.relations && Model.relations.user;
|
||||||
if (belongsToUser) return;
|
if (belongsToUser) return;
|
||||||
|
|
||||||
|
@ -454,6 +558,8 @@ app._verifyAuthModelRelations = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyUserRelations(Model) {
|
function verifyUserRelations(Model) {
|
||||||
|
if (!warnOnBadSetup) return;
|
||||||
|
|
||||||
const hasManyTokens = Model.relations && Model.relations.accessTokens;
|
const hasManyTokens = Model.relations && Model.relations.accessTokens;
|
||||||
if (hasManyTokens) return;
|
if (hasManyTokens) return;
|
||||||
|
|
||||||
|
|
|
@ -2442,7 +2442,7 @@ describe('User', function() {
|
||||||
it('handles subclassed user with no accessToken relation', () => {
|
it('handles subclassed user with no accessToken relation', () => {
|
||||||
// setup a new LoopBack app, we don't want to use shared models
|
// setup a new LoopBack app, we don't want to use shared models
|
||||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||||
app.set('_verifyAuthModelRelations', false);
|
app.set('_disableAuthModelRelationsWarnings', true);
|
||||||
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
||||||
app.dataSource('db', {connector: 'memory'});
|
app.dataSource('db', {connector: 'memory'});
|
||||||
const User = app.registry.createModel({
|
const User = app.registry.createModel({
|
||||||
|
|
Loading…
Reference in New Issue