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
*/
function setStatus(err) {
console.log('Server setting status to', err);
var code = grpc.status.INTERNAL;
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
* 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
* serialize
* @param {function(?Error, value=)} callback The callback to for when the
@ -189,8 +189,8 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
* Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events
*/
function makeUnaryRequest(channel, argument, callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
function makeUnaryRequest(argument, callback, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline);
var emitter = new EventEmitter();
forwardEvent(stream, emitter, 'status');
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
* 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
* response is received
* @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
* @return {EventEmitter} An event emitter for stream related events
*/
function makeClientStreamRequest(channel, callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
function makeClientStreamRequest(callback, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientWritableObjectStream(stream, serialize, {});
stream.on('data', function forwardData(chunk) {
try {
@ -256,7 +256,7 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
/**
* Make a server stream request with this method on the given channel with the
* 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
* serialize
* @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
* @return {EventEmitter} An event emitter for stream related events
*/
function makeServerStreamRequest(channel, argument, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
function makeServerStreamRequest(argument, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientReadableObjectStream(stream, deserialize, {});
stream.write(serialize(argument));
stream.end();
@ -287,15 +287,15 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
function makeBidiStreamRequestFunction(method, serialize, deserialize) {
/**
* 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
* call
* @param {(number|Date)=} deadline The deadline for processing this request.
* Defaults to infinite future
* @return {EventEmitter} An event emitter for stream related events
*/
function makeBidiStreamRequest(channel, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
function makeBidiStreamRequest(metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline);
var obj_stream = new ClientBidiObjectStream(stream,
serialize,
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);
}
/**
* See docs for makeServerStreamRequestFunction
*/
exports.makeServerStreamRequestFunction = makeServerStreamRequestFunction;
_.each(methods, function(method, name) {
var method_type;
if (method.client_stream) {
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);
});
/**
* See docs for makeBidiStreamRequestFunction
*/
exports.makeBidiStreamRequestFunction = makeBidiStreamRequestFunction;
return SurfaceClient;
}
exports.makeClientConstructor = makeClientConstructor;
/**
* See docs for client.Channel
*/
exports.Channel = client.Channel;
/**
* See docs for client.status
*/

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

Loading…
Cancel
Save