Skip to content

Commit

Permalink
Add more ORM assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
samselikoff committed Jul 28, 2018
1 parent 2eabc2a commit 68c503e
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 14 deletions.
68 changes: 54 additions & 14 deletions addon/orm/model.js
Expand Up @@ -397,20 +397,7 @@ class Model {
* @private
*/
_setupAttrs(attrs) {
// Verify no undefined associations are passed in
Object.keys(attrs)
.filter((key) => {
let value = attrs[key];
let isModelOrCollection = (value instanceof Model || value instanceof Collection || value instanceof PolymorphicCollection);
let isArrayOfModels = Array.isArray(value) && value.length && value.every(item => item instanceof Model);

return isModelOrCollection || isArrayOfModels;
})
.forEach((key) => {
let modelOrCollection = attrs[key];

assert(this.associationKeys.indexOf(key) > -1, `You're trying to create a ${this.modelName} model and you passed in a ${modelOrCollection.toString()} under the ${key} key, but you haven't defined that key as an association on your model.`);
});
this._validateAttrs(attrs);

// Filter out association keys
let hash = Object.keys(attrs).reduce((memo, key) => {
Expand Down Expand Up @@ -504,6 +491,59 @@ class Model {
}, this);
}

/**
* @method _validateAttrs
* @private
*/
_validateAttrs(attrs) {
// Verify attrs passed in for associations are actually associations
Object.keys(attrs)
.filter(key => this.associationKeys.includes(key))
.forEach(key => {
let value = attrs[key];
let association = this.associationFor(key);
let isNull = value === null;

if (association.isHasMany) {
let isCollection = value instanceof Collection || value instanceof PolymorphicCollection;
let isArrayOfModels = Array.isArray(value) && value.length && value.every(item => item instanceof Model);

assert(isCollection || isArrayOfModels || isNull, `You're trying to create a ${this.modelName} model and you passed in "${value}" under the ${key} key, but that key is a HasMany relationship. You must pass in a Collection, PolymorphicCollection, array of Models, or null.`);

} else if (association.isBelongsTo) {
assert(value instanceof Model || isNull, `You're trying to create a ${this.modelName} model and you passed in "${value}" under the ${key} key, but that key is a BelongsTo relationship. You must pass in a Model or null.`);
}
});

// Verify attrs passed in for association foreign keys are actually fks
Object.keys(attrs)
.filter(key => this.associationIdKeys.includes(key))
.forEach(key => {
let value = attrs[key];

if (key.match(/Ids$/)) {
let isArray = Array.isArray(value);
let isNull = value === null;
assert(isArray || isNull, `You're trying to create a ${this.modelName} model and you passed in "${value}" under the ${key} key, but that key is a foreign key for a HasMany relationship. You must pass in an array of ids or null.`);
}
});

// Verify no undefined associations are passed in
Object.keys(attrs)
.filter(key => {
let value = attrs[key];
let isModelOrCollection = (value instanceof Model || value instanceof Collection || value instanceof PolymorphicCollection);
let isArrayOfModels = Array.isArray(value) && value.length && value.every(item => item instanceof Model);

return isModelOrCollection || isArrayOfModels;
})
.forEach(key => {
let modelOrCollection = attrs[key];

assert(this.associationKeys.indexOf(key) > -1, `You're trying to create a ${this.modelName} model and you passed in a ${modelOrCollection.toString()} under the ${key} key, but you haven't defined that key as an association on your model.`);
});
}

/**
* Originally we validated this via association.setId method, but it triggered
* recursion. That method is designed for updating an existing model's ID so
Expand Down
69 changes: 69 additions & 0 deletions tests/integration/orm/assertions-test.js
@@ -0,0 +1,69 @@
import { module, test } from 'qunit';
import Server from 'ember-cli-mirage/server';
import { Model, hasMany, belongsTo } from 'ember-cli-mirage';

module('Integration | ORM | assertions', function(hooks) {

hooks.beforeEach(function() {
this.server = new Server({
models: {
user: Model.extend({
posts: hasMany()
}),
post: Model.extend({
author: belongsTo('user')
})
}
});
});

hooks.afterEach(function() {
this.server.shutdown();
});

test('it errors when passing in the wrong type for a HasMany association', function(assert) {
assert.throws(() => {
this.server.schema.users.create({
name: 'Sam',
posts: [ 1 ]
});
}, /You're trying to create a user model and you passed in "1" under the posts key, but that key is a HasMany relationship./);
});

test('it errors when passing in the wrong type for a HasMany association foreign key', function(assert) {
assert.throws(() => {
this.server.schema.users.create({
name: 'Sam',
postIds: 'foo'
});
}, /You're trying to create a user model and you passed in "foo" under the postIds key, but that key is a foreign key for a HasMany relationship./);
});

test('it errors when passing in a missing foreign key for a HasMany association foreign key', function(assert) {
assert.throws(() => {
this.server.schema.users.create({
name: 'Sam',
postIds: [ 2 ]
});
}, /You're instantiating a user that has a postIds of 2, but some of those records don't exist in the database/);
});

test('it errors when passing in the wrong type for a BelongsTo association', function(assert) {
assert.throws(() => {
this.server.schema.posts.create({
title: 'Post 1',
author: 'sam'
});
}, /You're trying to create a post model and you passed in "sam" under the author key, but that key is a BelongsTo relationship./);
});

test('it errors when passing in a missing foreign key for a BelongsTo association foreign key', function(assert) {
assert.throws(() => {
this.server.schema.posts.create({
title: 'Post 1',
authorId: 1
});
}, /You're instantiating a post that has a authorId of 1, but that record doesn't exist in the database/);
});

});

0 comments on commit 68c503e

Please sign in to comment.