Skip to content
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

SQL Generation #31

Merged
merged 49 commits into from
Sep 29, 2016
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0f46477
Add skeleton of plugin and example hapi server. Issue #30
eliasmalik Sep 23, 2016
70b3e00
Add env2 for DB config: issue #34
eliasmalik Sep 23, 2016
80522d4
add hapi-postgres-connection as a plugin to example server #33
eliasmalik Sep 23, 2016
e834b0b
use pg instead of hapi-postgres-connection because we need access to …
eliasmalik Sep 23, 2016
228ebc4
update sql queries in plugin setup
eliasmalik Sep 26, 2016
b3c23d1
add first pass of sql data map
eliasmalik Sep 26, 2016
c435d44
[WIP] add module file for generating sql, and corresponding test file
eliasmalik Sep 26, 2016
5821399
:heavy_plus_sign: mapper for creating sql columns
jrans Sep 26, 2016
e059f22
change field-names to align with Joi API; moved away from JSON schema…
eliasmalik Sep 26, 2016
da01305
write and test init method of sqlGen module to make query to create t…
eliasmalik Sep 26, 2016
3cb409a
write and test function to generate update SQL queries for arbitrary …
eliasmalik Sep 26, 2016
3110924
rename previous 'update' method to 'insert' (since that's what it doe…
eliasmalik Sep 26, 2016
9c5fb29
delete dead line
eliasmalik Sep 26, 2016
a45b93f
:heavy_plus_sign: config validator
jrans Sep 26, 2016
31ce282
Merge branch 'sql-generation' of https://github.com/dwyl/abase into s…
jrans Sep 26, 2016
431e786
change module inputs to inject table_name directly instead of entire …
eliasmalik Sep 26, 2016
9ee0209
write and test sql generator for SELECT queries
eliasmalik Sep 26, 2016
5cd2536
write and test SQL generator function for DELETE queries
eliasmalik Sep 26, 2016
e8dd68a
Merge branch 'sql-generation' of https://github.com/dwyl/abase into s…
eliasmalik Sep 26, 2016
ad3139b
migrate to new interface, expecting an options object instead to conf…
eliasmalik Sep 26, 2016
5b08cf6
add where clause to select method
eliasmalik Sep 26, 2016
aff1574
:bug: allow extra keys on field config #39
jrans Sep 26, 2016
0f670c8
:art: :fire: refactor out test fixture
jrans Sep 26, 2016
35f96ad
Merge branch 'sql-generation' of https://github.com/dwyl/abase into s…
jrans Sep 26, 2016
3296cab
:heavy_plus_sign: Expose database methods #11
jrans Sep 27, 2016
743656a
Fix tests
eliasmalik Sep 27, 2016
f6a61b5
Merge branch 'master' into sql-generation
eliasmalik Sep 27, 2016
b2d42f6
:memo: Update readme on test config
jrans Sep 27, 2016
b7c8d8d
Merge branch 'sql-generation' of https://github.com/dwyl/abase into s…
jrans Sep 27, 2016
f3b5da6
delete .eslintrc file, b/c will be using goodparts package v. soon
eliasmalik Sep 27, 2016
866aae6
add utils module to help with DRYing out the sql_gen module
eliasmalik Sep 27, 2016
e11d697
only drop table if it exists, to avoid error
eliasmalik Sep 27, 2016
a8f20c0
DRY out sql_gen
eliasmalik Sep 27, 2016
4b16cda
Merge branch 'sql-generation' of https://github.com/dwyl/abase into s…
eliasmalik Sep 27, 2016
22f8643
symlink the good parts eslint config for now, because we'd need a plu…
eliasmalik Sep 28, 2016
208b982
:art: :bug: Change confgig validator to throw #39
jrans Sep 28, 2016
d4f8fea
fixing merge conflicts
samhstn Sep 28, 2016
a57d5d3
fixing linting errors
samhstn Sep 28, 2016
cd802d4
change eslint back to goodparts
eliasmalik Sep 28, 2016
d9eefed
auto fix linter errors
eliasmalik Sep 28, 2016
ae66662
fix linter errors
eliasmalik Sep 28, 2016
cc62b23
add config.env to gitignore
eliasmalik Sep 28, 2016
ea3daca
Merge branch 'master' into goodparts
eliasmalik Sep 28, 2016
9b05448
fixing linting
samhstn Sep 28, 2016
19889da
bring branch coverage back up to 100%
eliasmalik Sep 28, 2016
2148f3f
add istanbul YAML
eliasmalik Sep 28, 2016
273bc9c
add postgres to travis to fix failing tests
eliasmalik Sep 28, 2016
7389e59
create travis test database before script execution
eliasmalik Sep 28, 2016
281c883
:art: Change ordering of require
jrans Sep 28, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
.DS_Store
coverage
config.env
File renamed without changes.
21 changes: 21 additions & 0 deletions examples/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

require('env2')('config.env');
var Hapi = require('hapi');
var Abase = require('../lib/index.js');
var server = new Hapi.Server();
var config = require('./config.json');

server.connection({
port: process.env.PORT || 8888,
});

server.register({ register: Abase, options: config }, function (err) {
if (err) {
throw err;
}

server.start(function () {
console.log('Server started');
});
});
20 changes: 20 additions & 0 deletions lib/config_validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var Joi = require('joi');

var mapObj = require('./create_table_map.js').mapObj;

var fieldTypes = Object.keys(mapObj);
var dbNameRegEx = /^[A-Za-z_]\w{0,62}$/;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like a comment here explaining this regex

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIll do!

var configSchema = Joi.object().keys({
table_name: Joi.string().regex(dbNameRegEx).required(),
fields: Joi.object().pattern(
dbNameRegEx,
Joi.object().keys({type: Joi.any().valid(fieldTypes)})
).required()
});

module.exports = function (config) {
return Joi.validate(config, configSchema);
};

module.exports.dbNameRegEx = dbNameRegEx;
27 changes: 27 additions & 0 deletions lib/create_table_map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

var mapObj = {
number: function (opts) {
return opts.integer ? 'BIGINT' : 'DOUBLE PRECISION';
},
string: function (opts) {
var length = opts.max || 80;

return 'VARCHAR(' + length + ')';
},
boolean: function () {
return 'BOOLEAN';
},
date: function (opts) {
return opts.timestamp ? 'TIMESTAMP' : 'DATE';
}
};

function mapper (name, type, options) {
var opts = options || {};

return name + ' ' + mapObj[type](opts);
};

module.exports = mapper;
module.exports.mapObj = mapObj;
37 changes: 37 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

var pg = require('pg');

exports.register = function (server, options, next) {

var dbUrl = process.env.DATABASE_URL;
server.app.pool = new pg.Pool(dbUrl);

server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
var select = 'select $1::text from information_schema.tables';

server.app.pool.query(select, ['table_name'], function(err, result) {
return reply(result);
});
}
});

