stellar-integration

GamesLabs Stellar integration module

Usage no npm install needed!

<script type="module">
  import stellarIntegration from 'https://cdn.skypack.dev/stellar-integration';
</script>

README

GamesLabs Stellar Integration Framework

Quick start

You need to have a config.json configuration file in your project root folder. Here is the default configuration:

{
    "rabbitmq": "amqp://localhost"
}

Instantiating stellar

const stellar = require('stellar-integration')

// listen to port 1234
const PORT = 1234

// code

stellar.listen(PORT)

Importing DSL elements

const redirect = stellar.redirect
const parallel = stellar.parallel
const rpc = stellar.rpc
const push = stellar.push
const peek = stellar.peek
const pop = stellar.pop
const pipeline = stellar.pipeline

Creating a router

let router1 = stellar.router()
let router2 = stellar.router('/prefix')

Declaring a route

Simple body writer:

router.get('/hello')
            .then(function(req, res) {
                res.body = "Hello world"
                this.next(res) // Continue the pipeline
            })
// Available router methods: get,post,put,delete,when

Stack

You can push and peek/pop requests through the pipeline

router.get('/hello')
            .then(function(req, res) {
                res.body = "Hello world"
                this.next(res) // Continue the pipeline
            })
            .then(push())
            .then(function(req, res) {
                res.body = 'RANDOM'
                this.next(res)
            })
            .then(pop()) // body is now "Hello World"

Redirecting

router.get('/google')
            .then(redirect('https://google.com'))
// GET request to post
router.when('/get-to-post/:id', 'GET')
            .then(redirect('https://requestb.in/1glh67p1', function(req) {
                req.method = 'POST'
                req.body = {
                    id: req.params.id,
                    filter: req.query.filter || 'default'
                }
            }))

Processing parallel requests

// error if one parallel endpoint fails
router.get('/parallel')
            .then(
                parallel()
                    .with(redirect(ENDPOINT_1))
                    .with(redirect(ENDPOINT_2), {default: {hello: "ko"}})// default return value
                    .with(redirect(ENDPOINT_3), {default: {hello: "ko"}, timeout: 1}) // default value and timeout (ms)
                    .aggregate(function(results, res) {
                        res.body = [{aggregate: results.map((r) => r.body)}]
                        this.next(res)
                    })
            )
// do not break the pipeline if an error occurs
router.get('/parallel')
            .then(
                parallel()
                    .with(redirect(ENDPOINT_1))
                    .with(redirect(ENDPOINT_2), {default: {hello: "ko"}})// default return value
                    .with(redirect(ENDPOINT_3), {default: {hello: "ko"}, timeout: 1}) // default value and timeout (ms)
                    .aggregate(function(results, res) {
                        res.body = [{aggregate: results.map((r) => r.body)}]
                        this.next(res)
                    }, {exitOnError: false})
            )

Resilient Remote Procedure Calls

router.post('/rpc')
    .then(rpc("users.createUser"))

// Example with get request
router.get('/rpc')
            // Transform request, create our user
            .then(function(req, res) {
                res.body = {
                    name: req.query.name || "toto"
                }
                this.next(res)
            })
            // rpc to (users).createUser({name: 'toto'})
            .then(rpc("users.createUser"))

// with timeout (in ms)
router.get('/rpc-timeout')
            // Transform request, create our user
            .then(function(req, res) {
                res.body = {
                    name: req.query.name || "toto"
                }
                this.next(res)
            })
            // rpc to (users).createUser({name: 'toto'})
            .then(rpc("users.createUser", {timeout: 100}))

Here is an example of the users service handling the rpc calls:

let events = require('stellar-integration').events
let listen = events.listen('users', 100) // Handle at max 100 rpc calls at once

listen('createUser', (user, callback) => {
    let user = {name: user.name, id: Math.random().toString()}
    callback(user)
})

Events

router.post('/event')
    .then(event("users.notify"))

// Example with get request
router.get('/event')
            // Transform request, create our user
            .then(function(req, res) {
                res.body = {
                    name: req.query.name || "toto"
                }
                this.next(res)
            })
            // rpc to (users).createUser({name: 'toto'})
            .then(event("users.notify"))

Here is an example of the users service handling the event calls:

let events = require('stellar-integration').events
let listen = events.listen('users', 100) // Handle at max 100 events at once

listen('notify', (user, callback) => {
    console.log('Notifying user', user)
    callback() // so the event queue knows we are done processing the event
})

Pipelines

You can use the pipeline dsl to create pipelines:

let myPipeline = pipeline()
        .then(function(req,res) {
            res.body = 'Hello'
            this.next(res)
        })
        .then(function(req,res) {
            res.body = req.body + ' World'
            this.next(res)
        })
        .build()

router.get('/hey')
            .then(myPipeline)

Split

You can use the clause and split dsl to create splits:

let clause = stellar.clause

let myPipeline = pineline()
        .then(function(req,res) {
            res.body = 'Hello'
            this.next(res)
        })
        .split([
            clause(function(req) {return req.body == 'Hello'}, function(req, res) {
                res.body += ' world'
                this.next(res)
            }),
            clause(function(req) {return req.body != 'Hello'}, function(req, res) {
                res.body = 'This should not happen...'
                this.stop(res)
            })
        ])
        .build()

router.get('/split')
            .then(myPipeline)

Plugins

WIP Available plugins:

  • authentication
  • authorization
  • circuit breaker