aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Tim Emiola <tbetbetbe@users.noreply.github.com>2015-03-23 19:33:16 -0700
committerGravatar Tim Emiola <tbetbetbe@users.noreply.github.com>2015-03-23 19:33:16 -0700
commit5a8cfdd8af010c490b4031893b659b161daff6a9 (patch)
tree84a36cc98c100f9a29ae16f47e0850a92ef4172c
parent71d2f559d5b10c3c5a468bc2991769cd59e364a3 (diff)
parent94c5e7135b74a7f5ddb578f7482bb3f09efbe81c (diff)
Merge pull request #1097 from murgatroid99/node_general_interface
Node general interface
-rw-r--r--src/node/index.js8
-rw-r--r--src/node/src/client.js53
-rw-r--r--src/node/src/common.js23
-rw-r--r--src/node/src/server.js99
-rw-r--r--src/node/test/surface_test.js53
5 files changed, 173 insertions, 63 deletions
diff --git a/src/node/index.js b/src/node/index.js
index ad3dd96af7..0b768edc6b 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -56,7 +56,7 @@ function loadObject(value) {
});
return result;
} else if (value.className === 'Service') {
- return client.makeClientConstructor(value);
+ return client.makeProtobufClientConstructor(value);
} else if (value.className === 'Message' || value.className === 'Enum') {
return value.build();
} else {
@@ -119,7 +119,7 @@ exports.load = load;
/**
* See docs for server.makeServerConstructor
*/
-exports.buildServer = server.makeServerConstructor;
+exports.buildServer = server.makeProtobufServerConstructor;
/**
* Status name to code number mapping
@@ -141,3 +141,7 @@ exports.Credentials = grpc.Credentials;
exports.ServerCredentials = grpc.ServerCredentials;
exports.getGoogleAuthDelegate = getGoogleAuthDelegate;
+
+exports.makeGenericClientConstructor = client.makeClientConstructor;
+
+exports.makeGenericServerConstructor = server.makeServerConstructor;
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 54b8dbdc9c..c46f7d0526 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -35,9 +35,6 @@
var _ = require('underscore');
-var capitalize = require('underscore.string/capitalize');
-var decapitalize = require('underscore.string/decapitalize');
-
var grpc = require('bindings')('grpc.node');
var common = require('./common.js');
@@ -463,13 +460,18 @@ var requester_makers = {
};
/**
- * Creates a constructor for clients for the given service
- * @param {ProtoBuf.Reflect.Service} service The service to generate a client
- * for
+ * Creates a constructor for a client with the given methods. The methods object
+ * maps method name to an object with the following keys:
+ * path: The path on the server for accessing the method. For example, for
+ * protocol buffers, we use "/service_name/method_name"
+ * requestStream: bool indicating whether the client sends a stream
+ * resonseStream: bool indicating whether the server sends a stream
+ * requestSerialize: function to serialize request objects
+ * responseDeserialize: function to deserialize response objects
+ * @param {Object} methods An object mapping method names to method attributes
* @return {function(string, Object)} New client constructor
*/
-function makeClientConstructor(service) {
- var prefix = '/' + common.fullyQualifiedName(service) + '/';
+function makeClientConstructor(methods) {
/**
* Create a client with the given methods
* @constructor
@@ -489,30 +491,41 @@ function makeClientConstructor(service) {
this.channel = new grpc.Channel(address, options);
}
- _.each(service.children, function(method) {
+ _.each(methods, function(attrs, name) {
var method_type;
- if (method.requestStream) {
- if (method.responseStream) {
+ if (attrs.requestStream) {
+ if (attrs.responseStream) {
method_type = 'bidi';
} else {
method_type = 'client_stream';
}
} else {
- if (method.responseStream) {
+ if (attrs.responseStream) {
method_type = 'server_stream';
} else {
method_type = 'unary';
}
}
- var serialize = common.serializeCls(method.resolvedRequestType.build());
- var deserialize = common.deserializeCls(
- method.resolvedResponseType.build());
- Client.prototype[decapitalize(method.name)] = requester_makers[method_type](
- prefix + capitalize(method.name), serialize, deserialize);
- Client.prototype[decapitalize(method.name)].serialize = serialize;
- Client.prototype[decapitalize(method.name)].deserialize = deserialize;
+ var serialize = attrs.requestSerialize;
+ var deserialize = attrs.responseDeserialize;
+ Client.prototype[name] = requester_makers[method_type](
+ attrs.path, serialize, deserialize);
+ Client.prototype[name].serialize = serialize;
+ Client.prototype[name].deserialize = deserialize;
});
+ return Client;
+}
+
+/**
+ * Creates a constructor for clients for the given service
+ * @param {ProtoBuf.Reflect.Service} service The service to generate a client
+ * for
+ * @return {function(string, Object)} New client constructor
+ */
+function makeProtobufClientConstructor(service) {
+ var method_attrs = common.getProtobufServiceAttrs(service);
+ var Client = makeClientConstructor(method_attrs);
Client.service = service;
return Client;
@@ -520,6 +533,8 @@ function makeClientConstructor(service) {
exports.makeClientConstructor = makeClientConstructor;
+exports.makeProtobufClientConstructor = makeProtobufClientConstructor;
+
/**
* See docs for client.status
*/
diff --git a/src/node/src/common.js b/src/node/src/common.js
index eec8f0f987..55a6b13782 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -36,6 +36,7 @@
var _ = require('underscore');
var capitalize = require('underscore.string/capitalize');
+var decapitalize = require('underscore.string/decapitalize');
/**
* Get a function that deserializes a specific type of protobuf.
@@ -110,6 +111,26 @@ function wrapIgnoreNull(func) {
}
/**
+ * Return a map from method names to method attributes for the service.
+ * @param {ProtoBuf.Reflect.Service} service The service to get attributes for
+ * @return {Object} The attributes map
+ */
+function getProtobufServiceAttrs(service) {
+ var prefix = '/' + fullyQualifiedName(service) + '/';
+ return _.object(_.map(service.children, function(method) {
+ return [decapitalize(method.name), {
+ path: prefix + capitalize(method.name),
+ requestStream: method.requestStream,
+ responseStream: method.responseStream,
+ requestSerialize: serializeCls(method.resolvedRequestType.build()),
+ requestDeserialize: deserializeCls(method.resolvedRequestType.build()),
+ responseSerialize: serializeCls(method.resolvedResponseType.build()),
+ responseDeserialize: deserializeCls(method.resolvedResponseType.build())
+ }];
+ }));
+}
+
+/**
* See docs for deserializeCls
*/
exports.deserializeCls = deserializeCls;
@@ -128,3 +149,5 @@ exports.fullyQualifiedName = fullyQualifiedName;
* See docs for wrapIgnoreNull
*/
exports.wrapIgnoreNull = wrapIgnoreNull;
+
+exports.getProtobufServiceAttrs = getProtobufServiceAttrs;
diff --git a/src/node/src/server.js b/src/node/src/server.js
index b72d110666..8a26a43606 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -35,9 +35,6 @@
var _ = require('underscore');
-var capitalize = require('underscore.string/capitalize');
-var decapitalize = require('underscore.string/decapitalize');
-
var grpc = require('bindings')('grpc.node');
var common = require('./common');
@@ -532,26 +529,20 @@ Server.prototype.bind = function(port, creds) {
};
/**
- * Creates a constructor for servers 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} methods Method descriptor for each method the server should
- * expose
- * @param {string} prefix The prefex to prepend to each method name
- * @return {function(Object, Object)} New server constructor
+ * Create a constructor for servers with services defined by service_attr_map.
+ * That is an object that maps (namespaced) service names to objects that in
+ * turn map method names to objects with the following keys:
+ * path: The path on the server for accessing the method. For example, for
+ * protocol buffers, we use "/service_name/method_name"
+ * requestStream: bool indicating whether the client sends a stream
+ * resonseStream: bool indicating whether the server sends a stream
+ * requestDeserialize: function to deserialize request objects
+ * responseSerialize: function to serialize response objects
+ * @param {Object} service_attr_map An object mapping service names to method
+ * attribute map objects
+ * @return {function(Object, function, Object=)} New server constructor
*/
-function makeServerConstructor(services) {
- var qual_names = [];
- _.each(services, function(service) {
- _.each(service.children, function(method) {
- var name = common.fullyQualifiedName(method);
- if (_.indexOf(qual_names, name) !== -1) {
- throw new Error('Method ' + name + ' exposed by more than one service');
- }
- qual_names.push(name);
- });
- });
+function makeServerConstructor(service_attr_map) {
/**
* Create a server with the given handlers for all of the methods.
* @constructor
@@ -565,41 +556,34 @@ function makeServerConstructor(services) {
function SurfaceServer(service_handlers, getMetadata, options) {
var server = new Server(getMetadata, options);
this.inner_server = server;
- _.each(services, function(service) {
- var service_name = common.fullyQualifiedName(service);
+ _.each(service_attr_map, function(service_attrs, service_name) {
if (service_handlers[service_name] === undefined) {
throw new Error('Handlers for service ' +
service_name + ' not provided.');
}
- var prefix = '/' + common.fullyQualifiedName(service) + '/';
- _.each(service.children, function(method) {
+ _.each(service_attrs, function(attrs, name) {
var method_type;
- if (method.requestStream) {
- if (method.responseStream) {
+ if (attrs.requestStream) {
+ if (attrs.responseStream) {
method_type = 'bidi';
} else {
method_type = 'client_stream';
}
} else {
- if (method.responseStream) {
+ if (attrs.responseStream) {
method_type = 'server_stream';
} else {
method_type = 'unary';
}
}
- if (service_handlers[service_name][decapitalize(method.name)] ===
- undefined) {
- throw new Error('Method handler for ' +
- common.fullyQualifiedName(method) + ' not provided.');
+ if (service_handlers[service_name][name] === undefined) {
+ throw new Error('Method handler for ' + attrs.path +
+ ' not provided.');
}
- var serialize = common.serializeCls(
- method.resolvedResponseType.build());
- var deserialize = common.deserializeCls(
- method.resolvedRequestType.build());
- server.register(
- prefix + capitalize(method.name),
- service_handlers[service_name][decapitalize(method.name)],
- serialize, deserialize, method_type);
+ var serialize = attrs.responseSerialize;
+ var deserialize = attrs.requestDeserialize;
+ server.register(attrs.path, service_handlers[service_name][name],
+ serialize, deserialize, method_type);
});
}, this);
}
@@ -636,6 +620,39 @@ function makeServerConstructor(services) {
}
/**
+ * Create a constructor for servers that serve the given services.
+ * @param {Array<ProtoBuf.Reflect.Service>} services The services that the
+ * servers will serve
+ * @return {function(Object, function, Object=)} New server constructor
+ */
+function makeProtobufServerConstructor(services) {
+ var qual_names = [];
+ var service_attr_map = {};
+ _.each(services, function(service) {
+ var service_name = common.fullyQualifiedName(service);
+ _.each(service.children, function(method) {
+ var name = common.fullyQualifiedName(method);
+ if (_.indexOf(qual_names, name) !== -1) {
+ throw new Error('Method ' + name + ' exposed by more than one service');
+ }
+ qual_names.push(name);
+ });
+ var method_attrs = common.getProtobufServiceAttrs(service);
+ if (!service_attr_map.hasOwnProperty(service_name)) {
+ service_attr_map[service_name] = {};
+ }
+ service_attr_map[service_name] = _.extend(service_attr_map[service_name],
+ method_attrs);
+ });
+ return makeServerConstructor(service_attr_map);
+}
+
+/**
* See documentation for makeServerConstructor
*/
exports.makeServerConstructor = makeServerConstructor;
+
+/**
+ * See documentation for makeProtobufServerConstructor
+ */
+exports.makeProtobufServerConstructor = makeProtobufServerConstructor;
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index 91d8197bee..96b47815e1 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -45,6 +45,8 @@ var math_proto = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto');
var mathService = math_proto.lookup('math.Math');
+var capitalize = require('underscore.string/capitalize');
+
describe('Surface server constructor', function() {
it('Should fail with conflicting method names', function() {
assert.throws(function() {
@@ -75,6 +77,55 @@ describe('Surface server constructor', function() {
}, /math.Math/);
});
});
+describe('Generic client and server', function() {
+ function toString(val) {
+ return val.toString();
+ }
+ function toBuffer(str) {
+ return new Buffer(str);
+ }
+ var string_service_attrs = {
+ 'capitalize' : {
+ path: '/string/capitalize',
+ requestStream: false,
+ responseStream: false,
+ requestSerialize: toBuffer,
+ requestDeserialize: toString,
+ responseSerialize: toBuffer,
+ responseDeserialize: toString
+ }
+ };
+ describe('String client and server', function() {
+ var client;
+ var server;
+ before(function() {
+ var Server = grpc.makeGenericServerConstructor({
+ string: string_service_attrs
+ });
+ server = new Server({
+ string: {
+ capitalize: function(call, callback) {
+ callback(null, capitalize(call.request));
+ }
+ }
+ });
+ var port = server.bind('localhost:0');
+ server.listen();
+ var Client = grpc.makeGenericClientConstructor(string_service_attrs);
+ client = new Client('localhost:' + port);
+ });
+ after(function() {
+ server.shutdown();
+ });
+ it('Should respond with a capitalized string', function(done) {
+ client.capitalize('abc', function(err, response) {
+ assert.ifError(err);
+ assert.strictEqual(response, 'Abc');
+ done();
+ });
+ });
+ });
+});
describe('Cancelling surface client', function() {
var client;
var server;
@@ -89,7 +140,7 @@ describe('Cancelling surface client', function() {
}
});
var port = server.bind('localhost:0');
- var Client = surface_client.makeClientConstructor(mathService);
+ var Client = surface_client.makeProtobufClientConstructor(mathService);
client = new Client('localhost:' + port);
});
after(function() {