Skip to content

Commit

Permalink
chore: move wrap to be method _expose() (#235)
Browse files Browse the repository at this point in the history
* chore: move wrap to be attribute _expose()

* chore: move wrap to be attribute _expose()
  • Loading branch information
Uzlopak authored Aug 15, 2023
1 parent ebb67df commit 9da3798
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 111 deletions.
169 changes: 89 additions & 80 deletions boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const {
AVV_ERR_EXPOSE_ALREADY_DEFINED,
AVV_ERR_CALLBACK_NOT_FN,
AVV_ERR_ROOT_PLG_BOOTED,
AVV_ERR_READY_TIMEOUT
AVV_ERR_READY_TIMEOUT,
AVV_ERR_ATTRIBUTE_ALREADY_DEFINED
} = require('./lib/errors')
const {
kAvvio,
Expand All @@ -22,72 +23,6 @@ const { isPromiseLike } = require('./lib/is-promise-like')
const { thenify } = require('./lib/thenify')
const { executeWithThenable } = require('./lib/executeWithThenable')

function wrap (server, opts, instance) {
const expose = opts.expose || {}
const useKey = expose.use || 'use'
const afterKey = expose.after || 'after'
const readyKey = expose.ready || 'ready'
const onCloseKey = expose.onClose || 'onClose'
const closeKey = expose.close || 'close'

if (server[useKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(useKey)
}

if (server[afterKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(afterKey)
}

if (server[readyKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(readyKey)
}

server[useKey] = function (fn, opts) {
instance.use(fn, opts)
return this
}

Object.defineProperty(server, 'then', { get: thenify.bind(instance) })
server[kAvvio] = true

server[afterKey] = function (func) {
if (typeof func !== 'function') {
return instance._loadRegistered()
}
instance.after(encapsulateThreeParam(func, this))
return this
}

server[readyKey] = function (func) {
if (func && typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(readyKey, typeof func)
}
return instance.ready(func ? encapsulateThreeParam(func, this) : undefined)
}

server[onCloseKey] = function (func) {
if (typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(onCloseKey, typeof func)
}
instance.onClose(encapsulateTwoParam(func, this))
return this
}

server[closeKey] = function (func) {
if (func && typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(closeKey, typeof func)
}

if (func) {
instance.close(encapsulateThreeParam(func, this))
return this
}

// this is a Promise
return instance.close()
}
}

function Boot (server, opts, done) {
if (typeof server === 'function' && arguments.length === 1) {
done = server
Expand All @@ -101,23 +36,21 @@ function Boot (server, opts, done) {
}

opts = opts || {}
opts.autostart = opts.autostart !== false
opts.timeout = Number(opts.timeout) || 0
opts.expose = opts.expose || {}

if (!new.target) {
return new Boot(server, opts, done)
}

if (server) {
wrap(server, opts, this)
}
this._server = server || this
this._opts = opts

if (opts.autostart !== false) {
opts.autostart = true
if (server) {
this._expose()
}

server = server || this

this._timeout = Number(opts.timeout) || 0
this._server = server
/**
* @type {Array<Plugin>}
*/
Expand Down Expand Up @@ -246,7 +179,7 @@ Boot.prototype._addPlugin = function (plugin, opts, isAfter) {
// we always add plugins to load at the current element
const current = this._current[0]

const obj = new Plugin(fastq(this, this._loadPluginNextTick, 1), plugin, opts, isAfter, this._timeout)
const obj = new Plugin(fastq(this, this._loadPluginNextTick, 1), plugin, opts, isAfter, this._opts.timeout)
obj.once('start', (serverName, funcName, time) => {
const nodeId = this.pluginTree.start(current.name, funcName, time)
obj.once('loaded', (serverName, funcName, time) => {
Expand All @@ -268,6 +201,82 @@ Boot.prototype._addPlugin = function (plugin, opts, isAfter) {
return obj
}

Boot.prototype._expose = function _expose () {
const instance = this
const server = instance._server
const {
use: useKey = 'use',
after: afterKey = 'after',
ready: readyKey = 'ready',
onClose: onCloseKey = 'onClose',
close: closeKey = 'close'
} = this._opts.expose

if (server[useKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(useKey, 'use')
}
server[useKey] = function (fn, opts) {
instance.use(fn, opts)
return this
}

if (server[afterKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(afterKey, 'after')
}
server[afterKey] = function (func) {
if (typeof func !== 'function') {
return instance._loadRegistered()
}
instance.after(encapsulateThreeParam(func, this))
return this
}

if (server[readyKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(readyKey, 'ready')
}
server[readyKey] = function (func) {
if (func && typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(readyKey, typeof func)
}
return instance.ready(func ? encapsulateThreeParam(func, this) : undefined)
}

if (server[onCloseKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(onCloseKey, 'onClose')
}
server[onCloseKey] = function (func) {
if (typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(onCloseKey, typeof func)
}
instance.onClose(encapsulateTwoParam(func, this))
return this
}

if (server[closeKey]) {
throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(closeKey, 'close')
}
server[closeKey] = function (func) {
if (func && typeof func !== 'function') {
throw new AVV_ERR_CALLBACK_NOT_FN(closeKey, typeof func)
}

if (func) {
instance.close(encapsulateThreeParam(func, this))
return this
}

// this is a Promise
return instance.close()
}

if (server.then) {
throw new AVV_ERR_ATTRIBUTE_ALREADY_DEFINED('then')
}
Object.defineProperty(server, 'then', { get: thenify.bind(instance) })

server[kAvvio] = true
}

Boot.prototype.after = function (func) {
if (!func) {
return this._loadRegistered()
Expand Down Expand Up @@ -448,7 +457,7 @@ function callWithCbOrNextTick (func, cb) {
} else if (func.length === 1) {
executeWithThenable(func, [err], cb)
} else {
if (this._timeout === 0) {
if (this._opts.timeout === 0) {
const wrapCb = (err) => {
this._error = err
cb(this._error)
Expand All @@ -467,15 +476,15 @@ function callWithCbOrNextTick (func, cb) {

function timeoutCall (func, rootErr, context, cb) {
const name = func.name
debug('setting up ready timeout', name, this._timeout)
debug('setting up ready timeout', name, this._opts.timeout)
let timer = setTimeout(() => {
debug('timed out', name)
timer = null
const toutErr = new AVV_ERR_READY_TIMEOUT(name)
toutErr.fn = func
this._error = toutErr
cb(toutErr)
}, this._timeout)
}, this._opts.timeout)

if (func.length === 2) {
func(rootErr, timeoutCb.bind(this))
Expand Down
6 changes: 5 additions & 1 deletion lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ const { createError } = require('@fastify/error')
module.exports = {
AVV_ERR_EXPOSE_ALREADY_DEFINED: createError(
'AVV_ERR_EXPOSE_ALREADY_DEFINED',
"'%s' () is already defined, specify an expose option"
"'%s' is already defined, specify an expose option for '%s'"
),
AVV_ERR_ATTRIBUTE_ALREADY_DEFINED: createError(
'AVV_ERR_ATTRIBUTE_ALREADY_DEFINED',
"'%s' is already defined"
),
AVV_ERR_CALLBACK_NOT_FN: createError(
'AVV_ERR_CALLBACK_NOT_FN',
Expand Down
30 changes: 0 additions & 30 deletions test/chainable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,36 +40,6 @@ test('chainable automatically binded', (t) => {
t.equal(readyResult, undefined)
})

;['use', 'after', 'ready'].forEach((key) => {
test('throws if ' + key + ' is already there', (t) => {
t.plan(1)

const app = {}
app[key] = () => {}

try {
boot(app)
t.fail('error must happen')
} catch (err) {
t.equal(err.message, `'${key}' () is already defined, specify an expose option`)
}
})

test('support expose for ' + key, (t) => {
const app = {}
app[key] = () => {}

const expose = {}
expose[key] = 'muahah'

boot(app, {
expose
})

t.end()
})
})

test('chainable standalone with server', (t) => {
t.plan(6)

Expand Down
1 change: 1 addition & 0 deletions test/errors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const errors = require('../lib/errors')
test('Correct codes of AvvioErrors', t => {
const testcases = [
'AVV_ERR_EXPOSE_ALREADY_DEFINED',
'AVV_ERR_ATTRIBUTE_ALREADY_DEFINED',
'AVV_ERR_CALLBACK_NOT_FN',
'AVV_ERR_PLUGIN_NOT_VALID',
'AVV_ERR_ROOT_PLG_BOOTED',
Expand Down
80 changes: 80 additions & 0 deletions test/expose.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use strict'

const { test } = require('tap')
const boot = require('..')
const { AVV_ERR_EXPOSE_ALREADY_DEFINED, AVV_ERR_ATTRIBUTE_ALREADY_DEFINED } = require('../lib/errors')
const { kAvvio } = require('../lib/symbols')

for (const key of ['use', 'after', 'ready', 'onClose', 'close']) {
test('throws if ' + key + ' is by default already there', (t) => {
t.plan(1)

const app = {}
app[key] = () => { }

t.throws(() => boot(app), new AVV_ERR_EXPOSE_ALREADY_DEFINED(key, key))
})

test('throws if ' + key + ' is already there', (t) => {
t.plan(1)

const app = {}
app['cust' + key] = () => { }

t.throws(() => boot(app, { expose: { [key]: 'cust' + key } }), new AVV_ERR_EXPOSE_ALREADY_DEFINED('cust' + key, key))
})

test('support expose for ' + key, (t) => {
const app = {}
app[key] = () => { }

const expose = {}
expose[key] = 'muahah'

boot(app, {
expose
})

t.end()
})
}

test('set the kAvvio to true on the server', (t) => {
t.plan(1)

const server = {}
boot(server)

t.ok(server[kAvvio])
})

test('.then()', t => {
t.plan(3)

t.test('.then() can not be overwritten', (t) => {
t.plan(1)

const server = {
then: () => {}
}
t.throws(() => boot(server), AVV_ERR_ATTRIBUTE_ALREADY_DEFINED('then'))
})

t.test('.then() is a function', (t) => {
t.plan(1)

const server = {}
boot(server)

t.type(server.then, 'function')
})

t.test('.then() can not be overwritten', (t) => {
t.plan(1)

const server = {}
boot(server)

t.throws(() => { server.then = 'invalid' }, TypeError('Cannot set property then of #<Object> which has only a getter'))
})
})

0 comments on commit 9da3798

Please sign in to comment.