db2graphql
Advanced tools
Comparing version 0.10.0 to 0.11.0
{ | ||
"name": "db2graphql", | ||
"version": "0.10.0", | ||
"version": "0.11.0", | ||
"description": "Generate Graphql schema based on existing relational database", | ||
@@ -10,2 +10,5 @@ "main": "index.js", | ||
"scripts": { | ||
"prettier": "prettier --write ./src", | ||
"lint": "eslint ./src", | ||
"check": "npm run prettier && npm run lint && npm test", | ||
"demo-pg": "psql -h localhost -U postgres -f ./demo/database.sql db2graphql", | ||
@@ -44,2 +47,3 @@ "demo-mysql": "mysql --user=root --password db2graphql < ./demo_mysql/database.sql", | ||
"coveralls": "^3.1.1", | ||
"eslint": "^8.32.0", | ||
"graphql": "^16.0.0", | ||
@@ -52,3 +56,4 @@ "jest": "^26.6.3", | ||
"nodemon": "^2.0.20", | ||
"pg": "^8.8.0" | ||
"pg": "^8.8.0", | ||
"prettier": "^2.8.3" | ||
}, | ||
@@ -55,0 +60,0 @@ "jest": { |
@@ -46,7 +46,6 @@ const hash = require('string-hash-64'); | ||
class MSSql { | ||
/** | ||
* Create a new adapter instance | ||
* | ||
* @param {Object} db | ||
* | ||
* @param {Object} db | ||
*/ | ||
@@ -68,9 +67,9 @@ constructor(db, dbSchema = {}) { | ||
* Get Graphql type from database column data type | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
*/ | ||
mapDbColumnToGraphqlType(columnname, attrs) { | ||
let graphqlType = ''; | ||
switch(attrs.data_type) { | ||
switch (attrs.data_type) { | ||
case 'bit': | ||
@@ -104,3 +103,9 @@ graphqlType = 'Boolean'; | ||
break; | ||
default: throw new Error('Undefined column type: ' + attrs.data_type + ' of column '+ columnname); | ||
default: | ||
throw new Error( | ||
'Undefined column type: ' + | ||
attrs.data_type + | ||
' of column ' + | ||
columnname | ||
); | ||
} | ||
@@ -112,8 +117,7 @@ return graphqlType; | ||
* Get a page of the table | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async page(tablename, args) { | ||
// Load from cache | ||
@@ -128,6 +132,7 @@ const key = this.getCacheKey(tablename, 'page', [], args); | ||
let query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
(args) && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
args && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
const items = await query; | ||
@@ -140,10 +145,10 @@ this.cache.set(key, items); | ||
* Get pagination total | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async pageTotal(tablename, args) { | ||
const query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
const totalResult = await query.count('* as total'); | ||
@@ -156,13 +161,13 @@ let result = JSON.parse(JSON.stringify(totalResult)); | ||
* Get one record | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async firstOf(tablename, args) { | ||
// Load item | ||
let query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
return await query.first(); | ||
@@ -173,5 +178,5 @@ } | ||
* Insert or update record | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
*/ | ||
@@ -183,4 +188,7 @@ async putItem(tablename, data) { | ||
// Check exists | ||
let count = [{"total": "0"}]; | ||
if (data.input[pk]) count = await this.db(tablename).where(pk, data.input[pk]).count('* as total'); | ||
let count = [{ total: '0' }]; | ||
if (data.input[pk]) | ||
count = await this.db(tablename) | ||
.where(pk, data.input[pk]) | ||
.count('* as total'); | ||
@@ -191,15 +199,15 @@ // Insert or update | ||
query.returning(pk); | ||
const lastIdresult = await query.insert( | ||
data.input, | ||
[pk], | ||
{ includeTriggerModifications: true } | ||
); | ||
const lastIdresult = await query.insert(data.input, [pk], { | ||
includeTriggerModifications: true | ||
}); | ||
result = [lastIdresult[0]]; | ||
if (data._debug) console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
} else { | ||
let query = this.db(tablename) | ||
query.where(pk, data.input[pk]) | ||
let query = this.db(tablename); | ||
query.where(pk, data.input[pk]); | ||
delete data.input[pk]; | ||
result = await query.update(data.input); | ||
if (data._debug) console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
} | ||
@@ -211,5 +219,5 @@ return result; | ||
* Load knex query with condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
*/ | ||
@@ -220,6 +228,6 @@ convertConditionToWhereClause(query, condition) { | ||
let value = condition[2]; | ||
switch(op) { | ||
switch (op) { | ||
case '~': | ||
op = 'ilike'; | ||
value = '%'+value.replace(' ', '%')+'%'; | ||
value = '%' + value.replace(' ', '%') + '%'; | ||
query.where(column, 'ilike', value); | ||
@@ -240,8 +248,7 @@ break; | ||
* Load knex query with all filters | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgs(tablename, query, args) { | ||
// Validate filter arguments | ||
@@ -260,8 +267,7 @@ if (!args.filter) return; | ||
* Load knex query with where condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgsWhere(query, args) { | ||
// Validate filter arguments | ||
@@ -277,8 +283,7 @@ if (!args.where) return; | ||
* Load knex query with all pagination | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addPaginationFromArgs(tablename, query, args) { | ||
// Validate pagination arguments | ||
@@ -293,3 +298,3 @@ if (!args.pagination) return; | ||
let value = pagination[i][1]; | ||
switch(op) { | ||
switch (op) { | ||
case 'limit': | ||
@@ -302,3 +307,3 @@ query.limit(value); | ||
case 'orderby': | ||
value = value.split(' ') | ||
value = value.split(' '); | ||
query.orderBy(value[0], value[1]); | ||
@@ -312,8 +317,8 @@ break; | ||
* Run a raw SQL query | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
*/ | ||
async query(sql, params) { | ||
let result = (await this.db.raw(sql, params)); | ||
let result = await this.db.raw(sql, params); | ||
return JSON.parse(JSON.stringify(result)); | ||
@@ -324,11 +329,12 @@ } | ||
* Generate cache key | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
*/ | ||
getCacheKey(tablename, columnname, ids, args) { | ||
const filteredArgs = { filter: args.filter, pagination: args.pagination }; | ||
let key = tablename + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
let key = | ||
tablename + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
return String(hash(key)); | ||
@@ -340,4 +346,4 @@ } | ||
* database table names | ||
* | ||
* @param {Array} exclude | ||
* | ||
* @param {Array} exclude | ||
*/ | ||
@@ -347,3 +353,3 @@ getExcludeCondition(exclude) { | ||
if (!exclude || exclude.length === 0) return sql; | ||
const placeholders = exclude.map(v => '?').join(','); | ||
const placeholders = exclude.map((v) => '?').join(','); | ||
sql += `AND table_name NOT IN (${placeholders})`; | ||
@@ -355,5 +361,5 @@ return sql; | ||
* Get database tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
@@ -376,3 +382,3 @@ async getTables(schemaname, exclude = []) { | ||
} | ||
return tables; | ||
return tables; | ||
} | ||
@@ -383,5 +389,5 @@ | ||
* and create an object representation | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -409,5 +415,5 @@ async getColumns(schemaname, tablename) { | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -436,3 +442,3 @@ async getForeignKeys(schemaname, tablename) { | ||
for (let j = 0; j < rows.length; j++) { | ||
fkeys.push(rows[j]); | ||
fkeys.push(rows[j]); | ||
} | ||
@@ -445,5 +451,5 @@ return fkeys; | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -480,7 +486,9 @@ async getPrimaryKey(schemaname, tablename) { | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
getTableColumnsFromSchema(tablename) { | ||
return Object.keys(this.dbSchema[tablename]).filter(c => c !== '__reverse' && c !== '__pk'); | ||
return Object.keys(this.dbSchema[tablename]).filter( | ||
(c) => c !== '__reverse' && c !== '__pk' | ||
); | ||
} | ||
@@ -491,4 +499,4 @@ | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -502,10 +510,9 @@ getPrimaryKeyFromSchema(tablename) { | ||
* Use exclude parameter to exclude indesired tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
async getSchema(schemaname, exclude = []) { | ||
let dbSchema = {}; | ||
let dbSchema = {}; | ||
// Get tables | ||
@@ -524,3 +531,3 @@ let tables = await this.getTables(schemaname, exclude); | ||
for (let j = 0; j < columns.length; j++) { | ||
let columnname = columns[j].name; | ||
let columnname = columns[j].name; | ||
dbSchema[tablename][columnname] = columns[j]; | ||
@@ -534,3 +541,2 @@ } | ||
for (let j = 0; j < fkeys.length; j++) { | ||
// Assign foreign key definition to column | ||
@@ -559,2 +565,2 @@ dbSchema[tablename][fkeys[j].columnname]['__foreign'] = { | ||
module.exports = MSSql; | ||
module.exports = MSSql; |
@@ -36,7 +36,6 @@ const hash = require('string-hash-64'); | ||
class Mysql { | ||
/** | ||
* Create a new adapter instance | ||
* | ||
* @param {Object} db | ||
* | ||
* @param {Object} db | ||
*/ | ||
@@ -58,9 +57,9 @@ constructor(db, dbSchema = {}) { | ||
* Get Graphql type from database column data type | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
*/ | ||
mapDbColumnToGraphqlType(columnname, attrs) { | ||
let graphqlType = ''; | ||
switch(attrs.data_type) { | ||
switch (attrs.data_type) { | ||
case 'tinyint': | ||
@@ -94,3 +93,9 @@ graphqlType = 'Boolean'; | ||
break; | ||
default: throw new Error('Undefined column type: ' + attrs.data_type + ' of column '+ columnname); | ||
default: | ||
throw new Error( | ||
'Undefined column type: ' + | ||
attrs.data_type + | ||
' of column ' + | ||
columnname | ||
); | ||
} | ||
@@ -102,8 +107,7 @@ return graphqlType; | ||
* Get a page of the table | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async page(tablename, args) { | ||
// Load from cache | ||
@@ -118,6 +122,7 @@ const key = this.getCacheKey(tablename, 'page', [], args); | ||
let query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
(args) && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
args && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
const items = await query; | ||
@@ -130,10 +135,10 @@ this.cache.set(key, items); | ||
* Get pagination total | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async pageTotal(tablename, args) { | ||
const query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
const totalResult = await query.count(); | ||
@@ -146,13 +151,13 @@ let result = JSON.parse(JSON.stringify(totalResult)); | ||
* Get one record | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async firstOf(tablename, args) { | ||
// Load item | ||
let query = this.db(tablename); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
return await query.first(); | ||
@@ -163,5 +168,5 @@ } | ||
* Insert or update record | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
*/ | ||
@@ -173,4 +178,5 @@ async putItem(tablename, data) { | ||
// Check exists | ||
let count = [{"count(*)": "0"}]; | ||
if (data.input[pk]) count = await this.db(tablename).where(pk, data.input[pk]).count(); | ||
let count = [{ 'count(*)': '0' }]; | ||
if (data.input[pk]) | ||
count = await this.db(tablename).where(pk, data.input[pk]).count(); | ||
@@ -182,8 +188,10 @@ // Insert or update | ||
result = [lastIdresult[0]]; | ||
if (data._debug) console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
} else { | ||
let query = this.db(tablename) | ||
query.where(pk, data.input[pk]) | ||
let query = this.db(tablename); | ||
query.where(pk, data.input[pk]); | ||
result = await query.update(data.input); | ||
if (data._debug) console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
} | ||
@@ -195,5 +203,5 @@ return result; | ||
* Load knex query with condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
*/ | ||
@@ -204,6 +212,6 @@ convertConditionToWhereClause(query, condition) { | ||
let value = condition[2]; | ||
switch(op) { | ||
switch (op) { | ||
case '~': | ||
op = 'ilike'; | ||
value = '%'+value.replace(' ', '%')+'%'; | ||
value = '%' + value.replace(' ', '%') + '%'; | ||
query.where(column, 'ilike', value); | ||
@@ -224,8 +232,7 @@ break; | ||
* Load knex query with all filters | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgs(tablename, query, args) { | ||
// Validate filter arguments | ||
@@ -244,8 +251,7 @@ if (!args.filter) return; | ||
* Load knex query with where condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgsWhere(query, args) { | ||
// Validate filter arguments | ||
@@ -261,8 +267,7 @@ if (!args.where) return; | ||
* Load knex query with all pagination | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addPaginationFromArgs(tablename, query, args) { | ||
// Validate pagination arguments | ||
@@ -277,3 +282,3 @@ if (!args.pagination) return; | ||
let value = pagination[i][1]; | ||
switch(op) { | ||
switch (op) { | ||
case 'limit': | ||
@@ -286,3 +291,3 @@ query.limit(value); | ||
case 'orderby': | ||
value = value.split(' ') | ||
value = value.split(' '); | ||
query.orderBy(value[0], value[1]); | ||
@@ -296,5 +301,5 @@ break; | ||
* Run a raw SQL query | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
*/ | ||
@@ -308,11 +313,12 @@ async query(sql, params) { | ||
* Generate cache key | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
*/ | ||
getCacheKey(tablename, columnname, ids, args) { | ||
const filteredArgs = { filter: args.filter, pagination: args.pagination }; | ||
let key = tablename + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
let key = | ||
tablename + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
return String(hash(key)); | ||
@@ -324,4 +330,4 @@ } | ||
* database table names | ||
* | ||
* @param {Array} exclude | ||
* | ||
* @param {Array} exclude | ||
*/ | ||
@@ -331,3 +337,3 @@ getExcludeCondition(exclude) { | ||
if (!exclude || exclude.length === 0) return sql; | ||
const placeholders = exclude.map(v => '?').join(','); | ||
const placeholders = exclude.map((v) => '?').join(','); | ||
sql += `AND table_name NOT IN (${placeholders})`; | ||
@@ -339,5 +345,5 @@ return sql; | ||
* Get database tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
@@ -360,3 +366,3 @@ async getTables(schemaname, exclude = []) { | ||
} | ||
return tables; | ||
return tables; | ||
} | ||
@@ -367,5 +373,5 @@ | ||
* and create an object representation | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -393,5 +399,5 @@ async getColumns(schemaname, tablename) { | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -418,3 +424,3 @@ async getForeignKeys(schemaname, tablename) { | ||
for (let j = 0; j < rows.length; j++) { | ||
fkeys.push(rows[j]); | ||
fkeys.push(rows[j]); | ||
} | ||
@@ -427,5 +433,5 @@ return fkeys; | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -462,7 +468,9 @@ async getPrimaryKey(schemaname, tablename) { | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
getTableColumnsFromSchema(tablename) { | ||
return Object.keys(this.dbSchema[tablename]).filter(c => c !== '__reverse' && c !== '__pk'); | ||
return Object.keys(this.dbSchema[tablename]).filter( | ||
(c) => c !== '__reverse' && c !== '__pk' | ||
); | ||
} | ||
@@ -473,4 +481,4 @@ | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -484,10 +492,9 @@ getPrimaryKeyFromSchema(tablename) { | ||
* Use exclude parameter to exclude indesired tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
async getSchema(schemaname, exclude = []) { | ||
let dbSchema = {}; | ||
let dbSchema = {}; | ||
// Get tables | ||
@@ -506,3 +513,3 @@ let tables = await this.getTables(schemaname, exclude); | ||
for (let j = 0; j < columns.length; j++) { | ||
let columnname = columns[j].name; | ||
let columnname = columns[j].name; | ||
dbSchema[tablename][columnname] = columns[j]; | ||
@@ -516,3 +523,2 @@ } | ||
for (let j = 0; j < fkeys.length; j++) { | ||
// Assign foreign key definition to column | ||
@@ -519,0 +525,0 @@ dbSchema[tablename][fkeys[j].columnname]['__foreign'] = { |
@@ -29,7 +29,6 @@ const hash = require('string-hash-64'); | ||
class PostgreSQL { | ||
/** | ||
* Create a new adapter instance | ||
* | ||
* @param {Object} db | ||
* | ||
* @param {Object} db | ||
*/ | ||
@@ -52,9 +51,9 @@ constructor(db, dbSchema = {}) { | ||
* Get Graphql type from database column data type | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
* | ||
* @param {String} columnname | ||
* @param {Object} attrs | ||
*/ | ||
mapDbColumnToGraphqlType(columnname, attrs) { | ||
let graphqlType = ''; | ||
switch(attrs.data_type) { | ||
switch (attrs.data_type) { | ||
case 'boolean': | ||
@@ -79,3 +78,9 @@ graphqlType = 'Boolean'; | ||
break; | ||
default: throw new Error('Undefined column type: ' + attrs.data_type + ' of column '+ columnname); | ||
default: | ||
throw new Error( | ||
'Undefined column type: ' + | ||
attrs.data_type + | ||
' of column ' + | ||
columnname | ||
); | ||
} | ||
@@ -87,8 +92,7 @@ return graphqlType; | ||
* Get a page of the table | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async page(tablename, args) { | ||
// Load from cache | ||
@@ -104,6 +108,7 @@ const key = this.getCacheKey(tablename, 'page', [], args); | ||
let query = this.db(fullname); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
(args) && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
args && this.addPaginationFromArgs(tablename, query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
const items = await query; | ||
@@ -116,5 +121,5 @@ this.cache.set(key, items); | ||
* Get pagination total | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
@@ -124,4 +129,4 @@ async pageTotal(tablename, args) { | ||
const query = this.db(fullname); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
const result = await query.count(); | ||
@@ -133,14 +138,14 @@ return parseInt(result[0].count, 10); | ||
* Get one record | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
async firstOf(tablename, args) { | ||
// Load item | ||
let fullname = [this.schemaname, tablename].join('.'); | ||
let query = this.db(fullname); | ||
(args) && this.addWhereFromArgs(tablename, query, args); | ||
(args) && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
args && this.addWhereFromArgs(tablename, query, args); | ||
args && this.addWhereFromArgsWhere(query, args); | ||
if (args._debug) | ||
console.log('db hit:', query.toSQL().sql, query.toSQL().bindings); | ||
return await query.first(); | ||
@@ -151,5 +156,5 @@ } | ||
* Insert or update record | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
*/ | ||
@@ -162,4 +167,5 @@ async putItem(tablename, data) { | ||
// Check exists | ||
let count = [{"count": "0"}] | ||
if (data.input[pk]) count = await this.db(fullname).where(pk, data.input[pk]).count() | ||
let count = [{ count: '0' }]; | ||
if (data.input[pk]) | ||
count = await this.db(fullname).where(pk, data.input[pk]).count(); | ||
@@ -171,8 +177,10 @@ // Insert or update | ||
result = await query.insert(data.input); | ||
if (data._debug) console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db insert:', query.toSQL().sql, query.toSQL().bindings); | ||
} else { | ||
let query = this.db(fullname) | ||
query.where(pk, data.input[pk]) | ||
let query = this.db(fullname); | ||
query.where(pk, data.input[pk]); | ||
result = await query.update(data.input); | ||
if (data._debug) console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
if (data._debug) | ||
console.log('db update:', query.toSQL().sql, query.toSQL().bindings); | ||
} | ||
@@ -184,5 +192,5 @@ return result; | ||
* Load knex query with condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} condition | ||
*/ | ||
@@ -194,6 +202,6 @@ convertConditionToWhereClause(query, condition) { | ||
//let fullname = [this.schemaname, column].join('.'); | ||
switch(op) { | ||
switch (op) { | ||
case '~': | ||
op = 'ilike'; | ||
value = '%'+value.replace(' ', '%')+'%'; | ||
value = '%' + value.replace(' ', '%') + '%'; | ||
query.where(column, 'ilike', value); | ||
@@ -214,8 +222,7 @@ break; | ||
* Load knex query with all filters | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgs(tablename, query, args) { | ||
// Validate filter arguments | ||
@@ -234,8 +241,7 @@ if (!args.filter) return; | ||
* Load knex query with where condition | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addWhereFromArgsWhere(query, args) { | ||
// Validate filter arguments | ||
@@ -251,8 +257,7 @@ if (!args.where) return; | ||
* Load knex query with all pagination | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
* | ||
* @param {Function} query | ||
* @param {Object} args | ||
*/ | ||
addPaginationFromArgs(tablename, query, args) { | ||
// Validate pagination arguments | ||
@@ -267,3 +272,3 @@ if (!args.pagination) return; | ||
let value = pagination[i][1]; | ||
switch(op) { | ||
switch (op) { | ||
case 'limit': | ||
@@ -276,3 +281,3 @@ query.limit(value); | ||
case 'orderby': | ||
value = value.split(' ') | ||
value = value.split(' '); | ||
query.orderBy(value[0], value[1]); | ||
@@ -286,5 +291,5 @@ break; | ||
* Run a raw SQL query | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
* | ||
* @param {String} sql | ||
* @param {Object} params | ||
*/ | ||
@@ -297,7 +302,7 @@ async query(sql, params) { | ||
* Generate cache key | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {String} columnname | ||
* @param {Array} ids | ||
* @param {Object} args | ||
*/ | ||
@@ -307,3 +312,4 @@ getCacheKey(tablename, columnname, ids, args) { | ||
const filteredArgs = { filter: args.filter, pagination: args.pagination }; | ||
let key = fullname + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
let key = | ||
fullname + columnname + ids.join(',') + JSON.stringify(filteredArgs); | ||
return String(hash(key)); | ||
@@ -315,4 +321,4 @@ } | ||
* database table names | ||
* | ||
* @param {Array} exclude | ||
* | ||
* @param {Array} exclude | ||
*/ | ||
@@ -322,3 +328,3 @@ getExcludeCondition(exclude) { | ||
if (!exclude || exclude.length === 0) return sql; | ||
const placeholders = exclude.map(v => '?').join(','); | ||
const placeholders = exclude.map((v) => '?').join(','); | ||
sql += `AND table_name NOT IN (${placeholders})`; | ||
@@ -330,5 +336,5 @@ return sql; | ||
* Get database tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
@@ -351,3 +357,3 @@ async getTables(schemaname, exclude = []) { | ||
} | ||
return tables; | ||
return tables; | ||
} | ||
@@ -358,5 +364,5 @@ | ||
* and create an object representation | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -384,5 +390,5 @@ async getColumns(schemaname, tablename) { | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -414,3 +420,3 @@ async getForeignKeys(schemaname, tablename) { | ||
for (let j = 0; j < res.rows.length; j++) { | ||
fkeys.push(res.rows[j]); | ||
fkeys.push(res.rows[j]); | ||
} | ||
@@ -423,5 +429,5 @@ return fkeys; | ||
* for a database table | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
* | ||
* @param {String} schemaname | ||
* @param {String} tablename | ||
*/ | ||
@@ -457,7 +463,9 @@ async getPrimaryKey(schemaname, tablename) { | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
getTableColumnsFromSchema(tablename) { | ||
return Object.keys(this.dbSchema[tablename]).filter(c => c !== '__reverse' && c !== '__pk'); | ||
return Object.keys(this.dbSchema[tablename]).filter( | ||
(c) => c !== '__reverse' && c !== '__pk' | ||
); | ||
} | ||
@@ -468,4 +476,4 @@ | ||
* from current database schema | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -479,11 +487,10 @@ getPrimaryKeyFromSchema(tablename) { | ||
* Use exclude parameter to exclude indesired tables | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
* | ||
* @param {String} schemaname | ||
* @param {Array} exclude | ||
*/ | ||
async getSchema(schemaname = 'public', exclude = []) { | ||
let dbSchema = {}; | ||
this.schemaname = schemaname; | ||
// Get tables | ||
@@ -502,3 +509,3 @@ let tables = await this.getTables(schemaname, exclude); | ||
for (let j = 0; j < columns.length; j++) { | ||
let columnname = columns[j].name; | ||
let columnname = columns[j].name; | ||
dbSchema[tablename][columnname] = columns[j]; | ||
@@ -512,3 +519,2 @@ } | ||
for (let j = 0; j < fkeys.length; j++) { | ||
// Assign foreign key definition to column | ||
@@ -537,2 +543,2 @@ dbSchema[tablename][fkeys[j].columnname]['__foreign'] = { | ||
module.exports = PostgreSQL; | ||
module.exports = PostgreSQL; |
151
src/db2g.js
@@ -9,3 +9,3 @@ const PostgreSQL = require('../src/adapters/postgres'); | ||
* Main facade interface for DB2Graphql. | ||
* | ||
* | ||
* <p> | ||
@@ -15,12 +15,11 @@ * DB2Graphql is a library to create a Graphql API | ||
* </p> | ||
* | ||
* | ||
*/ | ||
class DB2Graphql { | ||
/** | ||
* Creates a new DB2Graphql facade | ||
* | ||
* | ||
* <br> | ||
* <br>Usage example: | ||
* | ||
* | ||
* <pre> | ||
@@ -31,3 +30,3 @@ * import knex from 'knex' | ||
* </pre> | ||
* | ||
* | ||
* @param {String} name | ||
@@ -41,3 +40,3 @@ * @param {Object} [db=null] Knex database instance | ||
mssql: MSSql | ||
} | ||
}; | ||
this.connection = db; | ||
@@ -53,3 +52,3 @@ this.dbSchema = null; | ||
// Add initial resolver | ||
if (name) this.add("Query", "getAPIName", "String", () => name); | ||
if (name) this.add('Query', 'getAPIName', 'String', () => name); | ||
} | ||
@@ -60,9 +59,9 @@ | ||
* If it does not exists, it gets created. | ||
* | ||
* | ||
* <p>Usage example:</p> | ||
* | ||
* | ||
* <pre> | ||
* addField('Users.fullname', 'String', (user) => user.firstname + user.lastname) | ||
* </pre> | ||
* | ||
* | ||
* @access public | ||
@@ -73,8 +72,12 @@ * @param {String} path The field path of the resolver ie. Query.getUser | ||
* @param {Object} [params={}] The query arguments | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
*/ | ||
addField(path, returns, resolver, params = {}) { | ||
let segments = path.trim().split('.').filter(i => !!i); | ||
if (segments.length < 2) throw new Error('addField path must be in format Type.field'); | ||
let segments = path | ||
.trim() | ||
.split('.') | ||
.filter((i) => !!i); | ||
if (segments.length < 2) | ||
throw new Error('addField path must be in format Type.field'); | ||
let field = segments.pop(); | ||
@@ -89,9 +92,9 @@ let type = segments.pop(); | ||
* If the field does not exists, it gets created. | ||
* | ||
* | ||
* <p>Usage example:</p> | ||
* | ||
* | ||
* <pre> | ||
* add('Users', 'fullname', 'String', (user) => user.firstname + user.lastname) | ||
* </pre> | ||
* | ||
* | ||
* @access public | ||
@@ -103,4 +106,4 @@ * @param {String} type The root type name ie. Query | ||
* @param {Object} [params={}] The query arguments | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
*/ | ||
@@ -116,18 +119,22 @@ add(type, field, returns, resolver, params = {}) { | ||
* If the field does not exists, it gets created. | ||
* | ||
* | ||
* <p>Usage example:</p> | ||
* | ||
* | ||
* <pre> | ||
* addInput('InputUsers.username', 'String') | ||
* </pre> | ||
* | ||
* | ||
* @access public | ||
* @param {String} path The input type name and field name ie. InputUsers.username | ||
* @param {String|Array} subType The Graphql returning type ie. Boolean or 'String' | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
*/ | ||
addInput(path, subType) { | ||
let segments = path.trim().split('.').filter(i => !!i); | ||
if (segments.length < 2) throw new Error('addInput path must be in format Input.field'); | ||
let segments = path | ||
.trim() | ||
.split('.') | ||
.filter((i) => !!i); | ||
if (segments.length < 2) | ||
throw new Error('addInput path must be in format Input.field'); | ||
let field = segments.pop(); | ||
@@ -141,3 +148,3 @@ let type = segments.pop(); | ||
* Set before hook | ||
* | ||
* | ||
* <pre> | ||
@@ -147,12 +154,12 @@ * function validator(type, field, parent, args, context) | ||
* </pre> | ||
* | ||
* | ||
* Where: | ||
* <pre> | ||
* - type: Graphql type | ||
* - field: Graphql field | ||
* - parent: parent resolved | ||
* - args: request arguments | ||
* - context: context information | ||
* - type: Graphql type | ||
* - field: Graphql field | ||
* - parent: parent resolved | ||
* - args: request arguments | ||
* - context: context information | ||
* </pre> | ||
* | ||
* | ||
* @param {Function} validator The validator callback. Must return true/false | ||
@@ -162,3 +169,3 @@ * @param {Function} rejected The rejected callback. Must return resolver type/null | ||
onBefore(validator, rejected) { | ||
this.resolver.beforeHook.validator = validator | ||
this.resolver.beforeHook.validator = validator; | ||
if (rejected) this.resolver.beforeHook.rejected = rejected; | ||
@@ -169,6 +176,6 @@ } | ||
* Connects to the database and builds the database schema | ||
* | ||
* | ||
* @access public | ||
* @param {String} [connect="public"] The database namespace (if suported) | ||
* | ||
* | ||
* @returns {Promise} The self instance for fluent interface | ||
@@ -195,12 +202,12 @@ */ | ||
* Returns a new database schema as object. | ||
* | ||
* | ||
* <br> | ||
* Lazy loading. | ||
* | ||
* | ||
* <p> | ||
* Passing refresh will rebuild the database schema | ||
* </p> | ||
* | ||
* | ||
* @param {Boolean} [refresh=false] Reconnects to database and rebuilds the database schema | ||
* | ||
* | ||
* @returns {Promise} The self instance for fluent interface | ||
@@ -222,8 +229,8 @@ */ | ||
* Returns the Graphql schema (string) | ||
* | ||
* | ||
* <br> | ||
* Lazy loading | ||
* | ||
* | ||
* @param {Boolean} refresh Allows to reconnect to database and rebuild database schema | ||
* | ||
* | ||
* @returns {String} The Graphql schema as string | ||
@@ -240,11 +247,10 @@ */ | ||
* Adds the schema builder queries | ||
* | ||
* | ||
* @access public | ||
* | ||
* | ||
* @todo Move to its own repository as a plugin | ||
* | ||
* | ||
* @returns {DB2Graphql} The self instance for fluent interface | ||
*/ | ||
withBuilder() { | ||
let resolver; | ||
@@ -256,3 +262,3 @@ | ||
}; | ||
this.add("Query", "getSchema", "String", resolver); | ||
this.add('Query', 'getSchema', 'String', resolver); | ||
@@ -265,13 +271,14 @@ // Add addSchemaColumn | ||
try { | ||
await db.schema.table(args.tablename, table => { | ||
await db.schema.table(args.tablename, (table) => { | ||
table[args.type](args.columnname).defaultTo(args.default || null); | ||
if (args.unique) table.unique(args.columnname); | ||
if (args.index) table.index(args.columnname); | ||
if (args.foreign) table.foreign(args.columnname).references(args.foreign) | ||
if (args.foreign) | ||
table.foreign(args.columnname).references(args.foreign); | ||
}); | ||
return true; | ||
} catch(err) { | ||
} catch (err) { | ||
return false; | ||
} | ||
} | ||
}; | ||
const queryAlterColumnParams = { | ||
@@ -281,5 +288,11 @@ tablename: 'String!', | ||
type: 'String!', | ||
foreign: 'String', | ||
foreign: 'String' | ||
}; | ||
this.add('Query', 'addSchemaColumn', 'Boolean', resolver, queryAlterColumnParams); | ||
this.add( | ||
'Query', | ||
'addSchemaColumn', | ||
'Boolean', | ||
resolver, | ||
queryAlterColumnParams | ||
); | ||
@@ -290,3 +303,3 @@ // Add dropSchemaColumn | ||
try { | ||
await db.schema.table(args.tablename, table => { | ||
await db.schema.table(args.tablename, (table) => { | ||
table.dropColumn(args.columnname); | ||
@@ -303,3 +316,9 @@ }); | ||
}; | ||
this.add('Query', 'dropSchemaColumn', 'Boolean', resolver, queryDropColumnParams); | ||
this.add( | ||
'Query', | ||
'dropSchemaColumn', | ||
'Boolean', | ||
resolver, | ||
queryDropColumnParams | ||
); | ||
@@ -315,3 +334,3 @@ // Add addSchemaTable | ||
else table[args.type](args.primary).primary(); | ||
}) | ||
}); | ||
return true; | ||
@@ -328,3 +347,9 @@ } catch (err) { | ||
}; | ||
this.add('Query', 'addSchemaTable', 'Boolean', resolver, queryAddTableParams); | ||
this.add( | ||
'Query', | ||
'addSchemaTable', | ||
'Boolean', | ||
resolver, | ||
queryAddTableParams | ||
); | ||
@@ -341,3 +366,5 @@ // Add dropSchemaTable | ||
}; | ||
this.add('Query', 'dropSchemaTable', 'Boolean', resolver, { tablename: 'String!' }); | ||
this.add('Query', 'dropSchemaTable', 'Boolean', resolver, { | ||
tablename: 'String!' | ||
}); | ||
@@ -352,2 +379,2 @@ // Fluent interface | ||
exports.Resolver = Resolver; | ||
module.exports = DB2Graphql; | ||
module.exports = DB2Graphql; |
@@ -5,3 +5,3 @@ const utils = require('../utils/utils'); | ||
* Graphql compiler | ||
* | ||
* | ||
* Compiles to a Graphql schema string | ||
@@ -12,8 +12,7 @@ * from a database schema generated | ||
class Compiler { | ||
/** | ||
* Creates a new compiler instance | ||
* | ||
* @param {Object} dbSchema | ||
* @param {Function} dbDriver | ||
* | ||
* @param {Object} dbSchema | ||
* @param {Function} dbDriver | ||
*/ | ||
@@ -33,8 +32,8 @@ constructor(dbSchema, dbDriver) { | ||
* Build gql params string from an object | ||
* | ||
* @param {Object} obj | ||
* | ||
* @param {Object} obj | ||
*/ | ||
buildParamsFromObject(obj, join = ', ') { | ||
let params = []; | ||
Object.keys(obj).forEach(k => params.push(k + ': ' + obj[k])); | ||
Object.keys(obj).forEach((k) => params.push(k + ': ' + obj[k])); | ||
return params.length ? '(' + params.join(join) + ')' : ''; | ||
@@ -45,6 +44,6 @@ } | ||
* Builds a gql string for query/mutation | ||
* | ||
* @param {String} name | ||
* @param {String} returns | ||
* @param {Object} params | ||
* | ||
* @param {String} name | ||
* @param {String} returns | ||
* @param {Object} params | ||
*/ | ||
@@ -60,4 +59,4 @@ buildQuery(name, returns, params) { | ||
* from a database table | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -67,12 +66,15 @@ mapDbTableToGraphqlType(tablename) { | ||
if (!this.schema.type[field]) this.schema.type[field] = {}; | ||
// Add fields | ||
let columns = this.dbDriver.getTableColumnsFromSchema(tablename); | ||
columns.forEach(child => { | ||
columns.forEach((child) => { | ||
try { | ||
this.schema.type[field][child] = { | ||
name: child, | ||
type: this.dbDriver.mapDbColumnToGraphqlType(child, this.dbSchema[tablename][child]), | ||
type: this.dbDriver.mapDbColumnToGraphqlType( | ||
child, | ||
this.dbSchema[tablename][child] | ||
), | ||
params: {} | ||
} | ||
}; | ||
} catch (err) {} | ||
@@ -82,3 +84,3 @@ }); | ||
// Add foreign relations | ||
columns.map(c => { | ||
columns.map((c) => { | ||
let column = this.dbSchema[tablename][c]; | ||
@@ -91,8 +93,8 @@ if (column.__foreign) { | ||
params: {} | ||
} | ||
}; | ||
} | ||
}) | ||
}); | ||
// Add reverse relation | ||
this.dbSchema[tablename].__reverse.map(r => { | ||
this.dbSchema[tablename].__reverse.map((r) => { | ||
let child = r.ftablename; | ||
@@ -109,3 +111,3 @@ this.schema.type[field][child] = { | ||
} | ||
} | ||
}; | ||
}); | ||
@@ -124,3 +126,3 @@ | ||
} | ||
} | ||
}; | ||
} | ||
@@ -131,8 +133,14 @@ | ||
* for paginated results | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
mapDbTableToGraphqlQuery(tablename) { | ||
const field = utils.toCamelCase(tablename); | ||
const params = { filter: 'String', pagination: 'String', where: 'Condition', _debug: 'Boolean', _cache: 'Boolean' }; | ||
const params = { | ||
filter: 'String', | ||
pagination: 'String', | ||
where: 'Condition', | ||
_debug: 'Boolean', | ||
_cache: 'Boolean' | ||
}; | ||
if (!this.schema.type.Query) this.schema.type['Query'] = {}; | ||
@@ -143,3 +151,3 @@ this.schema.type.Query['getPage' + field] = { | ||
params | ||
} | ||
}; | ||
} | ||
@@ -150,11 +158,17 @@ | ||
* to get only one record from database. | ||
* | ||
* | ||
* Uses a simple filter that can be used | ||
* on Unique columns | ||
* | ||
* @param {String} tablename | ||
* on Unique columns | ||
* | ||
* @param {String} tablename | ||
*/ | ||
mapDbTableToGraphqlFirstOf(tablename) { | ||
const field = utils.toCamelCase(tablename); | ||
const params = { filter: 'String', pagination: 'String', where: 'Condition', _debug: 'Boolean', _cache: 'Boolean' }; | ||
const params = { | ||
filter: 'String', | ||
pagination: 'String', | ||
where: 'Condition', | ||
_debug: 'Boolean', | ||
_cache: 'Boolean' | ||
}; | ||
if (!this.schema.type.Query) this.schema.type['Query'] = {}; | ||
@@ -165,3 +179,3 @@ this.schema.type.Query['getFirst' + field] = { | ||
params | ||
} | ||
}; | ||
} | ||
@@ -171,4 +185,4 @@ | ||
* Generate input name from tablename | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -183,4 +197,4 @@ getInputName(tablename) { | ||
* Create a convenient input to be used in mutation | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -191,5 +205,8 @@ mapDbTableToGraphqlInput(tablename) { | ||
let columns = this.dbDriver.getTableColumnsFromSchema(tablename); | ||
columns.forEach(col => { | ||
columns.forEach((col) => { | ||
try { | ||
this.schema.input[name][col] = this.dbDriver.mapDbColumnToGraphqlType(col, this.dbSchema[tablename][col]); | ||
this.schema.input[name][col] = this.dbDriver.mapDbColumnToGraphqlType( | ||
col, | ||
this.dbSchema[tablename][col] | ||
); | ||
} catch (err) {} | ||
@@ -202,7 +219,7 @@ }); | ||
* to store a single record into the database | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
mapDbTableToGraphqlMutation(tablename) { | ||
const field = utils.toCamelCase(tablename) | ||
const field = utils.toCamelCase(tablename); | ||
if (!this.schema.type.Mutation) this.schema.type['Mutation'] = {}; | ||
@@ -220,4 +237,4 @@ let name = this.getInputName(tablename); | ||
* Adds a Graphql field in types | ||
* | ||
* @param {String} field | ||
* | ||
* @param {String} field | ||
*/ | ||
@@ -235,6 +252,6 @@ addType(type, field, returns, params) { | ||
* Add input field | ||
* | ||
* @param {String} input | ||
* @param {String} field | ||
* @param {String} subType | ||
* | ||
* @param {String} input | ||
* @param {String} field | ||
* @param {String} subType | ||
*/ | ||
@@ -274,8 +291,14 @@ addInput(input, field, subType) { | ||
let subfields = []; | ||
Object.keys(this.schema.type[field]).map(key => { | ||
Object.keys(this.schema.type[field]).map((key) => { | ||
let f = this.schema.type[field][key]; | ||
let type = Array.isArray(f.type) ? '[' + f.type[0] + ']' : f.type; | ||
subfields.push(" " + f.name + this.buildParamsFromObject(f.params || {}) + ": " + type); | ||
subfields.push( | ||
' ' + | ||
f.name + | ||
this.buildParamsFromObject(f.params || {}) + | ||
': ' + | ||
type | ||
); | ||
}); | ||
items.push("type " + field + " {\n" + subfields.join("\n") + "\n}"); | ||
items.push('type ' + field + ' {\n' + subfields.join('\n') + '\n}'); | ||
} | ||
@@ -286,16 +309,17 @@ | ||
let subfields = []; | ||
Object.keys(this.schema.input[field]).map(key => { | ||
subfields.push(" " + key + ": " + this.schema.input[field][key]); | ||
Object.keys(this.schema.input[field]).map((key) => { | ||
subfields.push(' ' + key + ': ' + this.schema.input[field][key]); | ||
}); | ||
items.push("input " + field + " {\n" + subfields.join("\n") + "\n}"); | ||
items.push('input ' + field + ' {\n' + subfields.join('\n') + '\n}'); | ||
} | ||
this.sdl = items.join("\n\n"); | ||
this.sdl = items.join('\n\n'); | ||
// Add condition type | ||
if (items.length) { | ||
this.sdl += "\n\ninput Condition {\n sql: String!\n val: [String!]!\n}"; | ||
this.sdl += | ||
'\n\ninput Condition {\n sql: String!\n val: [String!]!\n}'; | ||
} | ||
this.sdl += "\n"; | ||
this.sdl += '\n'; | ||
} | ||
@@ -306,2 +330,2 @@ return this.sdl.trim(); | ||
module.exports = Compiler; | ||
module.exports = Compiler; |
@@ -5,3 +5,3 @@ const utils = require('../utils/utils'); | ||
* Graphql resolver | ||
* | ||
* | ||
* By using a database schema and driver, | ||
@@ -11,3 +11,3 @@ * implements a convenient API | ||
* for the most common operations | ||
* | ||
* | ||
* It is not intended to perform exotic | ||
@@ -19,7 +19,6 @@ * database queries. For that case, | ||
class Resolver { | ||
/** | ||
* Creates a new Resolver instance | ||
* | ||
* @param {Function} dbDriver | ||
* | ||
* @param {Function} dbDriver | ||
*/ | ||
@@ -30,3 +29,3 @@ constructor(dbDriver) { | ||
// Holds resolvers object | ||
this.resolvers = {} | ||
this.resolvers = {}; | ||
@@ -37,3 +36,3 @@ // Default before hook | ||
rejected: async () => null | ||
} | ||
}; | ||
} | ||
@@ -45,5 +44,5 @@ | ||
* a page of records | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
@@ -61,5 +60,5 @@ async getPage(tablename, parent, args, context) { | ||
* only one record | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
*/ | ||
@@ -76,4 +75,4 @@ async getFirstOf(tablename, parent, args, context) { | ||
* a single record onto the database | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
* @param {Object} data | ||
@@ -90,3 +89,3 @@ */ | ||
// Retrieve updated item | ||
const args = { filter: { [tablename]: [['=', pk, id]] }}; | ||
const args = { filter: { [tablename]: [['=', pk, id]] } }; | ||
return await this.dbDriver.firstOf(tablename, args); | ||
@@ -102,4 +101,4 @@ } | ||
* these conditions. | ||
* | ||
* @param {String} filterExpr | ||
* | ||
* @param {String} filterExpr | ||
*/ | ||
@@ -110,4 +109,4 @@ parseFilterExpression(filterExpr, tablename) { | ||
filter[tablename.trim()] = []; | ||
where.split(';').map(f1 => { | ||
let op = /\<\=\>|>=|<=|=|>|<|~|\#/.exec(f1); | ||
where.split(';').map((f1) => { | ||
let op = /<=>|>=|<=|=|>|<|~|#/.exec(f1); | ||
if (!op) throw new Error('Filter operation not suported in: ' + f1); | ||
@@ -117,3 +116,3 @@ op = op[0].trim(); | ||
condition.unshift(op); | ||
condition = condition.map(c => c.trim()) | ||
condition = condition.map((c) => c.trim()); | ||
filter[tablename].push(condition); | ||
@@ -126,4 +125,4 @@ }); | ||
* Parse pagination expression | ||
* | ||
* @param {String} expression | ||
* | ||
* @param {String} expression | ||
*/ | ||
@@ -134,5 +133,5 @@ parsePaginationExpression(expression, tablename) { | ||
pagination[tablename.trim()] = []; | ||
pagExpr.split(';').map(f1 => { | ||
pagExpr.split(';').map((f1) => { | ||
let params = f1.split('='); | ||
params = params.map(p => p.trim()) | ||
params = params.map((p) => p.trim()); | ||
pagination[tablename].push(params); | ||
@@ -145,10 +144,15 @@ }); | ||
* Parse args common | ||
* | ||
* | ||
* @param {String} tablename | ||
* @param {Object} args | ||
* @param {Object} args | ||
*/ | ||
parseArgsCommon(tablename, args) { | ||
let localArgs = Object.assign({}, args); | ||
if (args.filter) localArgs.filter = this.parseFilterExpression(args.filter, tablename); | ||
if (args.pagination) localArgs.pagination = this.parsePaginationExpression(args.pagination, tablename); | ||
if (args.filter) | ||
localArgs.filter = this.parseFilterExpression(args.filter, tablename); | ||
if (args.pagination) | ||
localArgs.pagination = this.parsePaginationExpression( | ||
args.pagination, | ||
tablename | ||
); | ||
return localArgs; | ||
@@ -159,16 +163,33 @@ } | ||
* Adds a Graphql resolver | ||
* | ||
* @param {String} namespace | ||
* @param {String} name | ||
* @param {Function} cb | ||
* | ||
* @param {String} namespace | ||
* @param {String} name | ||
* @param {Function} cb | ||
*/ | ||
add(namespace, name, cb) { | ||
if (!this.resolvers[namespace]) this.resolvers[namespace] = {}; | ||
this.resolvers[namespace][name] = async (root = null, args = {}, context = {}) => { | ||
this.resolvers[namespace][name] = async ( | ||
root = null, | ||
args = {}, | ||
context = {} | ||
) => { | ||
const db = this.dbDriver ? this.dbDriver.db : null; | ||
context.ioc = { resolver: this, db }; | ||
const passBefore = await this.beforeHook.validator(namespace, name, root, args, context); | ||
if (!passBefore) return await this.beforeHook.rejected(namespace, name, root, args, context); | ||
const passBefore = await this.beforeHook.validator( | ||
namespace, | ||
name, | ||
root, | ||
args, | ||
context | ||
); | ||
if (!passBefore) | ||
return await this.beforeHook.rejected( | ||
namespace, | ||
name, | ||
root, | ||
args, | ||
context | ||
); | ||
return await cb(root, args, context); | ||
} | ||
}; | ||
} | ||
@@ -178,5 +199,5 @@ | ||
* Create relation resolver for foreign key | ||
* | ||
* | ||
* @todo Refactor to smaller complexity | ||
* @param {String} tablename | ||
* @param {String} tablename | ||
*/ | ||
@@ -186,3 +207,3 @@ createForeignFieldsResolvers(tablename) { | ||
const columns = this.dbDriver.getTableColumnsFromSchema(tablename); | ||
columns.map(c => { | ||
columns.map((c) => { | ||
const column = this.dbDriver.dbSchema[tablename][c]; | ||
@@ -196,5 +217,9 @@ if (column.__foreign) { | ||
if (!item[column.name]) return null; | ||
args['filter'] = (args.filter ? args.filter + ';' : '') + fcolumnname + '#' + item[fcolumnname]; | ||
args['filter'] = | ||
(args.filter ? args.filter + ';' : '') + | ||
fcolumnname + | ||
'#' + | ||
item[column.name]; | ||
return await this.getFirstOf(ftablename, item, args, context); | ||
} | ||
}; | ||
} | ||
@@ -206,9 +231,9 @@ }); | ||
* Create inverse relation resolver | ||
* | ||
* | ||
* @todo Refactor to smaller complexity | ||
* @param {String} tablename | ||
* @param {String} tablename | ||
*/ | ||
createReverseRelationsResolvers(tablename) { | ||
const queryName = utils.toCamelCase(tablename); | ||
this.dbDriver.dbSchema[tablename].__reverse.map(r => { | ||
this.dbDriver.dbSchema[tablename].__reverse.map((r) => { | ||
let field = r.ftablename; | ||
@@ -218,5 +243,9 @@ const fcolumnname = r.fcolumnname; | ||
this.resolvers[queryName][field] = async (item, args, context) => { | ||
args['filter'] = (args.filter ? args.filter + ';' : '') + fcolumnname + '#' + item[r.columnname]; | ||
args['filter'] = | ||
(args.filter ? args.filter + ';' : '') + | ||
fcolumnname + | ||
'#' + | ||
item[r.columnname]; | ||
return await this.getPage(field, item, args, context); | ||
} | ||
}; | ||
}); | ||
@@ -227,4 +256,4 @@ } | ||
* Add default API resolvers | ||
* | ||
* @param {String} tablename | ||
* | ||
* @param {String} tablename | ||
*/ | ||
@@ -239,5 +268,9 @@ addDefaultFieldsResolvers(tablename) { | ||
}); | ||
this.add('Mutation', 'putItem' + typeName, async (parent, args, context) => { | ||
return this.putItem(tablename, parent, args, context); | ||
}); | ||
this.add( | ||
'Mutation', | ||
'putItem' + typeName, | ||
async (parent, args, context) => { | ||
return this.putItem(tablename, parent, args, context); | ||
} | ||
); | ||
} | ||
@@ -248,3 +281,3 @@ | ||
* by population with the current API methods | ||
* | ||
* | ||
* @param {Boolean} withDatabase | ||
@@ -260,3 +293,3 @@ */ | ||
let tablename = tables[i]; | ||
// Add default resolvers | ||
@@ -278,2 +311,2 @@ this.addDefaultFieldsResolvers(tablename); | ||
module.exports = Resolver; | ||
module.exports = Resolver; |
@@ -1,6 +0,5 @@ | ||
/** | ||
* Takes a string as an argument and capitalizes it | ||
* Converts first character to upper case. | ||
* | ||
* | ||
* Used maily by Graphql compiler to name | ||
@@ -11,9 +10,12 @@ * Types, Queries and Mutations. | ||
return string.charAt(0).toUpperCase() + string.slice(1); | ||
} | ||
}; | ||
const toCamelCase = (string) => { | ||
return string.split('_').map(i => capitalize(i)).join(''); | ||
} | ||
return string | ||
.split('_') | ||
.map((i) => capitalize(i)) | ||
.join(''); | ||
}; | ||
exports.capitalize = capitalize; | ||
exports.toCamelCase = toCamelCase; |
1518185
91
6008
12