Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: move wrap to be method _expose() #235

Merged
merged 2 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -21,72 +22,6 @@ const { validatePlugin } = require('./lib/validate-plugin')
const { isBundledOrTypescriptPlugin } = require('./lib/is-bundled-or-typescript-plugin')
const { isPromiseLike } = require('./lib/is-promise-like')

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 @@ -100,23 +35,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 @@ -245,7 +178,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 @@ -267,6 +200,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 @@ -488,7 +497,7 @@ function callWithCbOrNextTick (func, cb) {
process.nextTick(cb)
}
} else {
if (this._timeout === 0) {
if (this._opts.timeout === 0) {
const wrapCb = (err) => {
this._error = err
cb(this._error)
Expand All @@ -507,15 +516,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'))
})
})