Skip to content

Commit

Permalink
relationships feature is complete with tests, small adjustments to er…
Browse files Browse the repository at this point in the history
…ror messages and randomly failing tests
  • Loading branch information
izelnakri committed Oct 28, 2017
1 parent fa4ac1e commit a18ecad
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 93 deletions.
4 changes: 2 additions & 2 deletions lib/mem-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ function resetDatabase(models) {
const primaryKey = getModelPrimaryKey(model, existingPrimaryKey, modelName);

if (!primaryKey) {
throw new Error(chalk.red(`MemServer DATABASE ERROR: At least one of your ${modelName} fixtures missing a primary key. Please make sure all your ${modelName} fixtures have either id or uuid primaryKey`));
throw new Error(chalk.red(`[MemServer] DATABASE ERROR: At least one of your ${modelName} fixtures missing a primary key. Please make sure all your ${modelName} fixtures have either id or uuid primaryKey`));
} else if (primaryKeys.includes(model[primaryKey])) {
throw new Error(chalk.red(`MemServer DATABASE ERROR: Duplication in ${modelName} fixtures with ${primaryKey}: ${model[primaryKey]}`));
throw new Error(chalk.red(`[MemServer] DATABASE ERROR: Duplication in ${modelName} fixtures with ${primaryKey}: ${model[primaryKey]}`));
}

const existingAttributes = targetNamespace.MemServer.Models[modelName].attributes;
Expand Down
51 changes: 42 additions & 9 deletions lib/mem-server/model.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import util from 'util';
import chalk from 'chalk';
import { primaryKeyTypeSafetyCheck, generateUUID } from './utils';
import { classify, underscore } from 'ember-cli-string-utils';

const { pluralize, singularize } = require('i')(); // NOTE: move to ES6 imports
const targetNamespace = typeof global === 'object' ? global : window;

export default function(options) {
Expand All @@ -17,7 +19,7 @@ export default function(options) {
},
find(param) {
if (!param) {
throw new Error(chalk.red(`MemServer ${this.modelName}.find(id) cannot be called without a valid id`));
throw new Error(chalk.red(`[MemServer] ${this.modelName}.find(id) cannot be called without a valid id`));
} else if (Array.isArray(param)) {
const models = targetNamespace.MemServer.DB[this.modelName] || [];

Expand All @@ -27,7 +29,7 @@ export default function(options) {
return foundModel ? result.concat([foundModel]) : result;
}, []);
} else if (typeof param !== 'number') {
throw new Error(chalk.red(`MemServer ${this.modelName}.find(id) cannot be called without a valid id`));
throw new Error(chalk.red(`[MemServer] ${this.modelName}.find(id) cannot be called without a valid id`));
}

const models = targetNamespace.MemServer.DB[this.modelName] || [];
Expand All @@ -36,7 +38,7 @@ export default function(options) {
},
findBy(options) {
if (!options) {
throw new Error(chalk.red(`MemServer ${this.modelName}.findBy(id) cannot be called without a parameter`));
throw new Error(chalk.red(`[MemServer] ${this.modelName}.findBy(id) cannot be called without a parameter`));
}

const keys = Object.keys(options);
Expand Down Expand Up @@ -128,8 +130,16 @@ export default function(options) {

return targetRecord;
},
embed(relationship) { // { comments: Comment }
// NOTE: console.error(`${relationshipName} couldnt found for ${model}, put an another model lookup as 4th argument to embed() perhaps?`);
embed(relationship) { // EXAMPLE: { comments: Comment }
if (typeof relationship !== 'object' || relationship.modelName) {
throw new Error(chalk.red(`[MemServer] ${this.modelName}.embed(relationshipObject) requires an object as a parameter: { relationshipKey: $RelationshipModel }`));
}

const key = Object.keys(relationship)[0];

if (!relationship[key]) {
throw new Error(chalk.red(`[MemServer] ${this.modelName}.embed() fails: ${key} Model reference is not a valid. Please put a valid $ModelName to ${this.modelName}.embed()`));
}

return Object.assign(this.embedReferences, relationship);
},
Expand All @@ -143,18 +153,41 @@ export default function(options) {
},
serialize(object) { // NOTE: add links object ?
return Object.keys(this.embedReferences).reduce((result, embedObject) => {
const embedKey = Object.keys(embedObject)[0];
const embedKey = Object.keys(embedObject)[0]; // NOTE: console.error(`${relationshipName} couldnt found for ${model}, put an another model lookup as 4th argument to embed() perhaps?`);
const embedModel = embedObject[embedKey];
const embeddedRecords = this.getRelationship(object, embedKey, embedModel);

return Object.assign(result, embedNamespace.serializer(embeddedRecords));
}, object);
},
getRelationship(parentObject, relationshipName, relationshipModel) {
// NOTE: checking from the relationship name if its plural
// NOTE: could be on both sides this or relationshipModel.modelName
if (Array.isArray(parentObject)) {
throw new Error(chalk.red(`[MemServer] ${this.modelName}.getRelationship expects model input to be an object not an array`));
}

const targetRelationshipModel = relationshipModel ||
targetNamespace.MemServer.Models[classify(singularize(relationshipName))];
const hasManyRelationship = pluralize(relationshipName) === relationshipName;

if (!targetRelationshipModel) { // NOTE: test this
throw new Error(chalk.red(`[MemServer] ${relationshipName} relationship could not be found on ${this.modelName} model. Please put the ${relationshipName} Model object as the third parameter to ${this.modelName}.getRelationship function`));
} else if (hasManyRelationship) {
const hasManyRecords = targetRelationshipModel.findAll({
[`${underscore(this.modelName)}_id`]: parentObject.id
});

return hasManyRecords.length > 0 ? hasManyRecords : [];
}

const objectsReference = parentObject[`${underscore(targetRelationshipModel.modelName)}_id`];

if (objectsReference) {
return targetRelationshipModel.find(objectsReference);
}

// NOTE: console.error(`${relationshipName} couldnt found for ${model}, put an another model lookup as 4th argument to embed() perhaps?`);
return targetRelationshipModel.findBy({ // NOTE: id or uuid lookup?
[`${underscore(this.modelName)}_id`]: parentObject.id
});
}
}, options);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/mem-server/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import chalk from 'chalk';
export function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);

return v.toString(16);
});
}
Expand All @@ -12,9 +12,9 @@ export function primaryKeyTypeSafetyCheck(targetPrimaryKeyType, primaryKey, mode
const primaryKeyType = typeof primaryKey;

if (targetPrimaryKeyType === 'id' && (primaryKeyType !== 'number')) {
throw new Error(chalk.red(`MemServer ${modelName} model primaryKey type is 'id'. Instead you've tried to enter id: ${primaryKey} with ${primaryKeyType} type`));
throw new Error(chalk.red(`[MemServer] ${modelName} model primaryKey type is 'id'. Instead you've tried to enter id: ${primaryKey} with ${primaryKeyType} type`));
} else if (targetPrimaryKeyType === 'uuid' && (primaryKeyType !== 'string')) {
throw new Error(chalk.red(`MemServer ${modelName} model primaryKey type is 'uuid'. Instead you've tried to enter uuid: ${primaryKey} with ${primaryKeyType} type`));
throw new Error(chalk.red(`[MemServer] ${modelName} model primaryKey type is 'uuid'. Instead you've tried to enter uuid: ${primaryKey} with ${primaryKeyType} type`));
}

return targetPrimaryKeyType;
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
"babel-preset-env": "^1.6.1",
"babel-register": "^6.26.0",
"chalk": "^2.2.0",
"core-object": "^3.1.5",
"ember-cli-string-utils": "^1.1.0",
"ember-inflector": "^2.0.1",
"fake-xml-http-request": "^1.6.0",
"i": "^0.3.6",
"jsdom": "^11.3.0",
Expand Down
10 changes: 5 additions & 5 deletions test/mem-server.fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('MemServer fixture constraint feature', function() {

assert.throws(() => MemServer.start(), (err) => {
return (err instanceof Error) &&
/MemServer DATABASE ERROR\: At least one of your PhotoComment fixtures missing a primary key\. Please make sure all your PhotoComment fixtures have either id or uuid primaryKey/.test(err);
/\[MemServer\] DATABASE ERROR\: At least one of your PhotoComment fixtures missing a primary key\. Please make sure all your PhotoComment fixtures have either id or uuid primaryKey/.test(err);
});

assert.deepEqual(MemServer.Pretender, {});
Expand Down Expand Up @@ -158,7 +158,7 @@ describe('MemServer fixture constraint feature', function() {

assert.throws(() => MemServer.start(), (err) => {
return (err instanceof Error) &&
/MemServer Photo model primaryKey type is 'id'. Instead you've tried to enter id: 2 with string type/.test(err);
/\[MemServer\] Photo model primaryKey type is 'id'. Instead you've tried to enter id: 2 with string type/.test(err);
});

assert.deepEqual(MemServer.Pretender, {});
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('MemServer fixture constraint feature', function() {

assert.throws(() => MemServer.start(), (err) => {
return (err instanceof Error) &&
/MemServer PhotoComment model primaryKey type is 'uuid'. Instead you've tried to enter uuid: 12 with number type/.test(err);
/\[MemServer\] PhotoComment model primaryKey type is 'uuid'. Instead you've tried to enter uuid: 12 with number type/.test(err);
});

assert.deepEqual(MemServer.Pretender, {});
Expand Down Expand Up @@ -265,7 +265,7 @@ describe('MemServer fixture constraint feature', function() {

assert.throws(() => MemServer.start(), (err) => {
return (err instanceof Error) &&
/MemServer DATABASE ERROR: Duplication in Photo fixtures with id: 2/.test(err);
/\[MemServer\] DATABASE ERROR: Duplication in Photo fixtures with id: 2/.test(err);
});
});

Expand Down Expand Up @@ -302,7 +302,7 @@ describe('MemServer fixture constraint feature', function() {

assert.throws(() => MemServer.start(), (err) => {
return (err instanceof Error) &&
/MemServer DATABASE ERROR: Duplication in PhotoComment fixtures with uuid: 499ec646-493f-4eea-b92e-e383d94182f4/.test(err);
/\[MemServer\] DATABASE ERROR: Duplication in PhotoComment fixtures with uuid: 499ec646-493f-4eea-b92e-e383d94182f4/.test(err);
});
});
});
2 changes: 1 addition & 1 deletion test/mem-server.model.delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('MemServer.Model Delete Interface', function() {

assert.throws(() => Photo.delete({ id: 1 }), (err) => {
return (err instanceof Error) &&
/\[MemServer] Photo has no records in the database to delete\. Photo\.delete\(\{ id: 1 \}\) failed/.test(err);
/\[MemServer\] Photo has no records in the database to delete\. Photo\.delete\(\{ id: 1 \}\) failed/.test(err);
});
assert.throws(() => PhotoComment.delete({ uuid: '374c7f4a-85d6-429a-bf2a-0719525f5111' }), (err) => {
return (err instanceof Error) &&
Expand Down
4 changes: 2 additions & 2 deletions test/mem-server.model.insert.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,11 @@ describe('MemServer.Model Insert Interface', function() {

assert.throws(() => Photo.insert({ id: '99' }), (err) => {
return (err instanceof Error) &&
/MemServer Photo model primaryKey type is 'id'. Instead you've tried to enter id: 99 with string type/.test(err);
/\[MemServer\] Photo model primaryKey type is 'id'. Instead you've tried to enter id: 99 with string type/.test(err);
});
assert.throws(() => PhotoComment.insert({ uuid: 1 }), (err) => {
return (err instanceof Error) &&
/MemServer PhotoComment model primaryKey type is 'uuid'. Instead you've tried to enter uuid: 1 with number type/.test(err);
/\[MemServer\] PhotoComment model primaryKey type is 'uuid'. Instead you've tried to enter uuid: 1 with number type/.test(err);
});
});
});
Expand Down
33 changes: 33 additions & 0 deletions test/mem-server.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,39 @@ describe('MemServer.Model Interface', function() {
});

it('reads the defaultAttributes correctly', function() {
fs.writeFileSync(`${process.cwd()}/memserver/models/photo.js`, `
import faker from 'faker';
import Model from '${process.cwd()}/lib/mem-server/model';
export default Model({
defaultAttributes: {
is_public: true,
name() {
return faker.name.title();
}
},
publicPhotos() {
return this.findAll({ is_public: true });
}
});
`);
fs.writeFileSync(`${process.cwd()}/memserver/models/photo-comment.js`, `
import moment from 'moment';
import Model from '${process.cwd()}/lib/mem-server/model';
export default Model({
defaultAttributes: {
inserted_at() {
return moment().toJSON();
},
is_important: true
},
forPhoto(photo) {
return this.findAll({ photo_id: photo.id });
}
});
`);

Object.keys(require.cache).forEach((key) => delete require.cache[key]);

const MemServer = require('../index.js');
Expand Down
6 changes: 3 additions & 3 deletions test/mem-server.model.query.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ describe('MemServer.Model Query Interface', function() {
array.forEach((param) => {
assert.throws(() => Photo.find(param), (err) => {
return (err instanceof Error) &&
/MemServer Photo.find\(id\) cannot be called without a valid id/.test(err);
/\[MemServer\] Photo.find\(id\) cannot be called without a valid id/.test(err);
});
assert.throws(() => PhotoComment.find(param), (err) => {
return (err instanceof Error) &&
/MemServer PhotoComment.find\(id\) cannot be called without a valid id/.test(err);
/\[MemServer\] PhotoComment.find\(id\) cannot be called without a valid id/.test(err);
});
});
});
Expand Down Expand Up @@ -134,7 +134,7 @@ describe('MemServer.Model Query Interface', function() {

assert.throws(() => Photo.findBy(), (err) => {
return (err instanceof Error) &&
/MemServer Photo.findBy\(id\) cannot be called without a parameter/.test(err);
/\[MemServer\] Photo.findBy\(id\) cannot be called without a parameter/.test(err);
});
});

Expand Down
Loading

0 comments on commit a18ecad

Please sign in to comment.