server.app.pool.query('CREATE TABLE IF NOT EXISTS '
+ '"' + options.tableName + '"'
+ ' ('
+ 'email varchar(80),'
+ 'username varchar(80)'
+ ')'
, function (err, result) {
console.log(err, result);
});
};

exports.register.attributes = {
pkg: {
name: 'Abase'
}
};
20 changes: 20 additions & 0 deletions lib/sql_gen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

var mapper = require('./create_table_map.js');

exports.init = function init (config) {
var tableName = config.table_name;
var fields = config.fields;
var query = 'CREATE TABLE IF NOT EXISTS "' + tableName + '" ';

var columns = Object.keys(fields).map(function (key) {
var type = fields[key].type;
var opts = fields[key];

return mapper(key, type, opts);
});

query += '(' + columns.join(', ') + ')';

return query;
};
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"homepage": "https://github.com/dwyl/abase#readme",
"devDependencies": {
"eslint": "^3.5.0",
"hapi": "^15.0.3",
"istanbul": "^0.4.5",
"pre-commit": "^1.1.3",
"tape": "^4.6.0"
Expand All @@ -29,5 +30,9 @@
"pre-commit": [
"lint",
"test"
]
],
"dependencies": {
"env2": "^2.1.1",
"pg": "^6.1.0"
}
}
55 changes: 55 additions & 0 deletions test/config_validator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var test = require('tape');

