Skip to content

Instantly share code, notes, and snippets.

@newhouse
Created January 12, 2018 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save newhouse/cd0f05ef83308d9972fb00df93ff2f95 to your computer and use it in GitHub Desktop.
Save newhouse/cd0f05ef83308d9972fb00df93ff2f95 to your computer and use it in GitHub Desktop.
Objection.js: insert-validating-as-if-insertGraph
'use strict';
const assert = require('assert');
const _ = require('lodash');
const objection = require('objection');
const Model = objection.Model;
const knex = require('knex')({
debug: false,
client: 'pg',
connection: '<CONNECTION_STRING_HERE>',
pool: {
min: 2,
max: 2
}
});
Model.knex(knex);
/**************************************************
* ___ _ _
* | \ __ _| |_ __ _| |__ __ _ ___ ___
* | |) / _` | _/ _` | '_ \/ _` (_-</ -_)
* |___/\__,_|\__\__,_|_.__/\__,_/__/\___|
*
*/
const createTables = () => {
return knex.schema.createTableIfNotExists('parents', table => {
table.integer('id').notNullable();
table.primary(['id']);
})
.then(() => {
return knex.schema.createTableIfNotExists('children', table => {
table.integer('id').notNullable();
table.string('foo', 100);
table.integer('parent_id').references('parents.id').onDelete('CASCADE').notNullable();
table.primary(['id']);
});
});
};
const dropTables = () => {
return knex.schema.dropTableIfExists('children')
.then(() => {
return knex.schema.dropTableIfExists('parents');
});
};
/**************************************************
* __ __ _ _
* | \/ |___ __| |___| |___
* | |\/| / _ \/ _` / -_) (_-<
* |_| |_\___/\__,_\___|_/__/
*
*/
class Base extends Model {
static get pickJsonSchemaProperties() {
return true;
}
}
class Parent extends Base {
static get tableName() {
return 'parents';
}
static get jsonSchema() {
return {
type: 'object',
required: ['id'],
properties: {
id: {
type: 'integer'
}
}
};
}
static get relationMappings() {
return {
children: {
relation: Model.HasManyRelation,
modelClass: Child,
join: {
from: 'parents.id',
to: 'children.parent_id'
}
}
};
}
}
class Child extends Base {
static get tableName() {
return 'children';
}
static get jsonSchema() {
return {
type: 'object',
required: ['id', 'foo', 'parent_id'],
properties: {
id: {
type: 'integer'
},
foo: {
type: 'string'
},
parent_id: {
type: 'integer'
}
}
};
}
static get relationMappings() {
return {
parent: {
relation: Model.BelongsToOneRelation,
modelClass: Parent,
join: {
from: 'children.parent_id',
to: 'parents.id'
}
}
};
}
}
//
//
//**************************************************
let parent;
let similarParent;
let similarParentData;
const parentData = {
id: 1,
children: [
{
id: 1,
foo: 'something interesting'
}
]
};
// HERE WE GO
return dropTables()
.then(() => {
return createTables();
})
.then(() => {
// I want to insertGraph here, so I'll use the 'parentData' I've already created
return Parent
.query()
.insertGraph(parentData)
.returning('*');
})
.then(p => {
parent = p;
// I don't know or care what is in 'parent', all I know is that I want it all except
// for the 'id' property.
// This would also work (to cause the issue):
// similarParentData = _.cloneDeep(parentData);
similarParentData = _.omit(parentData, ['id']);
// I'll give it another ID here since I just want something that's really similar to the
// original to go into the DB.
similarParentData.id = 2;
// Because this is just a regular 'insert' and not an 'insertGraph', I would expect that Objection
// would ignore the 'children' property in the data and not bother to validate it.
return Parent
.query()
.insert(similarParentData);
})
.then(() => {
throw new Error('Umm, for me I would never get here.');
})
.catch(err => {
console.log('Making sure there was a ValidationError');
// But I would be wrong.
assert.equal(err.constructor.name, 'ValidationError');
return Promise.resolve();
})
.then(() => {
// To highlight a bit more, let me go ahead and add a bogus 'parent_id' to the Child.
// The DB would throw an FK/refreneces error here if it were to try and actually insert this.
similarParentData.children[0].parent_id = 9999;
console.log('Inserting similarParent data with bogus children[0].parent_id');
return Parent
.query()
.eager('children')
// Still only doing a plain-old 'insert'
.insert(similarParentData)
.returning('*');
})
.then(sp => {
similarParent = sp;
// Since it didn't blow up, it would suggest that it's performing the validations on the 'children' relation
// but not actually trying to insert it. There should be no children, either:
console.log('Making sure the similarParent has no children');
assert.ok(similarParent.children.length === 0, 'Should have no children since it was a non-graph insert.');
// Why you do dat?
//
// ¯\_(ツ)_/¯
//
return dropTables();
})
.catch(err => {
console.warn(err);
return dropTables();
})
.then(() => {
console.log('DONE!');
process.exit();
})
.catch(err => {
console.log(err);
process.exit();
});
{
"name": "objection_insert-using-insertGraph",
"version": "0.0.1",
"description": "Objection 4-eva",
"main": "main.js",
"scripts": {
"start": "node ./app.js"
},
"engines": {
"node": "6.11.1"
},
"dependencies": {
"knex": "0.14.2",
"lodash": "latest",
"objection": "0.9.4",
"pg": "7.4.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment