Last active
January 23, 2017 03:51
-
-
Save mtth/e3e9b19d35fc86cae3a042ff641c0667 to your computer and use it in GitHub Desktop.
Promisified Avro service API
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
/* jshint esversion: 6, node: true */ | |
'use strict'; | |
/** Sample code using the promisified service API. */ | |
const avro = require('avsc'); | |
const promisify = require('./promisify'); // See file below. | |
// A sample service with a single message. Note that this message's error type | |
// will be a _wrapped_ union. | |
const service = avro.Service.forProtocol(avro.readProtocol(` | |
protocol EchoService { | |
error SomeError { string details; } | |
error AnotherError { int code = -1; } | |
string upperCase(string str) throws SomeError, AnotherError; | |
} | |
`), {wrapUnions: true}); | |
// Extract our custom errors' constructors to instantiate them idiomatically. | |
const SomeError = service.type('SomeError').recordConstructor; | |
const AnotherError = service.type('AnotherError').recordConstructor; | |
// A server for our service which accepts promise-based handlers. | |
const server = promisify.promisifyServer(service.createServer({silent: true})) | |
.onUpperCase(function (str) { | |
// OK case, we just return a value (if this computation was asynchronous, | |
// we would just return a promise instead). | |
return str.toUpperCase(); | |
// All the following lines are valid exceptional flows! | |
// | |
// Throwing a custom error: | |
// throw new SomeError('bar'); | |
// throw new AnotherError(); | |
// throw {AnotherError: new AnotherError()}; // Wrapping also works. | |
// | |
// Throwing a "system error": | |
// throw new Error('foo'); | |
// throw {string: 'baz'}; | |
}); | |
// Promise-based client. | |
const client = promisify.promisifyClient(service.createClient({server})); | |
client.upperCase('abc') | |
// OK case, the response is returned as expected. | |
.then(console.log) | |
// If a custom error is returned, it will be always unwrapped; this enables | |
// dispatching on the error's type: | |
.catch(SomeError, function (err) { | |
console.error(`got some error with details ${err.details}`); | |
}); |
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
/* jshint esversion: 6, node: true */ | |
'use strict'; | |
/** Helpers to provide a promise-based service API. */ | |
const Promise = require('bluebird'); | |
/** | |
* Transform (in-place) client RPC methods to return promises. | |
* | |
* Additionally, the new methods will unwrap any remote errors to enable the | |
* standard `.catch(MyError, fn)` bluebird idiom; see the attached example for | |
* details. | |
*/ | |
function promisifyClient(client) { | |
const fn = Promise.promisify(client.emitMessage); | |
client.emitMessage = function (name, req, opts) { | |
return fn.call(this, name, req, opts) | |
.catch(function (err) { | |
if (typeof err.unwrapped == 'function') { | |
const cause = err.unwrapped(); | |
if (cause.constructor.type.typeName === 'error') { | |
// Unwrap remote errors. | |
err = cause; | |
} | |
} | |
throw err; | |
}); | |
}; | |
return client; | |
} | |
/** | |
* Transform (in-place) a server to accept promise-based handlers. | |
* | |
* As a convenience, we also allow handlers to throw remote errors without | |
* having to wrap them first (even when the message's errors type is a wrapped | |
* union); see the attached example for details. | |
*/ | |
function promisifyServer(server) { | |
const fn = server.onMessage; | |
server.onMessage = function (name, handler) { | |
const msg = server.service.message(name); | |
if (!msg || msg.oneWay) { | |
// Nothing to do in this case, just use the same handler. | |
return fn.call(this, name, handler); | |
} | |
const wrappedHandler = Promise.method(handler); | |
return fn.call(this, name, function (req, cb) { | |
wrappedHandler.call(this, req) | |
.then(function (res) { cb(undefined, res); }) | |
.catch(function (err) { | |
if ( | |
err && typeof err.wrapped == 'function' && | |
msg.errorType.typeName === 'union:wrapped' | |
) { | |
// Allow handlers to throw custom error instances even when the | |
// message's error type is a wrapped union. | |
cb(err.wrapped()); | |
} else { | |
cb(err); | |
} | |
}); | |
}); | |
}; | |
return server; | |
} | |
module.exports = {promisifyClient, promisifyServer}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment