Skip to content

Commit

Permalink
print-routes options and print-plugins (#637)
Browse files Browse the repository at this point in the history
* Add printRoutes flags

This adds flags for method, commonPrefix and includeHooks options in the printRoutes method.

Closes #635

* Add print-plugins command

It runs the printPlugins method and prints the results. No arguments.

Closes #636

* Fix flakey tests

Better flakey CI skip test logic

Add timeout

Try awaiting in the same async function?

Skip in CI

Re-enable in darwin

Return close promise

Fix tests
  • Loading branch information
bcomnes committed Jul 21, 2023
1 parent eaa2dc3 commit af2e457
Show file tree
Hide file tree
Showing 16 changed files with 321 additions and 27 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Fastify command line interface, available commands are:
* generate-swagger generate Swagger/OpenAPI schema for a project using @fastify/swagger
* readme generate a README.md for the plugin
* print-routes prints the representation of the internal radix tree used by the router, useful for debugging.
* print-plugins prints the representation of the internal plugin tree used by avvio, useful for debugging.
* version the current fastify-cli version
* help help about commands
Expand Down
12 changes: 8 additions & 4 deletions args.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ const DEFAULT_ARGUMENTS = {
pluginTimeout: 10 * 1000, // everything should load in 10 seconds
closeGraceDelay: 500,
lang: 'js',
standardlint: false
standardlint: false,
commonPrefix: false
}

module.exports = function parseArgs (args) {
Expand All @@ -27,8 +28,8 @@ module.exports = function parseArgs (args) {
'populate--': true
},
number: ['port', 'inspect-port', 'body-limit', 'plugin-timeout', 'close-grace-delay'],
string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'config'],
boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug', 'standardlint'],
string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'config', 'method'],
boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug', 'standardlint', 'common-prefix', 'include-hooks'],
envPrefix: 'FASTIFY_',
alias: {
port: ['p'],
Expand Down Expand Up @@ -87,6 +88,9 @@ module.exports = function parseArgs (args) {
require: parsedArgs.require,
prefix: parsedArgs.prefix,
loggingModule: parsedArgs.loggingModule,
lang: parsedArgs.lang
lang: parsedArgs.lang,
method: parsedArgs.method,
commonPrefix: parsedArgs.commonPrefix,
includeHooks: parsedArgs.includeHooks
}
}
2 changes: 2 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const generatePlugin = require('./generate-plugin')
const generateSwagger = require('./generate-swagger')
const generateReadme = require('./generate-readme')
const printRoutes = require('./print-routes')
const printPlugins = require('./print-plugins')
commist.register('start', start.cli)
commist.register('eject', eject.cli)
commist.register('generate', generate.cli)
Expand All @@ -27,6 +28,7 @@ commist.register('version', function () {
console.log(require('./package.json').version)
})
commist.register('print-routes', printRoutes.cli)
commist.register('print-plugins', printPlugins.cli)

