- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 199
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(databse): add database and query builder
- v21.6.0
- v21.5.1
- v21.5.0
- v21.4.0
- v21.3.0
- v21.2.0
- v21.1.1
- v21.1.0
- v21.0.1
- v21.0.0
- v20.6.0
- v20.5.1
- v20.5.0
- v20.4.0
- v20.3.1
- v20.3.0
- v20.2.0
- v20.1.0
- v20.0.0
- v19.0.0
- v19.0.0-8
- v19.0.0-7
- v19.0.0-6
- v19.0.0-5
- v19.0.0-4
- v19.0.0-3
- v19.0.0-2
- v19.0.0-1
- v19.0.0-0
- v18.4.2
- v18.4.1
- v18.4.0
- v18.3.0
- v18.2.0
- v18.1.1
- v18.1.0
- v18.0.1
- v18.0.0
- v17.2.0
- v17.1.1
- v17.1.0
- v17.0.1
- v17.0.0
- v16.3.2
- v16.3.1
- v16.3.0
- v16.2.2
- v16.2.1
- v16.2.0
- v16.1.0
- v16.0.2
- v16.0.1
- v16.0.0
- v15.0.3
- v15.0.2
- v15.0.1
- v15.0.0
- v14.2.0
- v14.1.0
- v14.0.2
- v14.0.1
- v14.0.0
- v13.0.0
- v12.0.0
- v11.0.1
- v11.0.0
- v10.0.0
- v9.0.3
- v9.0.2
- v9.0.1
- v9.0.0
- v8.5.0
- v8.4.4
- v8.4.3
- v8.4.2
- v8.4.1
- v8.4.0
- v8.3.1
- v8.3.0
- v8.2.2
- v8.2.1
- v8.2.0
- v8.1.1
- v8.1.0
- v8.0.5
- v8.0.4
- v8.0.3
- v8.0.2
- v8.0.1
- v8.0.0
- v7.6.3
- v7.6.2
- v7.6.1
- v7.6.0
- v7.5.5
- v7.5.4
- v7.5.3
- v7.5.2
- v7.5.1
- v7.5.0
- v7.4.3
- v7.4.2
- v7.4.1
- v7.4.0
- v7.3.2
- v7.3.1
- v7.3.0
- v7.2.1
- v7.2.1-0
- v7.2.0-0
- v7.1.7-0
- v7.1.6-0
- v7.1.5-0
- v7.1.4-0
- v7.1.3-0
- v7.1.2-0
- v7.1.1-0
- v7.1.0-0
- v7.0.1-0
- v7.0.0-0
- v6.1.3
- v6.1.2
- v6.1.1
- v6.1.0
- v6.0.1
- v6.0.0
- v5.0.4
- v5.0.3
- v5.0.2
- v5.0.1
- v5.0.0
- v4.1.3
- v4.1.2
- v4.1.1
- v4.1.0
- v4.0.25
- v4.0.24
- v4.0.23
- v4.0.22
- v4.0.21
- v4.0.20
- v4.0.19
- v4.0.18
- v4.0.17
- v4.0.16
- v4.0.15
- v4.0.14
- v4.0.13
- v4.0.12
- v4.0.11
- v4.0.10
- v4.0.9
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
1 parent
6071e7c
commit 00f091d
Showing
15 changed files
with
3,825 additions
and
0 deletions.
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,13 @@ | ||
# editorconfig.org | ||
root = true | ||
|
||
[*] | ||
indent_size = 2 | ||
indent_style = space | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
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 @@ | ||
coverage | ||
node_modules | ||
.DS_Store | ||
npm-debug.log | ||
.idea | ||
out | ||
.nyc_output | ||
test/tmp |
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,12 @@ | ||
coverage | ||
node_modules | ||
.DS_Store | ||
npm-debug.log | ||
test | ||
.travis.yml | ||
.editorconfig | ||
benchmarks | ||
.idea | ||
bin | ||
out | ||
.nyc_output |
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,10 @@ | ||
language: node_js | ||
node_js: | ||
- node | ||
- 7.0.0 | ||
sudo: false | ||
install: | ||
- npm install | ||
notifications: | ||
slack: | ||
secure: m91zkX2cLVDRDMBAUnR1d+hbZqtSHXLkuPencHadhJ3C3wm53Box8U25co/goAmjnW5HNJ1SMSIg+DojtgDhqTbReSh5gSbU0uU8YaF8smbvmUv3b2Q8PRCA7f6hQiea+a8+jAb7BOvwh66dV4Al/1DJ2b4tCjPuVuxQ96Wll7Pnj1S7yW/Hb8fQlr9wc+INXUZOe8erFin+508r5h1L4Xv0N5ZmNw+Gqvn2kPJD8f/YBPpx0AeZdDssTL0IOcol1+cDtDzMw5PAkGnqwamtxhnsw+i8OW4avFt1GrRNlz3eci5Cb3NQGjHxJf+JIALvBeSqkOEFJIFGqwAXMctJ9q8/7XyXk7jVFUg5+0Z74HIkBwdtLwi/BTyXMZAgsnDjndmR9HsuBP7OSTJF5/V7HCJZAaO9shEgS8DwR78owv9Fr5er5m9IMI+EgSH3qtb8iuuQaPtflbk+cPD3nmYbDqmPwkSCXcXRfq3IxdcV9hkiaAw52AIqqhnAXJWZfL6+Ct32i2mtSaov9FYtp/G0xb4tjrUAsDUd/AGmMJNEBVoHtP7mKjrVQ35cEtFwJr/8SmZxGvOaJXPaLs43dhXKa2tAGl11wF02d+Rz1HhbOoq9pJvJuqkLAVvRdBHUJrB4/hnTta5B0W5pe3mIgLw3AmOpk+s/H4hAP4Hp0gOWlPA= |
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,9 @@ | ||
# Query Builder Features | ||
|
||
- [x] Paginate method | ||
- [x] forPage method | ||
- [ ] chunk ( removed ) | ||
- [ ] pluckAll ( removed ) | ||
- [x] withPrefix | ||
- [x] transactions | ||
- [x] global transactions |
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,22 @@ | ||
environment: | ||
matrix: | ||
- nodejs_version: 'Stable' | ||
- nodejs_version: '7' | ||
|
||
init: | ||
git config --global core.autocrlf true | ||
|
||
install: | ||
- ps: Install-Product node $env:nodejs_version | ||
- npm install | ||
|
||
test_script: | ||
- node --version | ||
- npm --version | ||
- npm run test:win | ||
|
||
build: off | ||
clone_depth: 1 | ||
|
||
matrix: | ||
fast_finish: true |
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 @@ | ||
'use strict' |
Large diffs are not rendered by default.
Oops, something went wrong.
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,35 @@ | ||
{ | ||
"name": "@adonisjs/lucid", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/adonisjs/adonis-lucid.git" | ||
}, | ||
"author": "", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/adonisjs/adonis-lucid/issues" | ||
}, | ||
"homepage": "https://github.com/adonisjs/adonis-lucid#readme", | ||
"dependencies": { | ||
"knex": "^0.13.0", | ||
"lodash": "^4.17.4", | ||
"node-exceptions": "^2.0.2" | ||
}, | ||
"devDependencies": { | ||
"@adonisjs/sink": "^1.0.8", | ||
"chance": "^1.0.9", | ||
"coveralls": "^2.13.1", | ||
"fs-extra": "^3.0.1", | ||
"japa": "^1.0.3", | ||
"japa-cli": "^1.0.1", | ||
"mysql": "^2.13.0", | ||
"nyc": "^11.0.2", | ||
"sqlite3": "^3.1.8" | ||
} | ||
} |
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,66 @@ | ||
'use strict' | ||
|
||
/* | ||
* adonis-lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
require('./MonkeyPatch') | ||
|
||
const Database = require('.') | ||
const CE = require('../Exceptions') | ||
|
||
const proxyHandler = { | ||
get (target, name) { | ||
if (target[name]) { | ||
return target[name] | ||
} | ||
|
||
const db = target.connection() | ||
if (typeof (db[name]) === 'function') { | ||
return db[name].bind(db) | ||
} | ||
return db[name] | ||
} | ||
} | ||
|
||
/** | ||
* DatabaseManager is a layer on top of {{crossLink "Database"}}{{/crossLink}} | ||
* class which manages a pool of different database connections and proxy | ||
* all Database methods, so that it's easier to work with them. | ||
*/ | ||
class DatabaseManager { | ||
constructor (Config) { | ||
this.Config = Config | ||
this._connectionPools = {} | ||
return new Proxy(this, proxyHandler) | ||
} | ||
|
||
/** | ||
* Creates a new/resuse and returns the database connection. | ||
* | ||
* @method connection | ||
* | ||
* @param {String} [name = Config.get('database.connection')] | ||
* | ||
* @return {Database} | ||
*/ | ||
connection (name = this.Config.get('database.connection')) { | ||
if (this._connectionPools[name]) { | ||
return this._connectionPools[name] | ||
} | ||
const connectionSettings = this.Config.get(`database.${name}`) | ||
if (!connectionSettings) { | ||
throw CE.RuntimeException.missingDatabaseConnection(name) | ||
} | ||
|
||
this._connectionPools[name] = new Database(connectionSettings) | ||
return this._connectionPools[name] | ||
} | ||
} | ||
|
||
module.exports = DatabaseManager |
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,69 @@ | ||
'use strict' | ||
|
||
/* | ||
* adonis-lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
/** | ||
* Here we monkey patch/extend knex query builder | ||
* prototype. | ||
*/ | ||
|
||
const _ = require('lodash') | ||
var KnexQueryBuilder = require('knex/lib/query/builder') | ||
const excludeAttrFromCount = ['order'] | ||
|
||
const _from = KnexQueryBuilder.prototype.from | ||
|
||
KnexQueryBuilder.prototype.from = function (name) { | ||
const prefix = _.get(this.client, 'config.prefix') | ||
name = prefix && !this._ignorePrefix ? `${prefix}${name}` : name | ||
return _from.bind(this)(name) | ||
} | ||
|
||
KnexQueryBuilder.prototype.table = function (...args) { | ||
return this.from(...args) | ||
} | ||
|
||
KnexQueryBuilder.prototype.into = function (...args) { | ||
return this.from(...args) | ||
} | ||
|
||
KnexQueryBuilder.prototype.withOutPrefix = function () { | ||
this._ignorePrefix = true | ||
return this | ||
} | ||
|
||
KnexQueryBuilder.prototype.forPage = function (page, perPage = 20) { | ||
const offset = page === 1 ? 0 : perPage * (page - 1) | ||
return this.offset(offset).limit(perPage) | ||
} | ||
|
||
KnexQueryBuilder.prototype.paginate = async function (page, perPage = 20) { | ||
const countByQuery = this.clone() | ||
|
||
/** | ||
* Remove statements that will make things bad with count | ||
* query, for example `orderBy` | ||
*/ | ||
countByQuery._statements = _.filter(countByQuery._statements, (statement) => { | ||
return excludeAttrFromCount.indexOf(statement.grouping) < 0 | ||
}) | ||
|
||
const counts = await countByQuery.count('* as total') | ||
const total = _.get(counts, '0.total', 0) | ||
const data = total === 0 ? [] : await this.forPage(page, perPage) | ||
|
||
return { | ||
total: total, | ||
perPage: perPage, | ||
page: page, | ||
lastPage: Math.ceil(total / perPage), | ||
data: data | ||
} | ||
} |
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,155 @@ | ||
'use strict' | ||
|
||
/* | ||
* adonis-lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
const knex = require('knex') | ||
const _ = require('lodash') | ||
|
||
const proxyHandler = { | ||
get (target, name) { | ||
if (typeof (target[name]) !== 'undefined') { | ||
return target[name] | ||
} | ||
|
||
const queryBuilder = target.query() | ||
if (typeof (queryBuilder[name]) !== 'function') { | ||
throw new Error(`Database.${name} is not a function`) | ||
} | ||
|
||
if (target._globalTrx) { | ||
queryBuilder.transacting(target._globalTrx) | ||
} | ||
return queryBuilder[name].bind(queryBuilder) | ||
} | ||
} | ||
|
||
/** | ||
* Database class instance is used to initiate database | ||
* queries and transactions. | ||
* | ||
* @class Database | ||
* @constructor | ||
*/ | ||
class Database { | ||
constructor (config) { | ||
if (config.client === 'sqlite') { | ||
config.useNullAsDefault = _.defaultTo(config.useNullAsDefault, true) | ||
} | ||
this.knex = knex(config) | ||
this._globalTrx = null | ||
return new Proxy(this, proxyHandler) | ||
} | ||
|
||
/** | ||
* Returns the schema builder | ||
* | ||
* @attribute schema | ||
* | ||
* @return {Object} | ||
*/ | ||
get schema () { | ||
return this.knex.schema | ||
} | ||
|
||
/** | ||
* Method to build raw queries | ||
* | ||
* @method raw | ||
* | ||
* @param {...Spread} args | ||
* | ||
* @return {String} | ||
*/ | ||
raw (...args) { | ||
return this.knex.raw(...args) | ||
} | ||
|
||
/** | ||
* Returns a trx object to be used for running queries | ||
* under transaction. | ||
* | ||
* @method beginTransaction | ||
* | ||
* @return {Promise} | ||
*/ | ||
beginTransaction () { | ||
return new Promise((resolve, reject) => { | ||
this | ||
.knex | ||
.transaction(function (trx) { | ||
resolve(trx) | ||
}).catch(() => {}) | ||
}) | ||
} | ||
|
||
/** | ||
* Starts a global transaction, where all query builder | ||
* methods will be part of transaction automatically. | ||
* | ||
* Note: You must not use it in real world apart from when | ||
* writing tests. | ||
* | ||
* @method beginGlobalTransaction | ||
* | ||
* @return {void} | ||
*/ | ||
async beginGlobalTransaction () { | ||
this._globalTrx = await this.beginTransaction() | ||
} | ||
|
||
/** | ||
* Rollbacks global transaction | ||
* | ||
* @method rollbackGlobalTransaction | ||
* | ||
* @return {void} | ||
*/ | ||
rollbackGlobalTransaction () { | ||
this._globalTrx.rollback() | ||
this._globalTrx = null | ||
} | ||
|
||
/** | ||
* Commits global transaction | ||
* | ||
* @method commitGlobalTransaction | ||
* | ||
* @return {void} | ||
*/ | ||
commitGlobalTransaction () { | ||
this._globalTrx.commit() | ||
this._globalTrx = null | ||
} | ||
|
||
/** | ||
* Return a new instance of query builder | ||
* | ||
* @method query | ||
* | ||
* @return {Object} | ||
*/ | ||
query () { | ||
return new this.knex.queryBuilder() | ||
} | ||
|
||
/** | ||
* Closes the database connection. No more queries | ||
* can be made after this | ||
* | ||
* @method close | ||
* | ||
* @return {Promise} | ||
*/ | ||
close () { | ||
return this.knex.destroy() | ||
} | ||
} | ||
|
||
module.exports = Database |
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,30 @@ | ||
'use strict' | ||
|
||
/* | ||
* adonis-lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
const NE = require('node-exceptions') | ||
|
||
class RuntimeException extends NE.RuntimeException { | ||
/** | ||
* This exception is raised when user is trying to use an | ||
* undefined database connection | ||
* | ||
* @method missingDatabaseConnection | ||
* | ||
* @param {String} name | ||
* | ||
* @return {Object} | ||
*/ | ||
static missingDatabaseConnection (name) { | ||
return new this(`Missing database connection {${name}}. Make sure you define it inside config/database.js file`, 500, 'E_MISSING_DB_CONNECTION') | ||
} | ||
} | ||
|
||
module.exports = { RuntimeException } |
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,243 @@ | ||
'use strict' | ||
|
||
/* | ||
* adonis-lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
const test = require('japa') | ||
const chance = require('chance').Chance() | ||
const _ = require('lodash') | ||
const fs = require('fs-extra') | ||
const path = require('path') | ||
const { Config } = require('@adonisjs/sink') | ||
const Database = require('../../src/Database') | ||
const DatabaseManager = require('../../src/Database/Manager') | ||
const helpers = require('./helpers') | ||
|
||
test.group('Database | QueryBuilder', (group) => { | ||
group.before(async () => { | ||
await fs.ensureDir(path.join(__dirname, './tmp')) | ||
this.database = new Database(helpers.getConfig()) | ||
await helpers.createTables(this.database) | ||
}) | ||
|
||
group.after(async () => { | ||
await helpers.dropTables(this.database) | ||
await fs.remove(path.join(__dirname, './tmp')) | ||
}) | ||
|
||
test('get instance of query builder', (assert) => { | ||
const queryBuilder = this.database.query() | ||
assert.property(queryBuilder, 'client') | ||
}) | ||
|
||
test('proxy query builder methods', (assert) => { | ||
const selectQuery = this.database.from('users').toSQL() | ||
assert.equal(selectQuery.sql, helpers.formatQuery('select * from "users"')) | ||
}) | ||
|
||
test('prefix table when defined inside config', (assert) => { | ||
const dbConfig = helpers.getConfig() | ||
dbConfig.prefix = 'my_' | ||
|
||
const selectQuery = new Database(dbConfig).from('users').toSQL() | ||
assert.equal(selectQuery.sql, helpers.formatQuery('select * from "my_users"')) | ||
}) | ||
|
||
test('ignore prefix at runtime', (assert) => { | ||
const dbConfig = helpers.getConfig() | ||
dbConfig.prefix = 'my_' | ||
|
||
const selectQuery = new Database(dbConfig).withOutPrefix().from('users').toSQL() | ||
assert.equal(selectQuery.sql, helpers.formatQuery('select * from "users"')) | ||
}) | ||
|
||
test('create a raw query', (assert) => { | ||
const selectQuery = this.database.raw(helpers.formatQuery('select * from "users"')) | ||
assert.equal(selectQuery.sql, helpers.formatQuery('select * from "users"')) | ||
}) | ||
|
||
test('create db transactions', async (assert) => { | ||
const trx = await this.database.beginTransaction() | ||
assert.isDefined(trx) | ||
trx.rollback() | ||
}) | ||
|
||
test('commit transactions', async (assert) => { | ||
const trx = await this.database.beginTransaction() | ||
await trx.table('users').insert({ username: 'virk' }) | ||
trx.commit() | ||
const firstUser = await this.database.table('users').first() | ||
assert.equal(firstUser.username, 'virk') | ||
await this.database.truncate('users') | ||
}) | ||
|
||
test('rollback transactions', async (assert) => { | ||
const trx = await this.database.beginTransaction() | ||
await trx.table('users').insert({ username: 'virk' }) | ||
trx.rollback() | ||
const users = await this.database.table('users') | ||
assert.lengthOf(users, 0) | ||
}) | ||
|
||
test('transaction should not collide with other queries', async (assert) => { | ||
const trx = await this.database.beginTransaction() | ||
setTimeout(() => { | ||
trx.rollback() | ||
}, 20) | ||
await this.database.table('users').insert({ username: 'virk' }) | ||
const users = await this.database.table('users') | ||
assert.lengthOf(users, 1) | ||
await this.database.truncate('users') | ||
}) | ||
|
||
test('create global transactions', async (assert) => { | ||
await this.database.beginGlobalTransaction() | ||
await this.database.table('users').insert({ username: 'virk' }) | ||
this.database.rollbackGlobalTransaction() | ||
const users = await this.database.table('users') | ||
assert.lengthOf(users, 0) | ||
}) | ||
|
||
test('commit global transactions', async (assert) => { | ||
await this.database.beginGlobalTransaction() | ||
await this.database.table('users').insert({ username: 'virk' }) | ||
this.database.commitGlobalTransaction() | ||
const users = await this.database.table('users') | ||
assert.lengthOf(users, 1) | ||
await this.database.truncate('users') | ||
}) | ||
|
||
test('destroy database connection', async (assert) => { | ||
await this.database.close() | ||
assert.plan(1) | ||
try { | ||
await this.database.table('users') | ||
} catch ({ message }) { | ||
assert.equal(message, 'Unable to acquire a connection') | ||
this.database = new Database(helpers.getConfig()) | ||
} | ||
}) | ||
|
||
test('add orderBy and limit clause using forPage method', async (assert) => { | ||
const query = this.database.table('users').forPage(1).toSQL() | ||
assert.equal(query.sql, helpers.formatQuery('select * from "users" limit ?')) | ||
assert.deepEqual(query.bindings, [20]) | ||
}) | ||
|
||
test('add orderBy and limit clause using forPage greater than 1', async (assert) => { | ||
const query = this.database.table('users').forPage(3).toSQL() | ||
assert.equal(query.sql, helpers.formatQuery('select * from "users" limit ? offset ?')) | ||
assert.deepEqual(query.bindings, [20, 40]) | ||
await this.database.table('users').truncate() | ||
}) | ||
|
||
test('paginate results', async (assert) => { | ||
const users = _.map(_.range(10), () => { | ||
return { username: chance.word() } | ||
}) | ||
await this.database.insert(users).into('users') | ||
const result = await this.database.table('users').orderBy('username').paginate(1, 5) | ||
assert.equal(result.perPage, 5) | ||
assert.equal(result.total, 10) | ||
assert.equal(result.page, 1) | ||
assert.equal(result.lastPage, 2) | ||
assert.isAtMost(result.data.length, result.perPage) | ||
await this.database.table('users').truncate() | ||
}) | ||
|
||
test('paginate results when records are less than perPage', async (assert) => { | ||
const users = _.map(_.range(4), () => { | ||
return { username: chance.word() } | ||
}) | ||
await this.database.insert(users).into('users') | ||
const result = await this.database.table('users').orderBy('username').paginate(1, 5) | ||
assert.equal(result.perPage, 5) | ||
assert.equal(result.total, 4) | ||
assert.equal(result.page, 1) | ||
assert.equal(result.lastPage, 1) | ||
assert.isAtMost(result.data.length, result.perPage) | ||
await this.database.table('users').truncate() | ||
}) | ||
|
||
test('paginate data inside transactions', async (assert) => { | ||
const trx = await this.database.beginTransaction() | ||
assert.equal(typeof(trx.table('users').paginate), 'function') | ||
trx.rollback() | ||
}) | ||
|
||
test('throw exception when proxy property is not a method', (assert) => { | ||
const fn = () => this.database.foo() | ||
assert.throw(fn, 'Database.foo is not a function') | ||
}) | ||
}) | ||
|
||
test.group('Database | Manager', () => { | ||
test('get instance of database using connection method', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: helpers.getConfig() | ||
}) | ||
const db = new DatabaseManager(config).connection() | ||
assert.instanceOf(db, Database) | ||
}) | ||
|
||
test('throw exception when unable to connect to database', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: {} | ||
}) | ||
const db = () => new DatabaseManager(config).connection() | ||
assert.throw(db, 'knex: Required configuration option \'client\' is missing') | ||
}) | ||
|
||
test('throw exception when connection does not exists', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: {} | ||
}) | ||
const db = () => new DatabaseManager(config).connection('foo') | ||
assert.throw(db, 'E_MISSING_DB_CONNECTION: Missing database connection {foo}. Make sure you define it inside config/database.js file') | ||
}) | ||
|
||
test('proxy database methods', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: helpers.getConfig() | ||
}) | ||
const query = new DatabaseManager(config).table('users').toSQL() | ||
assert.equal(query.sql, helpers.formatQuery('select * from "users"')) | ||
}) | ||
|
||
test('proxy database properties', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: helpers.getConfig() | ||
}) | ||
assert.isNull(new DatabaseManager(config)._globalTrx) | ||
}) | ||
|
||
test('reuse existing database connection', (assert) => { | ||
const config = new Config() | ||
config.set('database', { | ||
connection: 'testing', | ||
testing: helpers.getConfig() | ||
}) | ||
const dbManager = new DatabaseManager(config) | ||
dbManager.connection() | ||
assert.lengthOf(Object.keys(dbManager._connectionPools), 1) | ||
|
||
dbManager.connection() | ||
assert.lengthOf(Object.keys(dbManager._connectionPools), 1) | ||
}) | ||
}) |
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,55 @@ | ||
'use strict' | ||
|
||
const path = require('path') | ||
const _ = require('lodash') | ||
|
||
module.exports = { | ||
formatQuery (query, connection) { | ||
if (process.env.DB === 'sqlite') { | ||
return query | ||
} | ||
|
||
if (process.env.DB === 'mysql') { | ||
return query.replace(/"/g, '`') | ||
} | ||
}, | ||
|
||
getConfig () { | ||
if (process.env.DB === 'sqlite') { | ||
return _.cloneDeep({ | ||
client: 'sqlite', | ||
connection: { | ||
filename: path.join(__dirname, '../tmp/dev.sqlite3') | ||
} | ||
}) | ||
} | ||
|
||
if (process.env.DB === 'mysql') { | ||
return _.cloneDeep({ | ||
client: 'mysql', | ||
connection: { | ||
host: '127.0.0.1', | ||
user: 'root', | ||
password: '', | ||
database: 'testing_lucid' | ||
} | ||
}) | ||
} | ||
}, | ||
|
||
createTables (db) { | ||
return Promise.all([ | ||
db.schema.createTable('users', function (table) { | ||
table.increments() | ||
table.string('username') | ||
table.timestamps() | ||
}) | ||
]) | ||
}, | ||
|
||
dropTables (db) { | ||
return Promise.all([ | ||
db.schema.dropTable('users') | ||
]) | ||
} | ||
} |