-
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
--- | ||
id: version-6.1.0-plugin | ||
title: Plugin | ||
sidebar_label: Plugin | ||
original_id: plugin | ||
--- | ||
|
||
Hemera's plugin system based on the powerful [Avvio](https://github.com/mcollina/avvio) package. Avvio is fully reentrant and graph-based. You can load components/plugins within plugins, and be still sure that things will happen in the right order. | ||
|
||
> Plugins should encourage you to encapsulate a domain specific context in a reusable piece of software. Great practice is to seperate plugins by a different topic name. | ||
## Plugin helper library | ||
|
||
Before we get into the plugin system of hemera you have to install a package called [`hemera-plugin`](https://github.com/hemerajs/hemera/tree/master/packages/hemera-plugin). This package can do some things for you: | ||
|
||
- Check the bare-minimum version of Hemera | ||
- Provide consistent interface to register plugins even when the api is changed | ||
- Pass metadata to intialize your plugin with correct dependencies, default options and name | ||
- Skip plugin encapsulation | ||
|
||
## Create a plugin | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp((hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.add( | ||
{ | ||
topic, | ||
cmd: 'add' | ||
}, | ||
function(req, cb) { | ||
cb(null, { | ||
result: req.a + req.b | ||
}) | ||
} | ||
) | ||
|
||
done() | ||
}, '>=5.0.0') | ||
|
||
module.exports = myPlugin | ||
``` | ||
|
||
> Plugins are **encapsulated** "_scoped_" by default. If you define an extension inside it will only effects the actions in your plugin. You can disable it if you set the hemera plugin option `scoped:false`. | ||
## Break encapsulation | ||
|
||
Sometimes it is still useful to write a plugin which effects child as well as sibling scopes. You can archive this with plugin option `scoped: false` property. This approach is used in payload validators `hemera-joi` or authentication `hemera-jwt`. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp( | ||
(hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.ext('onClientPostRequest', function(ctx, next) { | ||
// some code | ||
next() | ||
}) | ||
|
||
done() | ||
}, | ||
{ scoped: false } | ||
) | ||
|
||
hemera.use(myPlugin) | ||
``` | ||
|
||
## Register child plugins | ||
|
||
You can load plugins inside other plugins. Be ware that [**Scoped sensitive settings**](#scoped-sensitive-settings) are effected. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp((hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.ext('onClientPostRequest', function(ctx, next) { | ||
// some code | ||
next() | ||
}) | ||
|
||
hemera.use( | ||
hp((hemera, opts, done) => { | ||
// some code | ||
// this plugin will be effected by the 'onClientPostRequest' extension | ||
done() | ||
}) | ||
) | ||
|
||
done() | ||
}) | ||
``` | ||
|
||
### Decorators | ||
|
||
Decorators are something special. Even if you create a plugin scope you can decorate the root hemera instance. Decorators are primarly used to expose data or functionality to other plugins. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp((hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.decorate('test', 1) | ||
|
||
done() | ||
}) | ||
|
||
hemera.use(myPlugin) | ||
hemera.ready(() => console.log(hemera.test)) | ||
``` | ||
|
||
### Expose | ||
|
||
If you want to share data inside a plugin you can use `expose()` it will effects all sibling and child scopes. Expose should be used to control the inner workings of the plugin. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp((hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.expose('cache', new Map()) | ||
hemera.cache.set('key', 'value') | ||
|
||
hemera.use(myPlugin2) // cache is available in this plugin too | ||
|
||
done() | ||
}) | ||
|
||
hemera.use(myPlugin) | ||
hemera.ready() | ||
``` | ||
|
||
### Global registration | ||
|
||
Sometimes it's still useful to write a plugin which effects sibling and child scopes. You can disable the creation of a plugin scope with the `scoped: false` property. | ||
This approach is used in payload validators `hemera-joi` or authentication plugins like `hemera-jwt`. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp( | ||
(hemera, opts, done) => { | ||
const topic = 'math' | ||
|
||
hemera.ext('onServerPreRequest', function(ctx, next) { | ||
// some code | ||
next() | ||
}) | ||
|
||
done() | ||
}, | ||
{ | ||
scoped: false | ||
} | ||
) | ||
|
||
hemera.use(myPlugin) | ||
``` | ||
|
||
### Scoped sensitive settings | ||
|
||
- [Request/Response Extensions](extension.md#server-client-lifecycle) | ||
- [Decorators](decorator.md) | ||
- [Schema Compilers](payload-validation.md#use-your-custom-validator) | ||
- [Codecs](codec.md) | ||
- [Not found pattern](notfound-pattern.md) | ||
|
||
## Add plugin metadata | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp( | ||
async (hemera, opts) => { | ||
const topic = 'math' | ||
|
||
hemera.add( | ||
{ | ||
topic, | ||
cmd: 'add' | ||
}, | ||
function(req, cb) { | ||
cb(null, { | ||
result: req.a + req.b | ||
}) | ||
} | ||
) | ||
}, | ||
{ | ||
hemera: '0.x', // bare-minimum version of Hemera | ||
name: 'my-plugin', // name of your plugin, will be used e.g for logging purposes | ||
options: { host: 'localhost', port: 8003 }, // default options for your plugin | ||
dependencies: ['plugin2'], | ||
decorators: ['joi'] | ||
} | ||
) | ||
|
||
hemera.use(myPlugin) | ||
``` | ||
|
||
## Plugin depdendencies | ||
|
||
You can declare plugins and decorators as dependencies. The constrains are checked when all plugins are registered. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp( | ||
(hemera, opts, done) => { | ||
const Joi = hemera.joi | ||
|
||
done() | ||
}, | ||
{ | ||
dependencies: ['plugin2'], | ||
decorators: ['joi'] | ||
} | ||
) | ||
|
||
hemera.use(myPlugin) | ||
hemera.ready(err => { | ||
// The dependency 'hemera-joi' is not registered | ||
}) | ||
``` | ||
|
||
## Async / Await | ||
|
||
You can also pass an async function and omit the `done` callback. | ||
|
||
```js | ||
const hp = require('hemera-plugin') | ||
const myPlugin = hp(async (hemera, opts) => { | ||
const topic = 'math' | ||
|
||
hemera.add( | ||
{ | ||
topic, | ||
cmd: 'add' | ||
}, | ||
function(req, cb) { | ||
cb(null, { | ||
result: req.a + req.b | ||
}) | ||
} | ||
) | ||
}) | ||
``` | ||
|
||
## Plugin registration | ||
|
||
A plugin must be registered before the `ready` function is called. The ready function will initialize all plugins. Default plugin options are preserved if you don't overwrite them. | ||
|
||
```js | ||
hemera.use(plugin, { a: 1 }) | ||
``` | ||
|
||
## After | ||
|
||
Calls a function after all previous registrations are loaded, including all their dependencies. This can be used to defer the registration of a plugin. | ||
|
||
```js | ||
hemera.use(plugin).after(cb) | ||
// or | ||
hemera.use(plugin) | ||
hemera.after(cb) | ||
``` | ||
|
||
## Plugin timeout | ||
|
||
If you need to handle a lot of plugins you could run into timeout issues e.g if your forgot to call the callback or when no promise was resolved. These errors are hard to track. In order to find out which plugin caused it you can specify a `pluginTimeout`. | ||
The error contains a property `fn` which contains your plugin functions all associated informations about your plugin. | ||
|
||
```js | ||
new Hemera(nats, { pluginTimeout: 100 }) | ||
hemera.use(plugin) | ||
hemera.use(err => { | ||
err.fn.name // the name of the plugin function | ||
err.fn[Symbol.for('plugin-meta')] // all plugin informations like name or options | ||
}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
[ | ||
"6.1.0", | ||
"6.0.1", | ||
"6.0.0", | ||
"5.8.6", | ||
|