if (argv.help) {
const command = argv._.splice(2)[0]
Expand Down
14 changes: 14 additions & 0 deletions examples/plugin-common-prefix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict'

module.exports = function (fastify, options, next) {
fastify.decorate('test', true)
fastify.get('/hello-world', function (req, reply) {
req.log.trace('trace')
req.log.debug('debug')
reply.send({ hello: 'world' })
})
fastify.post('/help', function (req, reply) {
reply.send({ hello: 'world' })
})
next()
}
1 change: 1 addition & 0 deletions help/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Fastify command line interface available commands are:
* generate-swagger generate Swagger/OpenAPI schema for a project using @fastify/swagger
* readme generate a README.md for the plugin
* print-routes prints the representation of the internal radix tree used by the router, useful for debugging.
* print-plugins prints the representation of the internal plugin tree used by avvio, useful for debugging.
* version the current fastify-cli version
* help help about commands

Expand Down
1 change: 1 addition & 0 deletions help/print-plugins.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Usage: fastify print-plugins <file>
11 changes: 11 additions & 0 deletions help/print-routes.txt
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
Usage: fastify print-routes <file>

OPTS

--method <METHOD>
print debugging safe internal router tree for a given method

--common-prefix
print uncompressed radix tree

--include-hooks
display all properties from the route.store object for each displayed route
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"lint": "standard",
"lint:fix": "standard --fix",
"unit:template-ts-esm": " cross-env TS_NODE_PROJECT=./templates/app-ts-esm/tsconfig.json tap templates/app-ts-esm/test/**/*.test.ts --no-coverage --node-arg=--loader=ts-node/esm --timeout 400 --jobs 1 --color -R specy",
"unit:cli": "tap \"test/**/*.test.{js,ts}\" --no-coverage --jobs=1 --timeout 400 --jobs 1 --color -R specy",
"unit:cli": "tap \"test/**/*.test.{js,ts}\" --no-coverage --timeout 400 --jobs 1 --color -R specy",
"unit:templates-without-ts-esm": "tap \"templates/app/**/*.test.js\" \"templates/app-esm/**/*.test.js\" \"templates/app-ts/**/*.test.ts\" --no-coverage --timeout 400 --jobs 1 --color -R specy",
"pretest": "xcopy /e /k /i . \"..\\node_modules\\fastify-cli\" || rsync -r --exclude=node_modules ./ node_modules/fastify-cli || echo 'this is fine'",
"test-no-coverage": "npm run unit:cli && npm run unit:templates-without-ts-esm && npm run unit:template-ts-esm && npm run test:typescript",
Expand Down
82 changes: 82 additions & 0 deletions print-plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#! /usr/bin/env node

'use strict'

const parseArgs = require('./args')
const log = require('./log')
const {
exit,
requireFastifyForModule,
requireServerPluginFromPath,
showHelpForCommand
} = require('./util')

let Fastify = null

function loadModules (opts) {
try {
Fastify = requireFastifyForModule(opts._[0]).module
} catch (e) {
module.exports.stop(e)
}
}

function printPlugins (args) {
const opts = parseArgs(args)
if (opts.help) {
return showHelpForCommand('print-plugins')
}

if (opts._.length !== 1) {
console.error('Missing the required file parameter\n')
return showHelpForCommand('print-plugins')
}

// we start crashing on unhandledRejection
require('make-promises-safe')

loadModules(opts)

return runFastify(opts)
}

async function runFastify (opts) {
require('dotenv').config()

let file = null

try {
file = await requireServerPluginFromPath(opts._[0])
} catch (e) {
return module.exports.stop(e)
}

const fastify = Fastify(opts.options)

const pluginOptions = {}
if (opts.prefix) {
pluginOptions.prefix = opts.prefix
}

await fastify.register(file, pluginOptions)
await fastify.ready()
log('debug', fastify.printPlugins())

return fastify
}

function stop (message) {
exit(message)
}

function cli (args) {
return printPlugins(args).then(fastify => {
if (fastify) return fastify.close()
})
}

module.exports = { cli, stop, printPlugins }

if (require.main === module) {
cli(process.argv.slice(2))
}
6 changes: 5 additions & 1 deletion print-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ async function runFastify (opts) {

await fastify.register(file, pluginOptions)
await fastify.ready()
log('debug', fastify.printRoutes())
log('debug', fastify.printRoutes({
method: opts.method,
commonPrefix: opts.commonPrefix,
includeHooks: opts.includeHooks
}))

return fastify
}
Expand Down
1 change: 1 addition & 0 deletions templates/app-ts-esm/__taprc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ test-env: [
TS_NODE_FILES=true,
TS_NODE_PROJECT=./test/tsconfig.json
]
timeout: 120
1 change: 1 addition & 0 deletions templates/app-ts/__taprc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ test-env: [
TS_NODE_FILES=true,
TS_NODE_PROJECT=./test/tsconfig.json
]
timeout: 120
30 changes: 24 additions & 6 deletions test/args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ test('should parse args correctly', t => {
debugPort: 1111,
debugHost: '1.1.1.1',
loggingModule: './custom-logger.js',
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})

Expand Down Expand Up @@ -102,7 +105,10 @@ test('should parse args with = assignment correctly', t => {
debugPort: 1111,
debugHost: '1.1.1.1',
loggingModule: './custom-logger.js',
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})

Expand Down Expand Up @@ -172,7 +178,10 @@ test('should parse env vars correctly', t => {
debugPort: 1111,
debugHost: '1.1.1.1',
loggingModule: './custom-logger.js',
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})

Expand Down Expand Up @@ -260,7 +269,10 @@ test('should parse custom plugin options', t => {
debugPort: 1111,
debugHost: '1.1.1.1',
loggingModule: './custom-logger.js',
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})

Expand Down Expand Up @@ -295,7 +307,10 @@ test('should parse config file correctly and prefer config values over default o
require: undefined,
prefix: 'FASTIFY_',
loggingModule: undefined,
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})

