diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3fec5c5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# [*.md] +# trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91f43bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# 0x +.__browserify_string_empty.js +profile-* + +# tap --cov +.nyc_output/ + +# JetBrains IntelliJ IDEA +.idea/ +*.iml + +# VS Code +.vscode/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..edb52e6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: node_js + +node_js: + - "9" + - "8" + - "6" + +script: + - npm run lint-ci + - npm run test-ci + +notifications: + email: + on_success: never + on_failure: always diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..b12101c --- /dev/null +++ b/Readme.md @@ -0,0 +1,44 @@ +# fastify-oracle + +This module provides access to an Oracle database connection pool via the +[oracledb](https://npm.im/oracledb) module. It decorates the [Fastify](https://fastify.io) +instance with an `oracle` property that is a connection pool instance. + +When the Fastify server is shutdown, this plugin invokes the `.close()` on the +connection pool. + +## Example + +```js +const fastify = require('fastify')() + +fastify.register(require('fastify-oracle'), { + pool: { + user: 'foo', + password: 'bar', + connectString: 'oracle.example.com:1521/foobar' + } +}) + +fastify.get('/db_data', async function (req, reply) { + const conn = await this.oracle.getConnection() + const results = await conn.execute('select 1 as foo from dual') + await conn.close() + return results +}) +``` + +## Options + +`fastify-oracle` requires an options object with at least one of the following +properties: + ++ `pool`: an `oracledb` [pool configuration object](https://github.com/oracle/node-oracledb/blob/33331413/doc/api.md#createpool) ++ `poolAlias`: the name of a pool alias that has already been configured. This +takes precedence over the `pool` option. ++ `client`: an instance of an `oracledb` connection pool. This takes precedence +over the `pool` and `poolAlias` options. + +## License + +[MIT License](http://jsumners.mit-license.org/) diff --git a/package.json b/package.json new file mode 100644 index 0000000..f149102 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "fastify-oracle", + "version": "1.0.0", + "description": "A plugin for Fastify to provide Oracle DB connections", + "main": "plugin.js", + "scripts": { + "test": "tap 'test/**/*.test.js'", + "test-ci": "tap --cov 'test/**/*.test.js'", + "lint": "standard | snazzy", + "lint-ci": "standard" + }, + "precommit": [ + "lint", + "test" + ], + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/jsumners/fastify-oracle.git" + }, + "keywords": [ + "fastify", + "oracle" + ], + "author": "James Sumners ", + "license": "MIT", + "bugs": { + "url": "https://github.com/jsumners/fastify-oracle/issues" + }, + "homepage": "https://github.com/jsumners/fastify-oracle#readme", + "devDependencies": { + "pre-commit": "^1.2.2", + "snazzy": "^7.0.0", + "standard": "^10.0.3", + "tap": "^11.0.1" + }, + "dependencies": { + "fastify-plugin": "^0.2.1", + "oracledb": "^2.0.15" + } +} diff --git a/plugin.js b/plugin.js new file mode 100644 index 0000000..b1da4e2 --- /dev/null +++ b/plugin.js @@ -0,0 +1,43 @@ +'use strict' + +const fp = require('fastify-plugin') +const oracledb = require('oracledb') + +function fastifyOracleDB (fastify, options, next) { + const close = function (fastify, done) { + fastify.oracle.close(done) + } + + if (options.client) { + if (oracledb.Pool.prototype.isPrototypeOf(options.client) === false) { + return next(Error('supplied client must be an instance of oracledb.pool')) + } + fastify.decorate('oracle', options.client) + fastify.addHook('onClose', close) + return next() + } + + if (options.poolAlias) { + const pool = oracledb.getPool(options.poolAlias) + if (!pool) return next('could not get default pool from oracledb instance') + fastify.decorate('oracle', pool) + fastify.addHook('onClose', close) + return next() + } + + if (!options.pool) { + return next(Error('must supply options.pool oracledb pool options')) + } + + oracledb.createPool(options.pool, (err, pool) => { + if (err) return next(err) + fastify.decorate('oracle', pool) + fastify.addHook('onClose', close) + next() + }) +} + +module.exports = fp(fastifyOracleDB, { + fastify: '>=0.40.0', + name: 'fastify-oracle' +}) diff --git a/test/client.test.js b/test/client.test.js new file mode 100644 index 0000000..b5cc6c8 --- /dev/null +++ b/test/client.test.js @@ -0,0 +1,60 @@ +'use strict' + +const test = require('tap').test +const oracledb = require('oracledb') +const plugin = require('../plugin') + +test('accepts singleton client', (t) => { + t.plan(4) + oracledb.createPool({ + user: 'SYSTEM', + password: 'oracle', + connectString: 'localhost/XE' + }, (err, pool) => { + if (err) t.threw(err) + const fastify = { + decorate (name, obj) { + t.is(name, 'oracle') + t.is(obj, pool) + }, + + addHook (name, fn) { + t.is(name, 'onClose') + t.match(fn, /fastify\.oracle\.close/) + } + } + + plugin(fastify, {client: pool}, (err) => { + if (err) t.threw(err) + pool.close() + }) + }) +}) + +test('retrieves a cached pool', (t) => { + t.plan(4) + oracledb.createPool({ + user: 'SYSTEM', + password: 'oracle', + connectString: 'localhost/XE', + poolAlias: 'foo' + }, (err, pool) => { + if (err) t.threw(err) + const fastify = { + decorate (name, obj) { + t.is(name, 'oracle') + t.is(obj, pool) + }, + + addHook (name, fn) { + t.is(name, 'onClose') + t.match(fn, /fastify\.oracle\.close/) + } + } + + plugin(fastify, {poolAlias: 'foo'}, (err) => { + if (err) t.threw(err) + pool.close() + }) + }) +}) diff --git a/test/pool.test.js b/test/pool.test.js new file mode 100644 index 0000000..3f9937f --- /dev/null +++ b/test/pool.test.js @@ -0,0 +1,44 @@ +'use strict' + +// Unless someone can provide an Oracle Docker image to test against, we'll +// just have to assume this works. + +// const test = require('tap').test +// const plugin = require('../plugin') + +// test('creates usable pool from config', (t) => { +// t.plan(6) + +// const fastify = { +// decorate (name, obj) { +// t.is(name, 'oracle') +// this[name] = obj +// }, + +// addHook (name, fn) { +// t.is(name, 'onClose') +// t.match(fn, /fastify\.oracle\.close/) +// } +// } + +// const opts = { +// user: 'SYSTEM', +// password: 'oracle', +// connectString: 'localhost/xe' +// } +// plugin(fastify, {pool: opts}, (err) => { +// if (err) t.threw(err) +// t.ok(fastify.oracle) +// fastify.oracle.getConnection() +// .then((conn) => { +// conn.execute('select 1 as foo from dual') +// .then((rows) => { +// t.is(rows.length, 1) +// t.is(rows[0].foo, 1) +// }) +// .then(() => conn.close()) +// .catch(t.threw) +// }) +// .catch(t.threw) +// }) +// })