Created
January 12, 2018 03:31
-
-
Save newhouse/cd0f05ef83308d9972fb00df93ff2f95 to your computer and use it in GitHub Desktop.
Objection.js: insert-validating-as-if-insertGraph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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