Expand Down Expand Up @@ -334,6 +349,9 @@ test('should prefer command line args over config file options', t => {
require: undefined,
prefix: 'FASTIFY_',
loggingModule: undefined,
lang: 'js'
lang: 'js',
method: undefined,
commonPrefix: false,
includeHooks: undefined
})
})
114 changes: 114 additions & 0 deletions test/print-plugins.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use strict'

const proxyquire = require('proxyquire')
const tap = require('tap')
const sinon = require('sinon')
const util = require('node:util')
const exec = util.promisify(require('node:child_process').exec)

const printPlugins = require('../print-plugins')

const test = tap.test

test('should print plugins', async t => {
t.plan(3)

const spy = sinon.spy()
const command = proxyquire('../print-plugins', {
'./log': spy
})
const fastify = await command.printPlugins(['./examples/plugin.js'])

await fastify.close()
t.ok(spy.called)
t.same(spy.args[0][0], 'debug')
t.match(spy.args[0][1], /bound root \d+ ms\n├── bound _after \d+ ms\n├─┬ function \(fastify, options, next\) { -- fastify\.decorate\('test', true\) \d+ ms\n│ ├── bound _after \d+ ms\n│ ├── bound _after \d+ ms\n│ └── bound _after \d+ ms\n└── bound _after \d+ ms\n/)
})

// This never exits in CI for some reason
test('should plugins routes via cli', { skip: process.env.CI }, async t => {
t.plan(1)
const { stdout } = await exec('node cli.js print-plugins ./examples/plugin.js', { encoding: 'utf-8', timeout: 10000 })
t.match(
stdout,
/bound root \d+ ms\n├── bound _after \d+ ms\n├─┬ function \(fastify, options, next\) { -- fastify\.decorate\('test', true\) \d+ ms\n│ ├── bound _after \d+ ms\n│ ├── bound _after \d+ ms\n│ └── bound _after \d+ ms\n└── bound _after \d+ ms\n\n/
)
})

test('should warn on file not found', t => {
t.plan(1)

const oldStop = printPlugins.stop
t.teardown(() => { printPlugins.stop = oldStop })
printPlugins.stop = function (message) {
t.ok(/not-found.js doesn't exist within/.test(message), message)
}

const argv = ['./data/not-found.js']
printPlugins.printPlugins(argv)
})

test('should throw on package not found', t => {
t.plan(1)

const oldStop = printPlugins.stop
t.teardown(() => { printPlugins.stop = oldStop })
printPlugins.stop = function (err) {
t.ok(/Cannot find module 'unknown-package'/.test(err.message), err.message)
}

const argv = ['./test/data/package-not-found.js']
printPlugins.printPlugins(argv)
})

test('should throw on parsing error', t => {
t.plan(1)

const oldStop = printPlugins.stop
t.teardown(() => { printPlugins.stop = oldStop })
printPlugins.stop = function (err) {
t.equal(err.constructor, SyntaxError)
}

const argv = ['./test/data/parsing-error.js']
printPlugins.printPlugins(argv)
})

test('should exit without error on help', t => {
const exit = process.exit
process.exit = sinon.spy()

t.teardown(() => {
process.exit = exit
})

const argv = ['-h', 'true']
printPlugins.printPlugins(argv)

t.ok(process.exit.called)
t.equal(process.exit.lastCall.args[0], undefined)

t.end()
})

test('should print plugins of server with an async/await plugin', async t => {
const nodeMajorVersion = process.versions.node.split('.').map(x => parseInt(x, 10))[0]
if (nodeMajorVersion < 7) {
t.pass('Skip because Node version < 7')
return t.end()
}

t.plan(3)

const spy = sinon.spy()
const command = proxyquire('../print-plugins', {
'./log': spy
})
const argv = ['./examples/async-await-plugin.js']
const fastify = await command.printPlugins(argv)

await fastify.close()
t.ok(spy.called)
t.same(spy.args[0][0], 'debug')
t.match(spy.args[0][1], /bound root \d+ ms\n├── bound _after \d+ ms\n├─┬ async function \(fastify, options\) { -- fastify\.get\('\/', async function \(req, reply\) { \d+ ms\n│ ├── bound _after \d+ ms\n│ └── bound _after \d+ ms\n└── bound _after \d+ ms\n/)
})
Loading

0 comments on commit af2e457

Please sign in to comment.