MicroCell
MicroCell is a toolkit for writing AppCell plugins.
Instalation
microcell
is available on npm. To install it, type:
$ npm install @apihawk/microcell
Usage
Import the library in your code and and choose the transport library you want to use:
const MicroCell = require('@apihawk/microcell');
const MicroCellAMQPTransport = require('@apihawk/microcell-amqp-transport');
const server = new (MicroCell.plugin(MicroCellAMQPTransport))();
server
.listen({
amqp_queue: process.ENV.AMQP_QUEUE,
amqp_url: process.ENV.AMQP_URL
})
.add({foo: 'One', bar: 'Two'}, (data, respond) => {
respond(null, `Payload is: ${data.payload}`);
})
.act({foo: 'One', bar: 'Two', payload: 'Hello World'}, () => {
console.log("Final response:", response); // Final response: Payload is: Hello World
});
Modes
Server mode
The server mode is the most common mode of usage. In that mode, the app runs as a daemon and listens for instructions over the selected transport. It also supports actions (act
) as the client mode.
const MicroCell = require('@apihawk/microcell');
const MicroCellAMQPTransport = require('@apihawk/microcell-amqp-transport');
const server = new (MicroCell.plugin(MicroCellAMQPTransport))();
server
.listen({
amqp_queue: process.ENV.AMQP_QUEUE,
amqp_url: process.ENV.AMQP_URL
})
.add({foo: 'One', bar: 'Two'}, (data, respond) => {
respond(null, `Payload is: ${data.payload}`);
})
Client mode
The client mode executes all actions and exits after all of them get a response:
const MicroCell = require('@apihawk/microcell');
const MicroCellAMQPTransport = require('@apihawk/microcell-amqp-transport');
const client = new (MicroCell.plugin(MicroCellAMQPTransport))();
client
.client({
amqp_queue: process.ENV.AMQP_QUEUE,
amqp_url: process.ENV.AMQP_URL,
timeout: 2000 // Force exit after 2s
})
.act({foo: 'One', bar: 'Two', payload: 'Hello World'}, () => {
console.log("Final response:", response);
});
Test mode
Test mode is for unit testing only. It does not require transport.
var Microcell = require('@apihawk/microcell');
var expect = require('chai').expect;
const Zones = require('../src/zones'); // Import some listeners (`add()`) from your module under test
describe('zones', () => {
const microcell = (new Microcell()).test().use(Zones);
it('create', done => {
expect(microcell).to.be.an('object');
microcell.act({resource: 'Zone', action: 'Create', _payload: 'secret data!'}, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
expect(result._payload).to.exist;
done();
});
});
});
Methods
static plugin(plugin)
The plugin
function allows extending of the library with plugins. It accepts a single parameter, which should be a MicroCell plugin. Returns the resulting class.
const MicroCell = require('@apihawk/microcell');
const MicroCellAMQPTransport = require('@apihawk/microcell-amqp-transport');
const client = new (MicroCell.plugin(MicroCellAMQPTransport))();
listen([params])
The listen
method starts a server that listens for JSON messages depending on the transport plugin. If you do not use a plugin, the function exits. Accepts params
object which passes parameters for the used transport plugin. Returns the microcell
object so that methods can be chained.
client([params])
On the client-side, calling microcell.client()
means that MicroCell sends any actions it cannot match locally out over the selected transport.
Accepts params
object which passes parameters for the used transport plugin. Returns the microcell
object so that methods can be chained.
test()
Test mode is to run tests locally.
use(module[, rule])
Loads module with add
/act
methods.
const Zones = require('./zones');
module.exports = function({
amqp_queue,
amqp_url
}) {
const server = new (MicroCell.plugin(MicroCellAMQPTransport))();
server
.use(Zones, {resource: 'Zone'})
.listen({
amqp_queue: amqp_queue,
amqp_url: amqp_url
});
};
And in zones.js
we have:
module.exports = function zones() {
this.add({action: 'Create'}, (msg, respond) => {
respond(null, { hello: 'create'});
});
this.add({action: 'Read'}, (msg, respond) => {
console.log("Read", msg);
respond(null, { hello: 'read' });
});
this.add({action: 'Update'}, (msg, respond) => {
respond(null, { hello: 'update' });
});
this.add({action: 'Delete'}, (msg, respond) => {
respond(null, { hello: 'delete' });
});
};
The function accepts two parameters. The first is the module and the second is a rule
to be matched for all the add
methods in the module. rule
is optional.
add(rules, callback)
Adds a listener for incoming actions. It accepts two parameters. rules
is the rule to match and a callback
is the function to execute on a match.
The callback
has two parameters. msg
is the data sent by the action together with the filtering rules. respond
is a function to be called in response to the action. Accepts two parameters as follows: an error
and a response data
.
server.add({action: 'Delete'}, (msg, respond) => {
respond(null, { hello: 'delete' });
});
act(data, callback)
act
sends an action to a local or remote listener. It accepts two parameters. data
is an object containing the rule parameters and the payload. Te callback
is called on response with two parameters identical to the respond
function of the add
method. error
and data
.
client.act({
resource: 'Zone',
action: 'Read',
payload: { domain: 'exapmpe.com' }
}, (err, response) => {
console.log("Final response:", response);
});
Tools
error(err | null[,options])
Returns errors formatted in RFC 7807 - Problem Details for HTTP APIs format.
Parameters:
-
err
- an Error object ornull
. -
params
- object with optional parameters:-
code
- status code. Type:string
Default value:'500'
-
message
- error message. Type:string
. Default value:'Internal server error'
-
detail
- error details. Type:string
. Default value:err.message
-
request
- original request data. TypeObject
-
stack
- error stack trace. Type:string
orObject
. Default value:err.stack
-
validation_messages
- messages on validation errors. Type:Array<string>
-
original_error
- The original error if any. Type:string
orObject
.
-
respond(this.error(new Error('Something went wrong!'), {
message: 'Service is temporarly unavailable',
code: '503'
}));
validationError(err | null[,options])
Same as error
but the default values for params.code
and params.messages
are '422'
and 'Validation failed'
.
respond(this.validationError(null, {validation_messages: errors}), null);
validate(data, schema)
Validates a data object against a JSON Schema.
const errors = this.validate(data.id, {'type': 'number'});
if (errors.length) {
respond(this.validationError(null, {validation_messages: errors}), null);
return;
}
validateHttpResponse(err, response)
Turns HTTP errors (4xx, 5xx) into API Problem errors. Parameters:
-
err
- an Error object. -
response
- a http.IncomingMessage object.
request.delete({
url: `${api_base}/dns_zones/${ data.id }.json`,
auth: auth
}, (err, response) => {
const httpError = this.validateHttpResponse(err, response);
if (httpError) {
respond(httpError);
return;
}
respond(null, this.formatResponse());
});
formatResponse(body[,params])
Returns a JavaScript object which includes response
body, title
and status
. The returned object can be for a response.
-
body
- A JavaScript Object to respond with -
params
- object with optional parameters:-
code
- status code. Type:string
Default value:'200'
-
title
- error title. Type:string
. Default value:OK
-
respond(null, this.formatResponse({foo: "bar"})); // {title:'OK',status:'200',response:{foo:'bar'}}
License
Copyright (c) 2019 Anton Katsarov and other contributors; Licensed under MIT.