Merge pull request #18 from murgatroid99/node_client_constructor

Node client constructor
pull/25/merge
Tim Emiola 10 years ago
commit 699c0ec2e6
  1. 1
      src/node/server.js
  2. 90
      src/node/surface_client.js
  3. 144
      src/node/test/math_client_test.js

@ -73,6 +73,7 @@ function GrpcServerStream(call, options) {
* @param {Error} err The error object * @param {Error} err The error object
*/ */
function setStatus(err) { function setStatus(err) {
console.log('Server setting status to', err);
var code = grpc.status.INTERNAL; var code = grpc.status.INTERNAL;
var details = 'Unknown Error'; var details = 'Unknown Error';

@ -178,7 +178,7 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
/** /**
* Make a unary request with this method on the given channel with the given * Make a unary request with this method on the given channel with the given
* argument, callback, etc. * argument, callback, etc.
* @param {client.Channel} channel The channel on which to make the request * @this {SurfaceClient} Client object. Must have a channel member.
* @param {*} argument The argument to the call. Should be serializable with * @param {*} argument The argument to the call. Should be serializable with
* serialize * serialize
* @param {function(?Error, value=)} callback The callback to for when the * @param {function(?Error, value=)} callback The callback to for when the
@ -189,8 +189,8 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
* Defaults to infinite future * Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeUnaryRequest(channel, argument, callback, metadata, deadline) { function makeUnaryRequest(argument, callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, metadata, deadline);
var emitter = new EventEmitter(); var emitter = new EventEmitter();
forwardEvent(stream, emitter, 'status'); forwardEvent(stream, emitter, 'status');
forwardEvent(stream, emitter, 'metadata'); forwardEvent(stream, emitter, 'metadata');
@ -220,7 +220,7 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
/** /**
* Make a client stream request with this method on the given channel with the * Make a client stream request with this method on the given channel with the
* given callback, etc. * given callback, etc.
* @param {client.Channel} channel The channel on which to make the request * @this {SurfaceClient} Client object. Must have a channel member.
* @param {function(?Error, value=)} callback The callback to for when the * @param {function(?Error, value=)} callback The callback to for when the
* response is received * response is received
* @param {array=} metadata Array of metadata key/value pairs to add to the * @param {array=} metadata Array of metadata key/value pairs to add to the
@ -229,8 +229,8 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
* Defaults to infinite future * Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeClientStreamRequest(channel, callback, metadata, deadline) { function makeClientStreamRequest(callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientWritableObjectStream(stream, serialize, {}); var obj_stream = new ClientWritableObjectStream(stream, serialize, {});
stream.on('data', function forwardData(chunk) { stream.on('data', function forwardData(chunk) {
try { try {
@ -256,7 +256,7 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
/** /**
* Make a server stream request with this method on the given channel with the * Make a server stream request with this method on the given channel with the
* given argument, etc. * given argument, etc.
* @param {client.Channel} channel The channel on which to make the request * @this {SurfaceClient} Client object. Must have a channel member.
* @param {*} argument The argument to the call. Should be serializable with * @param {*} argument The argument to the call. Should be serializable with
* serialize * serialize
* @param {array=} metadata Array of metadata key/value pairs to add to the * @param {array=} metadata Array of metadata key/value pairs to add to the
@ -265,8 +265,8 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
* Defaults to infinite future * Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeServerStreamRequest(channel, argument, metadata, deadline) { function makeServerStreamRequest(argument, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientReadableObjectStream(stream, deserialize, {}); var obj_stream = new ClientReadableObjectStream(stream, deserialize, {});
stream.write(serialize(argument)); stream.write(serialize(argument));
stream.end(); stream.end();
@ -287,15 +287,15 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
function makeBidiStreamRequestFunction(method, serialize, deserialize) { function makeBidiStreamRequestFunction(method, serialize, deserialize) {
/** /**
* Make a bidirectional stream request with this method on the given channel. * Make a bidirectional stream request with this method on the given channel.
* @param {client.Channel} channel The channel on which to make the request * @this {SurfaceClient} Client object. Must have a channel member.
* @param {array=} metadata Array of metadata key/value pairs to add to the * @param {array=} metadata Array of metadata key/value pairs to add to the
* call * call
* @param {(number|Date)=} deadline The deadline for processing this request. * @param {(number|Date)=} deadline The deadline for processing this request.
* Defaults to infinite future * Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeBidiStreamRequest(channel, metadata, deadline) { function makeBidiStreamRequest(metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientBidiObjectStream(stream, var obj_stream = new ClientBidiObjectStream(stream,
serialize, serialize,
deserialize, deserialize,
@ -306,29 +306,63 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
} }
/** /**
* See docs for makeUnaryRequestFunction * Map with short names for each of the requester maker functions. Used in
* makeClientConstructor
*/ */
exports.makeUnaryRequestFunction = makeUnaryRequestFunction; var requester_makers = {
unary: makeUnaryRequestFunction,
server_stream: makeServerStreamRequestFunction,
client_stream: makeClientStreamRequestFunction,
bidi: makeBidiStreamRequestFunction
}
/** /**
* See docs for makeClientStreamRequestFunction * Creates a constructor for clients with a service defined by the methods
* object. The methods object has string keys and values of this form:
* {serialize: function, deserialize: function, client_stream: bool,
* server_stream: bool}
* @param {!Object<string, Object>} methods Method descriptor for each method
* the client should expose
* @param {string} prefix The prefix to prepend to each method name
* @return {function(string, Object)} New client constructor
*/ */
exports.makeClientStreamRequestFunction = makeClientStreamRequestFunction; function makeClientConstructor(methods, prefix) {
/**
* Create a client with the given methods
* @constructor
* @param {string} address The address of the server to connect to
* @param {Object} options Options to pass to the underlying channel
*/
function SurfaceClient(address, options) {
this.channel = new client.Channel(address, options);
}
/** _.each(methods, function(method, name) {
* See docs for makeServerStreamRequestFunction var method_type;
*/ if (method.client_stream) {
exports.makeServerStreamRequestFunction = makeServerStreamRequestFunction; if (method.server_stream) {
method_type = 'bidi';
} else {
method_type = 'client_stream';
}
} else {
if (method.server_stream) {
method_type = 'server_stream';
} else {
method_type = 'unary';
}
}
SurfaceClient.prototype[name] = requester_makers[method_type](
prefix + name,
method.serialize,
method.deserialize);
});
/** return SurfaceClient;
* See docs for makeBidiStreamRequestFunction }
*/
exports.makeBidiStreamRequestFunction = makeBidiStreamRequestFunction; exports.makeClientConstructor = makeClientConstructor;
/**
* See docs for client.Channel
*/
exports.Channel = client.Channel;
/** /**
* See docs for client.status * See docs for client.status
*/ */

@ -32,13 +32,14 @@
*/ */
var assert = require('assert'); var assert = require('assert');
var client = require('../surface_client.js');
var ProtoBuf = require('protobufjs'); var ProtoBuf = require('protobufjs');
var port_picker = require('../port_picker'); var port_picker = require('../port_picker');
var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto');
var math = builder.build('math'); var math = builder.build('math');
var client = require('../surface_client.js');
var makeConstructor = client.makeClientConstructor;
/** /**
* Get a function that deserializes a specific type of protobuf. * Get a function that deserializes a specific type of protobuf.
* @param {function()} cls The constructor of the message type to deserialize * @param {function()} cls The constructor of the message type to deserialize
@ -56,78 +57,60 @@ function deserializeCls(cls) {
} }
/** /**
* Serialize an object to a buffer * Get a function that serializes objects to a buffer by protobuf class.
* @param {*} arg The object to serialize * @param {function()} Cls The constructor of the message type to serialize
* @return {Buffer} The serialized object * @return {function(Cls):Buffer} The serialization function
*/ */
function serialize(arg) { function serializeCls(Cls) {
return new Buffer(arg.encode().toBuffer()); /**
* Serialize an object to a Buffer
* @param {Object} arg The object to serialize
* @return {Buffer} The serialized object
*/
return function serialize(arg) {
return new Buffer(new Cls(arg).encode().toBuffer());
};
} }
/** /* This function call creates a client constructor for clients that expose the
* Sends a Div request on the channel. * four specified methods. This specifies how to serialize messages that the
* @param {client.Channel} channel The channel on which to make the request * client sends and deserialize messages that the server sends, and whether the
* @param {DivArg} argument The argument to the call. Should be serializable * client or the server will send a stream of messages, for each method. This
* with serialize * also specifies a prefix that will be added to method names when sending them
* @param {function(?Error, value=)} The callback to for when the response is * on the wire. This function call and all of the preceding code in this file
* received * are intended to approximate what the generated code will look like for the
* @param {array=} Array of metadata key/value pairs to add to the call * math client */
* @param {(number|Date)=} deadline The deadline for processing this request. var MathClient = makeConstructor({
* Defaults to infinite future Div: {
* @return {EventEmitter} An event emitter for stream related events serialize: serializeCls(math.DivArgs),
*/ deserialize: deserializeCls(math.DivReply),
var div = client.makeUnaryRequestFunction( client_stream: false,
'/Math/Div', server_stream: false
serialize, },
deserializeCls(math.DivReply)); Fib: {
serialize: serializeCls(math.FibArgs),
/** deserialize: deserializeCls(math.Num),
* Sends a Fib request on the channel. client_stream: false,
* @param {client.Channel} channel The channel on which to make the request server_stream: true
* @param {*} argument The argument to the call. Should be serializable with },
* serialize Sum: {
* @param {array=} Array of metadata key/value pairs to add to the call serialize: serializeCls(math.Num),
* @param {(number|Date)=} deadline The deadline for processing this request. deserialize: deserializeCls(math.Num),
* Defaults to infinite future client_stream: true,
* @return {EventEmitter} An event emitter for stream related events server_stream: false
*/ },
var fib = client.makeServerStreamRequestFunction( DivMany: {
'/Math/Fib', serialize: serializeCls(math.DivArgs),
serialize, deserialize: deserializeCls(math.DivReply),
deserializeCls(math.Num)); client_stream: true,
server_stream: true
/** }
* Sends a Sum request on the channel. }, '/Math/');
* @param {client.Channel} channel The channel on which to make the request
* @param {function(?Error, value=)} The callback to for when the response is
* received
* @param {array=} Array of metadata key/value pairs to add to the call
* @param {(number|Date)=} deadline The deadline for processing this request.
* Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events
*/
var sum = client.makeClientStreamRequestFunction(
'/Math/Sum',
serialize,
deserializeCls(math.Num));
/**
* Sends a DivMany request on the channel.
* @param {client.Channel} channel The channel on which to make the request
* @param {array=} Array of metadata key/value pairs to add to the call
* @param {(number|Date)=} deadline The deadline for processing this request.
* Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events
*/
var divMany = client.makeBidiStreamRequestFunction(
'/Math/DivMany',
serialize,
deserializeCls(math.DivReply));
/** /**
* Channel to use to make requests to a running server. * Channel to use to make requests to a running server.
*/ */
var channel; var math_client;
/** /**
* Server to test against * Server to test against
@ -139,7 +122,7 @@ describe('Math client', function() {
before(function(done) { before(function(done) {
port_picker.nextAvailablePort(function(port) { port_picker.nextAvailablePort(function(port) {
server.bind(port).listen(); server.bind(port).listen();
channel = new client.Channel(port); math_client = new MathClient(port);
done(); done();
}); });
}); });
@ -147,11 +130,11 @@ describe('Math client', function() {
server.shutdown(); server.shutdown();
}); });
it('should handle a single request', function(done) { it('should handle a single request', function(done) {
var arg = new math.DivArgs({dividend: 7, divisor: 4}); var arg = {dividend: 7, divisor: 4};
var call = div(channel, arg, function handleDivResult(err, value) { var call = math_client.Div(arg, function handleDivResult(err, value) {
assert.ifError(err); assert.ifError(err);
assert.equal(value.get('quotient'), 1); assert.equal(value.quotient, 1);
assert.equal(value.get('remainder'), 3); assert.equal(value.remainder, 3);
}); });
call.on('status', function checkStatus(status) { call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, client.status.OK); assert.strictEqual(status.code, client.status.OK);
@ -159,12 +142,11 @@ describe('Math client', function() {
}); });
}); });
it('should handle a server streaming request', function(done) { it('should handle a server streaming request', function(done) {
var arg = new math.FibArgs({limit: 7}); var call = math_client.Fib({limit: 7});
var call = fib(channel, arg);
var expected_results = [1, 1, 2, 3, 5, 8, 13]; var expected_results = [1, 1, 2, 3, 5, 8, 13];
var next_expected = 0; var next_expected = 0;
call.on('data', function checkResponse(value) { call.on('data', function checkResponse(value) {
assert.equal(value.get('num'), expected_results[next_expected]); assert.equal(value.num, expected_results[next_expected]);
next_expected += 1; next_expected += 1;
}); });
call.on('status', function checkStatus(status) { call.on('status', function checkStatus(status) {
@ -173,12 +155,12 @@ describe('Math client', function() {
}); });
}); });
it('should handle a client streaming request', function(done) { it('should handle a client streaming request', function(done) {
var call = sum(channel, function handleSumResult(err, value) { var call = math_client.Sum(function handleSumResult(err, value) {
assert.ifError(err); assert.ifError(err);
assert.equal(value.get('num'), 21); assert.equal(value.num, 21);
}); });
for (var i = 0; i < 7; i++) { for (var i = 0; i < 7; i++) {
call.write(new math.Num({'num': i})); call.write({'num': i});
} }
call.end(); call.end();
call.on('status', function checkStatus(status) { call.on('status', function checkStatus(status) {
@ -188,17 +170,17 @@ describe('Math client', function() {
}); });
it('should handle a bidirectional streaming request', function(done) { it('should handle a bidirectional streaming request', function(done) {
function checkResponse(index, value) { function checkResponse(index, value) {
assert.equal(value.get('quotient'), index); assert.equal(value.quotient, index);
assert.equal(value.get('remainder'), 1); assert.equal(value.remainder, 1);
} }
var call = divMany(channel); var call = math_client.DivMany();
var response_index = 0; var response_index = 0;
call.on('data', function(value) { call.on('data', function(value) {
checkResponse(response_index, value); checkResponse(response_index, value);
response_index += 1; response_index += 1;
}); });
for (var i = 0; i < 7; i++) { for (var i = 0; i < 7; i++) {
call.write(new math.DivArgs({dividend: 2 * i + 1, divisor: 2})); call.write({dividend: 2 * i + 1, divisor: 2});
} }
call.end(); call.end();
call.on('status', function checkStatus(status) { call.on('status', function checkStatus(status) {

Loading…
Cancel
Save