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

Autoload options (maxDepth and encapsulate) #867

Merged
merged 5 commits into from
Apr 14, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/reference/db/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ An optional object that defines the plugins loaded by Platformatic DB.
or an array of objects composed as follows,
- `path` (`string`): Relative path to plugin's entry point.
- `options` (`object`): Optional plugin options.
- `encapsulate` (`boolean`): if the path is a folder, it instruct Platformatic to not
[encapsulate](https://www.fastify.io/docs/latest/Reference/Encapsulation/) those plugins,
allowing decorators and hooks to be shared across all routes.
- `maxDepth` (`integer`): if the path is a folder, it limits the depth to load the content from.
- **`typescript`** (`boolean`): enable typescript compilation. A `tsconfig.json` file is required in the same folder.
- **`hotReload`** (`boolean`, default: `true`) if `true` or not specified, the plugin is loaded using [`fastify-sandbox`](https://github.com/mcollina/fastify-sandbox), otherwise is loaded directly using `require`/`import` and the hot reload is not enabled

Expand Down
2 changes: 2 additions & 0 deletions docs/reference/service/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ An optional object that defines the plugins loaded by Platformatic Service.
or an array of objects composed as follows,
- `path` (`string`): Relative path to plugin's entry point.
- `options` (`object`): Optional plugin options.
- `encapsulate` (`boolean`): if the path is a folder, it instruct Platformatic to not encapsulate those plugins.
mcollina marked this conversation as resolved.
Show resolved Hide resolved
- `maxDepth` (`integer`): if the path is a folder, it limits the depth to load the content from.
- **`typescript`** (`boolean`): enable typescript compilation. A `tsconfig.json` file is required in the same folder.
- **`hotReload`** (`boolean`, default: `true`) if `true` or not specified, the plugin is loaded using [`fastify-sandbox`](https://github.com/mcollina/fastify-sandbox), otherwise is loaded directly using `require`/`import` and the hot reload is not enabled

Expand Down
4 changes: 3 additions & 1 deletion packages/service/lib/sandbox-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ const { stat } = require('fs').promises
module.exports = fp(async function (app, opts) {
for (let plugin of opts.paths) {
if (typeof plugin === 'string') {
plugin = { path: plugin }
plugin = { path: plugin, encapsulate: true }
}
if ((await stat(plugin.path)).isDirectory()) {
app.register(autoload, {
dir: plugin.path,
encapsulate: plugin.encapsulate !== false,
maxDepth: plugin.maxDepth,
options: plugin.options
})
} else {
Expand Down
7 changes: 7 additions & 0 deletions packages/service/lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ const plugins = {
type: 'string',
resolvePath: true
},
encapsulate: {
type: 'boolean',
default: true
},
maxDepth: {
type: 'integer'
},
options: {
type: 'object',
additionalProperties: true
Expand Down
54 changes: 54 additions & 0 deletions packages/service/test/autoload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,57 @@ test('multiple files / watch false / no object', async ({ teardown, equal }) =>
equal(body.hello, 'bar', 'body')
}
})

test('nested directories', async ({ teardown, equal, same }) => {
const config = {
server: {
hostname: '127.0.0.1',
port: 0
},
service: {
openapi: true
},
plugins: {
paths: [{
path: join(__dirname, 'fixtures', 'nested-directories', 'plugins'),
encapsulate: false
}, {
path: join(__dirname, 'fixtures', 'nested-directories', 'modules'),
encapsulate: false,
maxDepth: 1
}]
}
}

const server = await buildServer(config)
teardown(server.stop)
await server.listen()

{
const res = await request(`${server.url}/inventory/product/42`)
equal(res.statusCode, 200, 'status code')
const body = await res.body.json()
same(body, { sku: 42, inStore: 2 }, 'body')
}

{
const res = await request(`${server.url}/catalogue/products`)
equal(res.statusCode, 200, 'status code')
const body = await res.body.json()
same(body, [{ sku: 42, name: 'foo', inStore: 2 }, { sku: 43, name: 'bar', inStore: 0 }], 'body')
}

{
const res = await request(`${server.url}/foo/baz`)
equal(res.statusCode, 404, 'status code')
const body = await res.body.text()
equal(body, 'I\'m sorry, I couldn\'t find what you were looking for.')
}

{
const res = await request(`${server.url}/catalogue/error`)
equal(res.statusCode, 500, 'status code')
const body = await res.body.text()
equal(body, 'I\'m sorry, there was an error processing your request.')
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import autoload from '@fastify/autoload'
import { join } from 'desm'

export default async function catalogue (app, opts) {
app.register(autoload, {
dir: join(import.meta.url, 'routes'),
options: {
prefix: opts.prefix
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default async function (fastify, opts) {
fastify.get('/products', async (request, reply) => {
const data = [{ sku: 42, name: 'foo' }, { sku: 43, name: 'bar' }]
// Currently slow and inefficient, but ok for the demo
for (const product of data) {
product.inStore = await fastify.inventory.howManyInStore(product.sku)
}
return data
})

fastify.get('/error', async (request, reply) => {
throw new Error('This is an error')
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import fp from 'fastify-plugin'
import autoload from '@fastify/autoload'
import { join } from 'desm'

class Inventory {
async howManyInStore (sku) {
if (sku === 42) {
return 2
} else {
return 0
}
}
}

async function inventory (fastify, opts) {
// This will be published to the root fastify instance
// it could also be extracted to a separate plugin
fastify.decorate('inventory', new Inventory())

// These routes would be created in their own child instances
fastify.register(autoload, {
dir: join(import.meta.url, 'routes'),
options: {
prefix: opts.prefix
}
})
}

export default fp(inventory)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

export default async function (fastify, opts) {
fastify.get('/product/:sku', {
schema: {
params: {
type: 'object',
properties: {
sku: { type: 'number' }
}
}
}
}, async (request, reply) => {
const sku = request.params.sku
return { sku, inStore: await fastify.inventory.howManyInStore(sku) }
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://platformatic.dev/schemas/v0.19.7/service",
"server": {
"hostname": "{PLT_SERVER_HOSTNAME}",
"port": "{PORT}",
"logger": {
"level": "{PLT_SERVER_LOGGER_LEVEL}"
}
},
"service": {
"openapi": true
},
"plugins": {
"paths": [{
"path": "plugins",
"encapsulate": false
}, {
"path": "modules",
"encapsulate": false,
"maxDepth": 1
}]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export default async function (app, opts) {
app.setErrorHandler(async (err, request, reply) => {
if (err.validation) {
reply.code(403)
return err.message
}
request.log.error({ err })
reply.code(err.statusCode || 500)

return "I'm sorry, there was an error processing your request."
})

app.setNotFoundHandler(async (request, reply) => {
reply.code(404)
return "I'm sorry, I couldn't find what you were looking for."
})
}