-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add migration support for sqlite, better validation #4
Open
cameronblandford
wants to merge
33
commits into
master
Choose a base branch
from
feature/migrations
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
6c5d6cb
Begin working on migrations
cameronblandford 0f2b1d7
Add support for remaking and altering existing columns
cameronblandford 42a037f
Merge branch 'master' of https://github.com/221B-io/json-to-knex into…
cameronblandford a37a49e
prettify validation example
cameronblandford 126a67d
Improve validation example
cameronblandford bb08f16
Add postgres test file
cameronblandford d359907
Show postgres working
cameronblandford 257a929
Move schemas into own file, add stringify util for readability/space
cameronblandford b62b40e
Get postgres tests working
cameronblandford 5963782
Remove obsolete comments
cameronblandford 9c35a09
Add basics for private migrations table management
cameronblandford 6ea88ae
Add instructions on how to migrate between schemas
cameronblandford 7835289
Update README.md
cameronblandford 190af29
Add full schema migration function
cameronblandford a280f84
Merge branch 'feature/migrations' of https://github.com/221B-io/json-…
cameronblandford ab4c6a3
Add cli stubs
cameronblandford c52291d
Refactor using utility function
cameronblandford 6354d73
Add non-internal migrating to db manager
cameronblandford 7c2c08f
Fix some bugs in the manager, add a test for the manager
cameronblandford c08e9de
Add full, passing tests of functionality with postgres
cameronblandford 7fc93df
Fix tests, some bugs, add test stub for validation specifically
cameronblandford 2503f90
Fix async issue
cameronblandford 646ad6d
Add JSDoc stubs for methods
cameronblandford 8dc5439
Update docs
cameronblandford 7363f68
Add comments
cameronblandford f7e0383
Remove unnecessary knex initialization
cameronblandford 7703aae
Change raw queries to knex chains
cameronblandford 3efc3be
Fix async issues, clean up
cameronblandford a52bdfd
Fix bug where table and column changes would happen in wrong order
cameronblandford 184b749
Remove obsolete comments
cameronblandford 620e693
Attempt to add CI
cameronblandford 1a53631
Fix copy/paste typo
cameronblandford f48c7f8
Start to fix tests
cameronblandford File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
language: node_js | ||
node_js: | ||
- "stable" | ||
cache: | ||
directories: | ||
- "node_modules" | ||
script: | ||
- npm test |
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
Empty file.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const fs = require("fs"); | ||
const migrator = require("../lib/migrations"); | ||
const dbManager = require("../lib/db-manager"); | ||
// check for dbSchema.json | ||
|
||
(async () => { | ||
// stub | ||
})(); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const fs = require("fs"); | ||
const migrator = require("../lib/migrations"); | ||
const dbManager = require("../lib/db-manager"); | ||
// check for dbSchema.json | ||
|
||
(async () => { | ||
await dbManager.makeMigrationsTable(); | ||
})(); |
Empty file.
Empty file.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,75 @@ | ||
const Ajv = require('ajv'); | ||
const fse = require('fse'); | ||
const path = require('path'); | ||
const Ajv = require("ajv"); | ||
const fse = require("fse"); | ||
const path = require("path"); | ||
|
||
const getSchema = (uri ) => { | ||
return request.json(uri).then(function (res) { | ||
const getSchema = uri => { | ||
return request.json(uri).then(function(res) { | ||
if (res.statusCode >= 400) | ||
throw new Error('Loading error: ' + res.statusCode); | ||
throw new Error("Loading error: " + res.statusCode); | ||
return res.body; | ||
}); | ||
} | ||
|
||
}; | ||
|
||
// const loadSchema = getSchema; | ||
|
||
async function go() { | ||
const base = '../sql-json-schema/' | ||
const base = "../sql-json-schema/"; | ||
|
||
const loadSchema = (uri) => { | ||
const loadSchema = uri => { | ||
return fse.readJson(path.join(base, uri)); | ||
} | ||
}; | ||
|
||
const schemaSchema = await fse.readJson(path.join(base, 'schema.schema.json')) | ||
const ajv = new Ajv({ loadSchema, }); | ||
const schemaSchema = await fse.readJson( | ||
path.join(base, "schema.schema.json") | ||
); | ||
const ajv = new Ajv({ loadSchema }); | ||
require("ajv-merge-patch")(ajv); // add merge and patch compatibility | ||
|
||
ajv.compileAsync( | ||
schemaSchema | ||
).then((validate) => { | ||
const valid = validate( | ||
{ | ||
"tables": [ | ||
{ | ||
"name": "users", | ||
"columns": [ | ||
{ | ||
"name": "firstName", | ||
"type": "float", | ||
scale: 'something' | ||
} | ||
] | ||
} | ||
] | ||
} | ||
) | ||
if(!valid) { | ||
console.log('Error!'); | ||
ajv.compileAsync(schemaSchema).then(validate => { | ||
const valid = validate({ | ||
tables: [ | ||
{ | ||
name: "users", | ||
columns: [ | ||
{ | ||
name: "age", | ||
type: "integer", | ||
unsigned: false | ||
}, | ||
{ | ||
name: "firstName", | ||
type: "string" | ||
}, | ||
{ | ||
name: "bio", | ||
type: "text", | ||
default: "This bio is currently empty." | ||
}, | ||
{ | ||
name: "age", | ||
type: "integer" | ||
}, | ||
{ | ||
name: "mightBeValid", | ||
type: "integer" | ||
// , unrecognizedField: 5 // will invalidate | ||
}, | ||
{ | ||
name: "friendId", | ||
type: "integer", | ||
unsigned: true | ||
} | ||
] | ||
} | ||
] | ||
}); | ||
if (!valid) { | ||
console.log("Error!"); | ||
console.log(validate.errors); | ||
} else { | ||
console.log('Valid!'); | ||
console.log("Valid!"); | ||
} | ||
}) | ||
}); | ||
} | ||
|
||
go() | ||
// go(); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
const _ = require("lodash"); | ||
const validateColumn = require("./validation"); | ||
|
||
// Values that can go in a column's "type" field | ||
const types = { | ||
binary: (table, name, options) => { | ||
return table.binary(name, options.length); | ||
|
@@ -55,9 +56,15 @@ const types = { | |
}, | ||
uuid: (table, name) => { | ||
return table.uuid(name); | ||
}, | ||
foreign: (table, name, options) => { | ||
debugger; | ||
console.log(`Creating foreign key ${options.foreign}`); | ||
return table.foreign(options.foreign); | ||
} | ||
}; | ||
|
||
// Fields that can go in a column | ||
const typeProperties = { | ||
primary: (chain, value) => { | ||
if (value === true) { | ||
|
@@ -77,9 +84,6 @@ const typeProperties = { | |
} | ||
return chain; | ||
}, | ||
foreign: (table, name) => { | ||
return table.foreign(name); | ||
}, | ||
references: (chain, value) => { | ||
return chain.references(value); | ||
}, | ||
|
@@ -100,69 +104,109 @@ const typeProperties = { | |
}, | ||
index: (chain, value) => { | ||
return value ? chain.index() : chain; | ||
}, | ||
onDelete: (chain, value) => { | ||
console.log(chain); | ||
console.log(value); | ||
return chain.onDelete(value); | ||
} | ||
}; | ||
|
||
function createTable(chain, name, schema) { | ||
/** | ||
* Creates a table with columns specified by the schema | ||
* @param {*} chain | ||
* @param {*} name | ||
* @param {*} tableSchema | ||
*/ | ||
async function createTable(chain, name, tableSchema) { | ||
// Usually start with knex.createTable.... | ||
return chain.createTable(name, table => { | ||
const sawPrimary = false; | ||
return await chain.createTable(name, table => { | ||
_.forEach(tableSchema.columns, column => { | ||
createColumn(table, column); | ||
}); | ||
}); | ||
} | ||
|
||
_.forEach(schema.columns, column => { | ||
const columnName = column.name; | ||
// e.g., id: { type: "increments" } | ||
if (_.isPlainObject(column)) { | ||
validateColumn(column); | ||
if (_.has(types, column.type)) { | ||
let columnChain = types[column.type](table, columnName, column); | ||
const order = _.uniq( | ||
_.concat( | ||
["primary", "type", "unsigned", "references", "inTable"], | ||
_.keys(column) | ||
) | ||
/** | ||
* Creates a column in the passed-in table using the specified column config | ||
* @param {*} table | ||
* @param {*} column | ||
*/ | ||
async function createColumn(table, column, alter = false) { | ||
const columnName = column.name; | ||
// e.g., id: { type: "increments" } | ||
debugger; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More.... |
||
if (_.isPlainObject(column) && _.has(types, column.type)) { | ||
let columnChain = types[column.type](table, columnName, column); | ||
// correctly orders the properties to be chained: | ||
// the ones where order is important, then all the rest | ||
const order = _.uniq( | ||
_.concat( | ||
["primary", "type", "unsigned", "references", "inTable"], | ||
_.keys(column) | ||
) | ||
); | ||
|
||
// for each item in the order | ||
_.forEach(order, columnPropertyName => { | ||
if (_.has(column, columnPropertyName)) { | ||
// if it exists in the column def, apply the related chain method | ||
const columnProperty = column[columnPropertyName]; | ||
if (_.has(typeProperties, columnPropertyName)) { | ||
columnChain = typeProperties[columnPropertyName]( | ||
columnChain, | ||
columnProperty | ||
); | ||
_.forEach(order, columnPropertyName => { | ||
if (_.has(column, columnPropertyName)) { | ||
const columnProperty = column[columnPropertyName]; | ||
if (_.has(typeProperties, columnPropertyName)) { | ||
columnChain = typeProperties[columnPropertyName]( | ||
columnChain, | ||
columnProperty | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
} else { | ||
throw Error( | ||
`Column '${ | ||
column.type | ||
}' of '${name}:${columnName}' is not of recognized type` | ||
); | ||
} | ||
}); | ||
}); | ||
|
||
// this is only for altering columns that already exist | ||
if (alter) { | ||
columChain = columnChain.alter(); | ||
} | ||
} else { | ||
throw Error( | ||
`Column '${ | ||
column.type | ||
}' of '${name}:${columnName}' is not of recognized type` | ||
); | ||
} | ||
} | ||
|
||
function createTables(knex, schema) { | ||
/** | ||
* Creates a series of tables based off a db JSON schema | ||
* @param {*} knex a knex instance | ||
* @param {JSON} schema the JSON schema to be used as the blueprint | ||
* @returns {*} a chainable knex schema object | ||
*/ | ||
async function createTables(knex, schema) { | ||
let chain = knex.schema; | ||
_.forEach(schema.tables, tableConfig => { | ||
for (let i in schema.tables) { | ||
const tableConfig = schema.tables[i]; | ||
const tableName = tableConfig.name; | ||
chain = createTable(chain, tableName, tableConfig); | ||
}); | ||
chain = await createTable(chain, tableName, tableConfig); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the await necessary here? |
||
} | ||
return chain; | ||
} | ||
|
||
function dropTablesIfExists(knex, schema) { | ||
/** | ||
* Removes all tables listed in the passed-in schema | ||
* @param {*} knex a knex instance | ||
* @param {Object} schema the JSON schema to be used | ||
* @returns {*} | ||
*/ | ||
async function dropTablesIfExists(knex, schema) { | ||
let chain = knex.schema; | ||
_.forEach(schema.tables, tableConfig => { | ||
_.forEach(schema.tables.reverse(), async tableConfig => { | ||
const tableName = tableConfig.name; | ||
chain.dropTableIfExists(tableName); | ||
await chain.dropTableIfExists(tableName); | ||
}); | ||
return chain; | ||
} | ||
|
||
module.exports = { | ||
createColumn, | ||
createTable, | ||
createTables, | ||
dropTablesIfExists | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still working on this?