var validator = require('../lib/config_validator.js');
var dbNameRegEx = validator.dbNameRegEx;

test('config validator', function (t) {
t.ok(
validator({ fields: {} }).error,
'error if no table_name property'
);
t.ok(
validator({ table_name: 'test' }).error,
'error if no fields property'
);
t.ok(
validator({ table_name: '2test', fields: {} }).error,
'error if table name doesn\t pass db name regex'
);
t.ok(
validator({ table_name: '2test', fields: {} }).error,
'error if table name doesn\t pass db name regex'
);
t.ok(
validator({
table_name: 'test',
fields: {'2field': {type: 'string'}}
}).error,
'error if field name doesn\'t pass db name regex'
);

t.end();
});

test('dbNameRegEx', function (t) {
t.ok(
dbNameRegEx.exec('_a1pha_Numer1c'),
'alpha numeric keys allowed only'
);
t.notOk(
dbNameRegEx.exec('no£way'),
'no other characters allowed'
);
t.notOk(
dbNameRegEx.exec('3Numer1c'),
'must only start with a _ or letter'
);
t.notOk(
dbNameRegEx.exec(
'_morethan63characters_morethan63characters_morethan63characters_'
),
'63 character limit for field names'
);

t.end();
});
76 changes: 76 additions & 0 deletions test/create_table_map.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict';

var test = require('tape');

var mapper = require('../lib/create_table_map.js');

var mapObj = mapper.mapObj;

test('Boolean type', function (t) {
t.equal(
mapObj['boolean']({}),
'BOOLEAN',
'boolean type: default'
);
t.end();
});

test('Date type', function (t) {
t.equal(
mapObj['date']({}),
'DATE',
'date type: default'
);
t.equal(
mapObj['date']({ timestamp: true }),
'TIMESTAMP',
'date type: timestamp'
);
t.end();
});

test('Number type', function (t) {
t.equal(
mapObj['number']({}),
'DOUBLE PRECISION',
'number type: default'
);
t.equal(
mapObj['number']({ integer: true }),
'BIGINT',
'number type: integer'
);
t.end();
});

test('String type', function (t) {
t.equal(
mapObj['string']({}),
'VARCHAR(80)',
'string type: default'
);
t.equal(
mapObj['string']({ max: 12 }),
'VARCHAR(12)',
'string type: specifies length'
);
t.end();
});

test('Create Table Mapper Function', function (t) {
t.equal(
mapper('field', 'string', { max: 140 }),
'field VARCHAR(140)',
'name added to sql query and options passed through'
);
t.end();
});

test('Create Table Mapper Function w/ no options', function (t) {
t.equal(
mapper('field', 'string'),
'field VARCHAR(80)',
'name added to sql query and default options used'
);
t.end();
});
48 changes: 48 additions & 0 deletions test/sql_gen.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

var tape = require('tape');
var sqlGen = require('../lib/sql_gen.js');

var schema = {
table_name: 'user_data',
fields: {
email: {
type: 'string',
email: true
},
username: {
type: 'string',
min: 3,
max: 20
},
dob: {
type: 'date'
}
}
};

tape('::init should throw on empty or invalid input', function (t) {
t.throws(function () {
sqlGen.init();
});
t.end();
});

tape('::init should generate SQL to create a table if none exists', function (t) {
var query = sqlGen.init(schema);

t.equal(
query,
'CREATE TABLE IF NOT EXISTS "user_data" ('
+ 'email VARCHAR(80), '
+ 'username VARCHAR(20), '
+ 'dob DATE'
+ ')',
'Create table query generation from config object'
);
t.end();
});

// tape('::update should generate empty string on invalid input', function () {});

// tape('::update should generate SQL to update a column in a table', function () {});