<|start_filename|>index.js<|end_filename|>
'use strict'

const fp = require('fastify-plugin')
const Nats = require('nats')
const Hemera = require('nats-hemera')

function fastifyHemera(fastify, opts, next) {
  const hemera = new Hemera(opts.natsInstance || Nats.connect(opts.nats), opts.hemera)

  hemera.ext('onError', (error) => fastify.log.error(error))

  if (opts.plugins && opts.plugins.length) {
    opts.plugins.forEach((p) => {
      if (p.plugin) {
        hemera.use(p.plugin, p.options)
      } else {
        hemera.use(p)
      }
    })
  }

  fastify.addHook('onClose', (instance, done) => {
    fastify.log.debug('closing hemera...')
    hemera.close(() => {
      fastify.log.debug('hemera closed!')
      done()
    })
  })

  fastify.decorate('hemera', hemera)
  fastify.decorateRequest('hemera', hemera)
  fastify.decorateReply('add', hemera.add.bind(hemera))
  fastify.decorateReply('act', function act(pattern) {
    return hemera
      .act(pattern)
      .then((resp) => this.send(resp.data))
      .catch((err) => this.send(err))
  })

  hemera.ready(next)
}

module.exports = fp(fastifyHemera, {
  fastify: '>=3.0.0',
  name: 'fastify-hemera'
})


<|start_filename|>example.js<|end_filename|>
'use strict'

// eslint-disable-next-line
const Fastify = require('fastify')
const plugin = require('./')

function hemeraActions(fastify) {
  fastify.hemera.add(
    {
      topic: 'math',
      cmd: 'add'
    },
    function handler(req, reply) {
      reply(null, { result: req.a + req.b })
    }
  )
}

function routes(fastify) {
  fastify.route({
    method: 'GET',
    url: '/reply',
    schema: {
      querystring: {
        a: { type: 'integer' },
        b: { type: 'integer' }
      }
    },
    handler: (req, reply) => {
      req.log.info('Reply route')
      reply.act({ topic: 'math', cmd: 'add', a: req.query.a, b: req.query.b })
    }
  })

  fastify.route({
    method: 'GET',
    url: '/request',
    schema: {
      querystring: {
        a: { type: 'integer' },
        b: { type: 'integer' }
      }
    },
    handler: (req) => {
      req.log.info('Reply route')
      return req.hemera
        .act({
          topic: 'math',
          cmd: 'add',
          a: req.query.a,
          b: req.query.b
        })
        .then((resp) => resp.data)
    }
  })
}

function build(opts) {
  const fastify = Fastify({ logger: opts.logger })

  fastify
    .register(plugin, {
      hemera: opts.hemera,
      plugins: opts.plugins,
      nats: opts.nats
    })
    .after((err) => {
      if (err) {
        throw err
      }
      routes(fastify)
      hemeraActions(fastify)
    })

  return fastify
}

if (require.main === module) {
  const fastify = build({
    logger: {
      level: 'info'
    }
  })
  fastify.listen(3000)
}

module.exports = build


<|start_filename|>basic.test.js<|end_filename|>
'use strict'

// eslint-disable-next-line node/no-unpublished-require
const { test } = require('tap')
// eslint-disable-next-line node/no-unpublished-require
const ts = require('hemera-testsuite')
const build = require('./example')

let fastify = null
let server = null
const port = 4234

test('setup', (t) => {
  server = ts.start_server(port, (err) => {
    t.error(err)
    fastify = build({
      plugins: [],
      nats: `nats://127.0.0.1:${port}`
    })
    t.end()
  })
})

test('reply decorator', (t) => {
  t.plan(2)

  fastify.inject(
    {
      method: 'GET',
      url: '/reply?a=1&b=2'
    },
    (err, res) => {
      t.error(err)
      const payload = JSON.parse(res.payload)
      t.deepEqual(payload, { result: 3 })
    }
  )
})

test('fastify decorator', (t) => {
  t.plan(1)

  fastify.hemera
    .act({ topic: 'math', cmd: 'add', a: 1, b: 2 })
    .then((result) => {
      t.deepEqual(result.data, { result: 3 })
    })
    .catch(() => t.fail('should not land in catch'))
})

test('request decorator', (t) => {
  t.plan(2)

  fastify.inject(
    {
      method: 'GET',
      url: '/request?a=1&b=2'
    },
    (err, res) => {
      t.error(err)
      const payload = JSON.parse(res.payload)
      t.deepEqual(payload, { result: 3 })
    }
  )
})

test('teardown', function teardown(t) {
  fastify.close(() => {
    server.kill()
    t.end()
  })
})


<|start_filename|>plugin.test.js<|end_filename|>
'use strict'

// eslint-disable-next-line node/no-unpublished-require
const { test } = require('tap')
// eslint-disable-next-line node/no-unpublished-require
const ts = require('hemera-testsuite')
// eslint-disable-next-line node/no-unpublished-require
const hp = require('hemera-plugin')

const build = require('./example')

const port = 4250
let server = null

test('setup', (t) => {
  server = ts.start_server(port, (err) => {
    t.error(err)
    t.end()
  })
})

test('Should be able to register a plugin', (t) => {
  t.plan(3)

  const fastify = build({
    plugins: [
      hp(
        function myPlugin(hemera, options, done) {
          t.deepEqual(options, { a: 1 })
          done()
        },
        {
          name: 'myPlugin',
          options: { a: 1 }
        }
      )
    ],
    nats: `nats://127.0.0.1:${port}`
  })

  fastify.ready((readyErr) => {
    t.error(readyErr)
    fastify.close((closeError) => {
      t.error(closeError)
      t.end()
    })
  })
})

test('Should be able to register a plugin with custom options', (t) => {
  t.plan(3)

  const fastify = build({
    plugins: [
      {
        plugin: hp(
          function myPlugin(hemera, options, done) {
            t.deepEqual(options, { a: 1, b: 2 })
            done()
          },
          {
            name: 'myPlugin',
            options: { a: 1 }
          }
        ),
        options: {
          b: 2
        }
      }
    ],
    nats: `nats://127.0.0.1:${port}`
  })

  fastify.ready((readyErr) => {
    t.error(readyErr)
    fastify.close((closeError) => {
      t.error(closeError)
      t.end()
    })
  })
})

test('teardown', function teardown(t) {
  server.kill()
  t.end()
})
