Skip to content

Commit

Permalink
Merge pull request #1354 from samselikoff/1091-self-referential-max-c…
Browse files Browse the repository at this point in the history
…all-stack

Throw if using association() helper on a self-referential belongsTo r…
  • Loading branch information
samselikoff committed Jul 28, 2018
2 parents 2b629dd + 4cbd5bf commit 2eabc2a
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 24 deletions.
1 change: 1 addition & 0 deletions addon/association.js
@@ -1,5 +1,6 @@
let association = function(...traitsAndOverrides) {
let __isAssociation__ = true;

return {
__isAssociation__,
traitsAndOverrides
Expand Down
6 changes: 5 additions & 1 deletion addon/orm/schema.js
Expand Up @@ -222,7 +222,11 @@ export default class Schema {
}

modelClassFor(modelName) {
return this._registry[camelize(modelName)].class.prototype;
let model = this._registry[camelize(modelName)];

assert(model, `Model not registered: ${modelName}`);

return model.class.prototype;
}

/*
Expand Down
36 changes: 14 additions & 22 deletions addon/server.js
Expand Up @@ -678,35 +678,27 @@ export default class Server {
*
* @private
*/
_mapAssociationsFromAttributes(modelType, attributes, overrides = {}) {
_mapAssociationsFromAttributes(modelName, attributes, overrides = {}) {
Object.keys(attributes || {}).filter((attr) => {
return isAssociation(attributes[attr]);
}).forEach((attr) => {
let association = attributes[attr];
let associationName = this._fetchAssociationNameFromModel(modelType, attr);
let modelClass = this.schema.modelClassFor(modelName);
let association = modelClass.associationFor(attr);

assert(association && association.isBelongsTo,
`You're using the \`association\` factory helper on the '${attr}' attribute of your ${modelName} factory, but that attribute is not a \`belongsTo\` association. Read the Factories docs for more information: http://www.ember-cli-mirage.com/docs/v0.3.x/factories/#factories-and-relationships`
);

let isSelfReferentialBelongsTo = association && association.isBelongsTo && association.modelName === modelName;

assert(!isSelfReferentialBelongsTo, `You're using the association() helper on your ${modelName} factory for ${attr}, which is a belongsTo self-referential relationship. You can't do this as it will lead to infinite recursion. You can move the helper inside of a trait and use it selectively.`);

let factoryAssociation = attributes[attr];
let foreignKey = `${camelize(attr)}Id`;
if (!overrides[attr]) {
attributes[foreignKey] = this.create(associationName, ...association.traitsAndOverrides).id;
attributes[foreignKey] = this.create(association.modelName, ...factoryAssociation.traitsAndOverrides).id;
}
delete attributes[attr];
});
}

/**
*
* @private
*/
_fetchAssociationNameFromModel(modelType, associationAttribute) {
let camelizedModelType = camelize(modelType);
let model = this.schema.modelFor(camelizedModelType);
if (!model) {
throw new Error(`Model not registered: ${modelType}`);
}

let association = model.class.findBelongsToAssociation(associationAttribute);
if (!association) {
throw new Error(`You're using the \`association\` factory helper on the '${associationAttribute}' attribute of your ${modelType} factory, but that attribute is not a \`belongsTo\` association. Read the Factories docs for more information: http://www.ember-cli-mirage.com/docs/v0.3.x/factories/#factories-and-relationships`);
}
return camelize(association.modelName);
}
}
21 changes: 21 additions & 0 deletions tests/integration/factories/helpers-test.js
Expand Up @@ -107,4 +107,25 @@ module('Integration | Server | Factories | helpers', function(hooks) {
postIds: ['1']
});
});

test('it throws if using the association helper on a self-referential belongsTo relationship', function(assert) {
this.server = new Server({
environment: 'test',
models: {
page: Model.extend({
parentPage: belongsTo('page', { inverse: 'childPages' }),
childPages: hasMany('page', { inverse: 'parentPage' })
})
},
factories: {
page: Factory.extend({
parentPage: association()
})
}
});

assert.throws(() => {
this.server.create('page');
}, /You're using the association\(\) helper on your page factory for parentPage, which is a belongsTo self-referential relationship. You can't do this as it will lead to infinite recursion. You can move the helper inside of a trait and use it selectively./);
});
});
2 changes: 1 addition & 1 deletion tests/unit/server-test.js
Expand Up @@ -1127,7 +1127,7 @@ module('Unit | Server #build', function(hooks) {

assert.throws(() => {
server.build('article', 'withCategory');
}, /You're using the `association` factory helper on the 'category' attribute/);
}, /You're using the `association` factory helper on the 'category' attribute of your article factory/);
});
});

Expand Down

0 comments on commit 2eabc2a

Please sign in to comment.