mirror of https://github.com/grpc/grpc-node.git
Merge pull request #1097 from murgatroid99/node_general_interface
Node general interface
This commit is contained in:
commit
af3c157d06
8
index.js
8
index.js
|
@ -56,7 +56,7 @@ function loadObject(value) {
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
} else if (value.className === 'Service') {
|
} else if (value.className === 'Service') {
|
||||||
return client.makeClientConstructor(value);
|
return client.makeProtobufClientConstructor(value);
|
||||||
} else if (value.className === 'Message' || value.className === 'Enum') {
|
} else if (value.className === 'Message' || value.className === 'Enum') {
|
||||||
return value.build();
|
return value.build();
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,7 +119,7 @@ exports.load = load;
|
||||||
/**
|
/**
|
||||||
* See docs for server.makeServerConstructor
|
* See docs for server.makeServerConstructor
|
||||||
*/
|
*/
|
||||||
exports.buildServer = server.makeServerConstructor;
|
exports.buildServer = server.makeProtobufServerConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status name to code number mapping
|
* Status name to code number mapping
|
||||||
|
@ -141,3 +141,7 @@ exports.Credentials = grpc.Credentials;
|
||||||
exports.ServerCredentials = grpc.ServerCredentials;
|
exports.ServerCredentials = grpc.ServerCredentials;
|
||||||
|
|
||||||
exports.getGoogleAuthDelegate = getGoogleAuthDelegate;
|
exports.getGoogleAuthDelegate = getGoogleAuthDelegate;
|
||||||
|
|
||||||
|
exports.makeGenericClientConstructor = client.makeClientConstructor;
|
||||||
|
|
||||||
|
exports.makeGenericServerConstructor = server.makeServerConstructor;
|
||||||
|
|
|
@ -35,9 +35,6 @@
|
||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var capitalize = require('underscore.string/capitalize');
|
|
||||||
var decapitalize = require('underscore.string/decapitalize');
|
|
||||||
|
|
||||||
var grpc = require('bindings')('grpc.node');
|
var grpc = require('bindings')('grpc.node');
|
||||||
|
|
||||||
var common = require('./common.js');
|
var common = require('./common.js');
|
||||||
|
@ -463,13 +460,18 @@ var requester_makers = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a constructor for clients for the given service
|
* Creates a constructor for a client with the given methods. The methods object
|
||||||
* @param {ProtoBuf.Reflect.Service} service The service to generate a client
|
* maps method name to an object with the following keys:
|
||||||
* for
|
* 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
|
* @return {function(string, Object)} New client constructor
|
||||||
*/
|
*/
|
||||||
function makeClientConstructor(service) {
|
function makeClientConstructor(methods) {
|
||||||
var prefix = '/' + common.fullyQualifiedName(service) + '/';
|
|
||||||
/**
|
/**
|
||||||
* Create a client with the given methods
|
* Create a client with the given methods
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -489,30 +491,41 @@ function makeClientConstructor(service) {
|
||||||
this.channel = new grpc.Channel(address, options);
|
this.channel = new grpc.Channel(address, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(service.children, function(method) {
|
_.each(methods, function(attrs, name) {
|
||||||
var method_type;
|
var method_type;
|
||||||
if (method.requestStream) {
|
if (attrs.requestStream) {
|
||||||
if (method.responseStream) {
|
if (attrs.responseStream) {
|
||||||
method_type = 'bidi';
|
method_type = 'bidi';
|
||||||
} else {
|
} else {
|
||||||
method_type = 'client_stream';
|
method_type = 'client_stream';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (method.responseStream) {
|
if (attrs.responseStream) {
|
||||||
method_type = 'server_stream';
|
method_type = 'server_stream';
|
||||||
} else {
|
} else {
|
||||||
method_type = 'unary';
|
method_type = 'unary';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var serialize = common.serializeCls(method.resolvedRequestType.build());
|
var serialize = attrs.requestSerialize;
|
||||||
var deserialize = common.deserializeCls(
|
var deserialize = attrs.responseDeserialize;
|
||||||
method.resolvedResponseType.build());
|
Client.prototype[name] = requester_makers[method_type](
|
||||||
Client.prototype[decapitalize(method.name)] = requester_makers[method_type](
|
attrs.path, serialize, deserialize);
|
||||||
prefix + capitalize(method.name), serialize, deserialize);
|
Client.prototype[name].serialize = serialize;
|
||||||
Client.prototype[decapitalize(method.name)].serialize = serialize;
|
Client.prototype[name].deserialize = deserialize;
|
||||||
Client.prototype[decapitalize(method.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;
|
Client.service = service;
|
||||||
|
|
||||||
return Client;
|
return Client;
|
||||||
|
@ -520,6 +533,8 @@ function makeClientConstructor(service) {
|
||||||
|
|
||||||
exports.makeClientConstructor = makeClientConstructor;
|
exports.makeClientConstructor = makeClientConstructor;
|
||||||
|
|
||||||
|
exports.makeProtobufClientConstructor = makeProtobufClientConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See docs for client.status
|
* See docs for client.status
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var capitalize = require('underscore.string/capitalize');
|
var capitalize = require('underscore.string/capitalize');
|
||||||
|
var decapitalize = require('underscore.string/decapitalize');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a function that deserializes a specific type of protobuf.
|
* Get a function that deserializes a specific type of protobuf.
|
||||||
|
@ -109,6 +110,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
|
* See docs for deserializeCls
|
||||||
*/
|
*/
|
||||||
|
@ -128,3 +149,5 @@ exports.fullyQualifiedName = fullyQualifiedName;
|
||||||
* See docs for wrapIgnoreNull
|
* See docs for wrapIgnoreNull
|
||||||
*/
|
*/
|
||||||
exports.wrapIgnoreNull = wrapIgnoreNull;
|
exports.wrapIgnoreNull = wrapIgnoreNull;
|
||||||
|
|
||||||
|
exports.getProtobufServiceAttrs = getProtobufServiceAttrs;
|
||||||
|
|
|
@ -35,9 +35,6 @@
|
||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var capitalize = require('underscore.string/capitalize');
|
|
||||||
var decapitalize = require('underscore.string/decapitalize');
|
|
||||||
|
|
||||||
var grpc = require('bindings')('grpc.node');
|
var grpc = require('bindings')('grpc.node');
|
||||||
|
|
||||||
var common = require('./common');
|
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
|
* Create a constructor for servers with services defined by service_attr_map.
|
||||||
* object. The methods object has string keys and values of this form:
|
* That is an object that maps (namespaced) service names to objects that in
|
||||||
* {serialize: function, deserialize: function, client_stream: bool,
|
* turn map method names to objects with the following keys:
|
||||||
* server_stream: bool}
|
* path: The path on the server for accessing the method. For example, for
|
||||||
* @param {Object} methods Method descriptor for each method the server should
|
* protocol buffers, we use "/service_name/method_name"
|
||||||
* expose
|
* requestStream: bool indicating whether the client sends a stream
|
||||||
* @param {string} prefix The prefex to prepend to each method name
|
* resonseStream: bool indicating whether the server sends a stream
|
||||||
* @return {function(Object, Object)} New server constructor
|
* 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) {
|
function makeServerConstructor(service_attr_map) {
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
* Create a server with the given handlers for all of the methods.
|
* Create a server with the given handlers for all of the methods.
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -565,41 +556,34 @@ function makeServerConstructor(services) {
|
||||||
function SurfaceServer(service_handlers, getMetadata, options) {
|
function SurfaceServer(service_handlers, getMetadata, options) {
|
||||||
var server = new Server(getMetadata, options);
|
var server = new Server(getMetadata, options);
|
||||||
this.inner_server = server;
|
this.inner_server = server;
|
||||||
_.each(services, function(service) {
|
_.each(service_attr_map, function(service_attrs, service_name) {
|
||||||
var service_name = common.fullyQualifiedName(service);
|
|
||||||
if (service_handlers[service_name] === undefined) {
|
if (service_handlers[service_name] === undefined) {
|
||||||
throw new Error('Handlers for service ' +
|
throw new Error('Handlers for service ' +
|
||||||
service_name + ' not provided.');
|
service_name + ' not provided.');
|
||||||
}
|
}
|
||||||
var prefix = '/' + common.fullyQualifiedName(service) + '/';
|
_.each(service_attrs, function(attrs, name) {
|
||||||
_.each(service.children, function(method) {
|
|
||||||
var method_type;
|
var method_type;
|
||||||
if (method.requestStream) {
|
if (attrs.requestStream) {
|
||||||
if (method.responseStream) {
|
if (attrs.responseStream) {
|
||||||
method_type = 'bidi';
|
method_type = 'bidi';
|
||||||
} else {
|
} else {
|
||||||
method_type = 'client_stream';
|
method_type = 'client_stream';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (method.responseStream) {
|
if (attrs.responseStream) {
|
||||||
method_type = 'server_stream';
|
method_type = 'server_stream';
|
||||||
} else {
|
} else {
|
||||||
method_type = 'unary';
|
method_type = 'unary';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (service_handlers[service_name][decapitalize(method.name)] ===
|
if (service_handlers[service_name][name] === undefined) {
|
||||||
undefined) {
|
throw new Error('Method handler for ' + attrs.path +
|
||||||
throw new Error('Method handler for ' +
|
' not provided.');
|
||||||
common.fullyQualifiedName(method) + ' not provided.');
|
|
||||||
}
|
}
|
||||||
var serialize = common.serializeCls(
|
var serialize = attrs.responseSerialize;
|
||||||
method.resolvedResponseType.build());
|
var deserialize = attrs.requestDeserialize;
|
||||||
var deserialize = common.deserializeCls(
|
server.register(attrs.path, service_handlers[service_name][name],
|
||||||
method.resolvedRequestType.build());
|
serialize, deserialize, method_type);
|
||||||
server.register(
|
|
||||||
prefix + capitalize(method.name),
|
|
||||||
service_handlers[service_name][decapitalize(method.name)],
|
|
||||||
serialize, deserialize, method_type);
|
|
||||||
});
|
});
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
@ -635,7 +619,40 @@ function makeServerConstructor(services) {
|
||||||
return SurfaceServer;
|
return SurfaceServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* See documentation for makeServerConstructor
|
||||||
*/
|
*/
|
||||||
exports.makeServerConstructor = makeServerConstructor;
|
exports.makeServerConstructor = makeServerConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See documentation for makeProtobufServerConstructor
|
||||||
|
*/
|
||||||
|
exports.makeProtobufServerConstructor = makeProtobufServerConstructor;
|
||||||
|
|
|
@ -45,6 +45,8 @@ var math_proto = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto');
|
||||||
|
|
||||||
var mathService = math_proto.lookup('math.Math');
|
var mathService = math_proto.lookup('math.Math');
|
||||||
|
|
||||||
|
var capitalize = require('underscore.string/capitalize');
|
||||||
|
|
||||||
describe('Surface server constructor', function() {
|
describe('Surface server constructor', function() {
|
||||||
it('Should fail with conflicting method names', function() {
|
it('Should fail with conflicting method names', function() {
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
|
@ -75,6 +77,55 @@ describe('Surface server constructor', function() {
|
||||||
}, /math.Math/);
|
}, /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() {
|
describe('Cancelling surface client', function() {
|
||||||
var client;
|
var client;
|
||||||
var server;
|
var server;
|
||||||
|
@ -89,7 +140,7 @@ describe('Cancelling surface client', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var port = server.bind('localhost:0');
|
var port = server.bind('localhost:0');
|
||||||
var Client = surface_client.makeClientConstructor(mathService);
|
var Client = surface_client.makeProtobufClientConstructor(mathService);
|
||||||
client = new Client('localhost:' + port);
|
client = new Client('localhost:' + port);
|
||||||
});
|
});
|
||||||
after(function() {
|
after(function() {
|
||||||
|
|
Loading…
Reference in New Issue