Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/oakfang/oompa
A tiny pick-me-app for websocket-based, stateless, microservices.
https://github.com/oakfang/oompa
Last synced: 20 days ago
JSON representation
A tiny pick-me-app for websocket-based, stateless, microservices.
- Host: GitHub
- URL: https://github.com/oakfang/oompa
- Owner: oakfang
- License: mit
- Created: 2016-07-06T17:56:42.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2016-07-19T08:24:24.000Z (over 8 years ago)
- Last Synced: 2024-12-28T17:04:19.983Z (28 days ago)
- Language: JavaScript
- Size: 16.6 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# oompa
A tiny pick-me-app for websocket-based, stateless, microservices.## Installation
**Server (Node 6+)** `require('oompa')`
**Client (Node 4+)** `require('oompa/client')`
## Usage
### ServerThe `oompa` library draws inspiration from [redux](https://github.com/reactjs/redux) in that it handles
typed *tasks* (called `actions` in redux).The task types are defined in an object called *Application Schema*.
#### The Application Schema
This is an example schema that defines the `ADD` task, giving it a factory method.```js
const appSchema = {
// The factory method accepts the task payload as a parameter,
// and should return a promise
ADD: ({x, y}) => Promise.resolve(x + y)
};
```#### The healthcheck
Every microservice should have a way to express health via HTTP (or so AWS says).
This is why the second parameter of the server's constructor should be a method that returns
a promise that is resolved when we know all is well, and rejected otherwise.#### Server methods
- `listen(port)` - Start listening @`port`. Return a promise resolved when the server is ready.
- `close()` - Close both HTTP and WebSockets servers, returns a promise resolved when both are closed.
- `use(middleware)` - see [middleware](#middleware) section below.#### Server events
- `error(Error err)`: emitted when the healthcheck fails with error `err`.
- `connection(Connection con)`: emitted when `con` is connected to the server.
- `terminated(Connection con)`: emitted when `con` is disconnected from the server.
- `reply(Reply r)`: emitted when a reply is ready to be sent. A reply has a type (`OK|ERR`) an `id` correlating to its request and `payload` or `error`.
- `stale(Reply)`: emitted after the `reply` event, when finding no live connection to reply to.#### An actual example
```js
const serverApp = {
ADD: ({x, y}) => Promise.resolve(x + y),
SUB: ({x, y}) => Promise.resolve(x - y),
MUL: ({x, y}) => Promise.resolve(x * y),
DIV: ({x, y}) => Promise[y ? 'resolve' : 'reject'](x / y),
};function healthcheck() {
return Promise.resolve(); // let's assume sunshine and rainbows
}const server = new Server(serverApp, healthcheck);
server
.on('connection',
() => logger.info('Connection created'))
.on('terminated',
() => logger.info('Connection terminated'))
.on('error',
err => logger.error(err, 'Server error'))
.on('reply',
({type, id}) => logger.debug(`[${type}] for request #${id}`))
.on('stale',
({type, id}) => logger.warn(`[${type}] for stale request #${id}`))
.listen(PORT).then(() => logger.info(`Listening on port ${PORT}`));
```### Client
The Oompa client is actually very lean. You can use it in one of two forms:#### Lean usage
```js
const OompaClient = require('oompa/client');
const client = new OompaClient('ws://localhost:9000');// dispatch accepts the type and the payload of the task
client.dispatch('ADD', { x: 1, y: 6 }).then(result => {
console.log(`${res} should be 7`);
});
```#### Verbose Usage
```js
const OompaClient = require('oompa/client');
const clientMethods = {
// [methodName]: {type: [taskType], factory: [methodParams -> taskPayload]}
add: { type: 'ADD', factory: (x, y) => ({x, y}) },
sub: { type: 'SUB', factory: (x, y) => ({x, y}) },
mul: { type: 'MUL', factory: (x, y) => ({x, y}) },
div: { type: 'DIV', factory: (x, y) => ({x, y}) },
};
const client = new OompaClient('ws://localhost:9000', clientMethods);client.add(1, 2).then(...);
```#### Client methods
- `constructor(url, methods, options)` - Create a new client with server @ `url`, the specified `methods`, with the following options:
- `noServer:false` don't actually try to attempt to connect on init.
- `reconnectInterval:1000` try to reconnect to a server after server CLOSE event every `reconnectInterval` ms.
- `timeout:10000` disregard an attempt of request as a timeout after `timeout` ms.
- `attempts:3` reject a request after `attempts` timeouts as a timeout error.
- `tolerance:{ratio: 0.05, interval: 10000}` attempt reconnect to server if after `interval` ms, the ratio of timeouts out of total requests exceeds `ratio`.
- `ping(timeout)` - Check for the server's health. Wait for `timeout` ms until auto-rejecting.#### Client events
- `error`: emitted by propagation from the underlying socket
- `host-closed`: emitted when the host is closed abnormally (attempts to reconnect every 1 second)
- `reconnect-failed`: emitted when an attempt to reconnect has failed
- `reconnected`: emitted when the last attempt to reconnect was successful
- `OK:`: emitted when task received an OK reply from the server, with its payload
- `ERR:`: emitted when task received an ERR reply from the server, with its error### Middleware
Normally, a server simply forwards the request payload to its factory. Sometimes, however, you'd rather
the request go through other steps before reaching the factory, if at all!```js
const Server = require('oompa');const cache = new Map();
function cacheMiddleware(request, next) {
const type = request.type;
const {x, y} = request.payload;
const cacheUrl = `${type}/${x}/${y}`;
if (cache.has(cacheUrl)) return cache.get(cacheUrl);
return next(request).then(result => {
cache.set(cacheUrl, result);
return result;
});
}const serverApp = {
ADD: ({x, y}) => Promise.resolve(x + y),
SUB: ({x, y}) => Promise.resolve(x - y),
MUL: ({x, y}) => Promise.resolve(x * y),
DIV: ({x, y}) => Promise[y ? 'resolve' : 'reject'](x / y),
};function healthcheck() {
return Promise.resolve(); // let's assume sunshine and rainbows
}const server = new Server(serverApp, healthcheck);
server.use(cacheMiddleware);server
.on('connection',
() => logger.info('Connection created'))
.on('terminated',
() => logger.info('Connection terminated'))
.on('error',
err => logger.error(err, 'Server error'))
.on('reply',
({type, id}) => logger.debug(`[${type}] for request #${id}`))
.on('stale',
({type, id}) => logger.warn(`[${type}] for stale request #${id}`))
.listen(PORT).then(() => logger.info(`Listening on port ${PORT}`));
```