Follow Node's callback-last convention for client calls

pull/5900/head
murgatroid99 9 years ago
parent b08ee7a260
commit 9a0c74116f
  1. 1
      package.json
  2. 10
      src/node/interop/interop_client.js
  3. 91
      src/node/src/client.js
  4. 19
      src/node/test/credentials_test.js
  5. 42
      src/node/test/surface_test.js
  6. 1
      templates/package.json.template

@ -27,6 +27,7 @@
}, },
"bundledDependencies": ["node-pre-gyp"], "bundledDependencies": ["node-pre-gyp"],
"dependencies": { "dependencies": {
"arguejs": "^0.2.3",
"lodash": "^3.9.3", "lodash": "^3.9.3",
"nan": "^2.0.0", "nan": "^2.0.0",
"protobufjs": "^4.0.0" "protobufjs": "^4.0.0"

@ -286,7 +286,7 @@ function cancelAfterFirstResponse(client, done) {
function timeoutOnSleepingServer(client, done) { function timeoutOnSleepingServer(client, done) {
var deadline = new Date(); var deadline = new Date();
deadline.setMilliseconds(deadline.getMilliseconds() + 1); deadline.setMilliseconds(deadline.getMilliseconds() + 1);
var call = client.fullDuplexCall(null, {deadline: deadline}); var call = client.fullDuplexCall({deadline: deadline});
call.write({ call.write({
payload: {body: zeroBuffer(27182)} payload: {body: zeroBuffer(27182)}
}); });
@ -316,10 +316,10 @@ function customMetadata(client, done) {
body: zeroBuffer(271828) body: zeroBuffer(271828)
} }
}; };
var unary = client.unaryCall(arg, function(err, resp) { var unary = client.unaryCall(arg, metadata, function(err, resp) {
assert.ifError(err); assert.ifError(err);
done(); done();
}, metadata); });
unary.on('metadata', function(metadata) { unary.on('metadata', function(metadata) {
assert.deepEqual(metadata.get(ECHO_INITIAL_KEY), assert.deepEqual(metadata.get(ECHO_INITIAL_KEY),
['test_initial_metadata_value']); ['test_initial_metadata_value']);
@ -455,14 +455,14 @@ function perRpcAuthTest(client, done, extra) {
credential = credential.createScoped(scope); credential = credential.createScoped(scope);
} }
var creds = grpc.credentials.createFromGoogleCredential(credential); var creds = grpc.credentials.createFromGoogleCredential(credential);
client.unaryCall(arg, function(err, resp) { client.unaryCall(arg, {credentials: creds}, function(err, resp) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(resp.username, SERVICE_ACCOUNT_EMAIL); assert.strictEqual(resp.username, SERVICE_ACCOUNT_EMAIL);
assert(extra.oauth_scope.indexOf(resp.oauth_scope) > -1); assert(extra.oauth_scope.indexOf(resp.oauth_scope) > -1);
if (done) { if (done) {
done(); done();
} }
}, null, {credentials: creds}); });
}); });
} }

@ -50,6 +50,7 @@
'use strict'; 'use strict';
var _ = require('lodash'); var _ = require('lodash');
var arguejs = require('arguejs');
var grpc = require('./grpc_extension'); var grpc = require('./grpc_extension');
@ -353,21 +354,23 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
* @this {Client} Client object. Must have a channel member. * @this {Client} 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
* response is received
* @param {Metadata=} metadata Metadata to add to the call * @param {Metadata=} metadata Metadata to add to the call
* @param {Object=} options Options map * @param {Object=} options Options map
* @param {function(?Error, value=)} callback The callback to for when the
* response is received
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeUnaryRequest(argument, callback, metadata, options) { function makeUnaryRequest(argument, metadata, options, callback) {
/* jshint validthis: true */ /* jshint validthis: true */
/* While the arguments are listed in the function signature, those variables
* are not used directly. Instead, ArgueJS processes the arguments
* object. This allows for simple handling of optional arguments in the
* middle of the argument list, and also provides type checking. */
var args = arguejs({argument: null, metadata: [Metadata, new Metadata()],
options: [Object], callback: Function}, arguments);
var emitter = new EventEmitter(); var emitter = new EventEmitter();
var call = getCall(this.$channel, method, options); var call = getCall(this.$channel, method, args.options);
if (metadata === null || metadata === undefined) { metadata = args.metadata.clone();
metadata = new Metadata();
} else {
metadata = metadata.clone();
}
emitter.cancel = function cancel() { emitter.cancel = function cancel() {
call.cancel(); call.cancel();
}; };
@ -375,9 +378,9 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
return call.getPeer(); return call.getPeer();
}; };
var client_batch = {}; var client_batch = {};
var message = serialize(argument); var message = serialize(args.argument);
if (options) { if (args.options) {
message.grpcWriteFlags = options.flags; message.grpcWriteFlags = args.options.flags;
} }
client_batch[grpc.opType.SEND_INITIAL_METADATA] = client_batch[grpc.opType.SEND_INITIAL_METADATA] =
metadata._getCoreRepresentation(); metadata._getCoreRepresentation();
@ -395,7 +398,7 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
if (status.code === grpc.status.OK) { if (status.code === grpc.status.OK) {
if (err) { if (err) {
// Got a batch error, but OK status. Something went wrong // Got a batch error, but OK status. Something went wrong
callback(err); args.callback(err);
return; return;
} else { } else {
try { try {
@ -414,9 +417,9 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
error = new Error(status.details); error = new Error(status.details);
error.code = status.code; error.code = status.code;
error.metadata = status.metadata; error.metadata = status.metadata;
callback(error); args.callback(error);
} else { } else {
callback(null, deserialized); args.callback(null, deserialized);
} }
emitter.emit('status', status); emitter.emit('status', status);
emitter.emit('metadata', Metadata._fromCoreRepresentation( emitter.emit('metadata', Metadata._fromCoreRepresentation(
@ -440,21 +443,23 @@ 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.
* @this {Client} Client object. Must have a channel member. * @this {Client} Client object. Must have a channel member.
* @param {function(?Error, value=)} callback The callback to for when the
* response is received
* @param {Metadata=} metadata Array of metadata key/value pairs to add to the * @param {Metadata=} metadata Array of metadata key/value pairs to add to the
* call * call
* @param {Object=} options Options map * @param {Object=} options Options map
* @param {function(?Error, value=)} callback The callback to for when the
* response is received
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeClientStreamRequest(callback, metadata, options) { function makeClientStreamRequest(metadata, options, callback) {
/* jshint validthis: true */ /* jshint validthis: true */
var call = getCall(this.$channel, method, options); /* While the arguments are listed in the function signature, those variables
if (metadata === null || metadata === undefined) { * are not used directly. Instead, ArgueJS processes the arguments
metadata = new Metadata(); * object. This allows for simple handling of optional arguments in the
} else { * middle of the argument list, and also provides type checking. */
metadata = metadata.clone(); var args = arguejs({metadata: [Metadata, new Metadata()],
} options: [Object], callback: Function}, arguments);
var call = getCall(this.$channel, method, args.options);
metadata = args.metadata.clone();
var stream = new ClientWritableStream(call, serialize); var stream = new ClientWritableStream(call, serialize);
var metadata_batch = {}; var metadata_batch = {};
metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
@ -481,7 +486,7 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
if (status.code === grpc.status.OK) { if (status.code === grpc.status.OK) {
if (err) { if (err) {
// Got a batch error, but OK status. Something went wrong // Got a batch error, but OK status. Something went wrong
callback(err); args.callback(err);
return; return;
} else { } else {
try { try {
@ -500,9 +505,9 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
error = new Error(response.status.details); error = new Error(response.status.details);
error.code = status.code; error.code = status.code;
error.metadata = status.metadata; error.metadata = status.metadata;
callback(error); args.callback(error);
} else { } else {
callback(null, deserialized); args.callback(null, deserialized);
} }
stream.emit('status', status); stream.emit('status', status);
}); });
@ -533,17 +538,18 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
*/ */
function makeServerStreamRequest(argument, metadata, options) { function makeServerStreamRequest(argument, metadata, options) {
/* jshint validthis: true */ /* jshint validthis: true */
var call = getCall(this.$channel, method, options); /* While the arguments are listed in the function signature, those variables
if (metadata === null || metadata === undefined) { * are not used directly. Instead, ArgueJS processes the arguments
metadata = new Metadata(); * object. */
} else { var args = arguejs({argument: null, metadata: [Metadata, new Metadata()],
metadata = metadata.clone(); options: [Object]}, arguments);
} var call = getCall(this.$channel, method, args.options);
metadata = args.metadata.clone();
var stream = new ClientReadableStream(call, deserialize); var stream = new ClientReadableStream(call, deserialize);
var start_batch = {}; var start_batch = {};
var message = serialize(argument); var message = serialize(args.argument);
if (options) { if (args.options) {
message.grpcWriteFlags = options.flags; message.grpcWriteFlags = args.options.flags;
} }
start_batch[grpc.opType.SEND_INITIAL_METADATA] = start_batch[grpc.opType.SEND_INITIAL_METADATA] =
metadata._getCoreRepresentation(); metadata._getCoreRepresentation();
@ -595,12 +601,13 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
*/ */
function makeBidiStreamRequest(metadata, options) { function makeBidiStreamRequest(metadata, options) {
/* jshint validthis: true */ /* jshint validthis: true */
var call = getCall(this.$channel, method, options); /* While the arguments are listed in the function signature, those variables
if (metadata === null || metadata === undefined) { * are not used directly. Instead, ArgueJS processes the arguments
metadata = new Metadata(); * object. */
} else { var args = arguejs({metadata: [Metadata, new Metadata()],
metadata = metadata.clone(); options: [Object]}, arguments);
} var call = getCall(this.$channel, method, args.options);
metadata = args.metadata.clone();
var stream = new ClientDuplexStream(call, serialize, deserialize); var stream = new ClientDuplexStream(call, serialize, deserialize);
var start_batch = {}; var start_batch = {};
start_batch[grpc.opType.SEND_INITIAL_METADATA] = start_batch[grpc.opType.SEND_INITIAL_METADATA] =

@ -398,18 +398,20 @@ describe('client credentials', function() {
metadataUpdater); metadataUpdater);
}); });
it('Should update metadata on a unary call', function(done) { it('Should update metadata on a unary call', function(done) {
var call = client.unary({}, function(err, data) { var call = client.unary({}, {credentials: updater_creds},
function(err, data) {
assert.ifError(err); assert.ifError(err);
}, null, {credentials: updater_creds}); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done(); done();
}); });
}); });
it('should update metadata on a client streaming call', function(done) { it('should update metadata on a client streaming call', function(done) {
var call = client.clientStream(function(err, data) { var call = client.clientStream({credentials: updater_creds},
function(err, data) {
assert.ifError(err); assert.ifError(err);
}, null, {credentials: updater_creds}); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done(); done();
@ -417,7 +419,7 @@ describe('client credentials', function() {
call.end(); call.end();
}); });
it('should update metadata on a server streaming call', function(done) { it('should update metadata on a server streaming call', function(done) {
var call = client.serverStream({}, null, {credentials: updater_creds}); var call = client.serverStream({}, {credentials: updater_creds});
call.on('data', function() {}); call.on('data', function() {});
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
@ -425,7 +427,7 @@ describe('client credentials', function() {
}); });
}); });
it('should update metadata on a bidi streaming call', function(done) { it('should update metadata on a bidi streaming call', function(done) {
var call = client.bidiStream(null, {credentials: updater_creds}); var call = client.bidiStream({credentials: updater_creds});
call.on('data', function() {}); call.on('data', function() {});
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
@ -443,9 +445,10 @@ describe('client credentials', function() {
altMetadataUpdater); altMetadataUpdater);
var combined_updater = grpc.credentials.combineCallCredentials( var combined_updater = grpc.credentials.combineCallCredentials(
updater_creds, alt_updater_creds); updater_creds, alt_updater_creds);
var call = client.unary({}, function(err, data) { var call = client.unary({}, {credentials: combined_updater},
function(err, data) {
assert.ifError(err); assert.ifError(err);
}, null, {credentials: combined_updater}); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
assert.deepEqual(metadata.get('other_plugin_key'), assert.deepEqual(metadata.get('other_plugin_key'),

@ -404,18 +404,18 @@ describe('Echo metadata', function() {
server.forceShutdown(); server.forceShutdown();
}); });
it('with unary call', function(done) { it('with unary call', function(done) {
var call = client.unary({}, function(err, data) { var call = client.unary({}, metadata, function(err, data) {
assert.ifError(err); assert.ifError(err);
}, metadata); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']); assert.deepEqual(metadata.get('key'), ['value']);
done(); done();
}); });
}); });
it('with client stream call', function(done) { it('with client stream call', function(done) {
var call = client.clientStream(function(err, data) { var call = client.clientStream(metadata, function(err, data) {
assert.ifError(err); assert.ifError(err);
}, metadata); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']); assert.deepEqual(metadata.get('key'), ['value']);
done(); done();
@ -441,8 +441,8 @@ describe('Echo metadata', function() {
}); });
it('shows the correct user-agent string', function(done) { it('shows the correct user-agent string', function(done) {
var version = require('../../../package.json').version; var version = require('../../../package.json').version;
var call = client.unary({}, function(err, data) { assert.ifError(err); }, var call = client.unary({}, metadata,
metadata); function(err, data) { assert.ifError(err); });
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
assert(_.startsWith(metadata.get('user-agent')[0], assert(_.startsWith(metadata.get('user-agent')[0],
'grpc-node/' + version)); 'grpc-node/' + version));
@ -452,8 +452,8 @@ describe('Echo metadata', function() {
it('properly handles duplicate values', function(done) { it('properly handles duplicate values', function(done) {
var dup_metadata = metadata.clone(); var dup_metadata = metadata.clone();
dup_metadata.add('key', 'value2'); dup_metadata.add('key', 'value2');
var call = client.unary({}, function(err, data) {assert.ifError(err); }, var call = client.unary({}, dup_metadata,
dup_metadata); function(err, data) {assert.ifError(err); });
call.on('metadata', function(resp_metadata) { call.on('metadata', function(resp_metadata) {
// Two arrays are equal iff their symmetric difference is empty // Two arrays are equal iff their symmetric difference is empty
assert.deepEqual(_.xor(dup_metadata.get('key'), resp_metadata.get('key')), assert.deepEqual(_.xor(dup_metadata.get('key'), resp_metadata.get('key')),
@ -954,7 +954,7 @@ describe('Call propagation', function() {
done = multiDone(done, 2); done = multiDone(done, 2);
var call; var call;
proxy_impl.unary = function(parent, callback) { proxy_impl.unary = function(parent, callback) {
client.unary(parent.request, function(err, value) { client.unary(parent.request, {parent: parent}, function(err, value) {
try { try {
assert(err); assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED); assert.strictEqual(err.code, grpc.status.CANCELLED);
@ -962,7 +962,7 @@ describe('Call propagation', function() {
callback(err, value); callback(err, value);
done(); done();
} }
}, null, {parent: parent}); });
call.cancel(); call.cancel();
}; };
proxy.addProtoService(test_service, proxy_impl); proxy.addProtoService(test_service, proxy_impl);
@ -976,7 +976,7 @@ describe('Call propagation', function() {
done = multiDone(done, 2); done = multiDone(done, 2);
var call; var call;
proxy_impl.clientStream = function(parent, callback) { proxy_impl.clientStream = function(parent, callback) {
client.clientStream(function(err, value) { client.clientStream({parent: parent}, function(err, value) {
try { try {
assert(err); assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED); assert.strictEqual(err.code, grpc.status.CANCELLED);
@ -984,7 +984,7 @@ describe('Call propagation', function() {
callback(err, value); callback(err, value);
done(); done();
} }
}, null, {parent: parent}); });
call.cancel(); call.cancel();
}; };
proxy.addProtoService(test_service, proxy_impl); proxy.addProtoService(test_service, proxy_impl);
@ -998,8 +998,7 @@ describe('Call propagation', function() {
done = multiDone(done, 2); done = multiDone(done, 2);
var call; var call;
proxy_impl.serverStream = function(parent) { proxy_impl.serverStream = function(parent) {
var child = client.serverStream(parent.request, null, var child = client.serverStream(parent.request, {parent: parent});
{parent: parent});
child.on('data', function() {}); child.on('data', function() {});
child.on('error', function(err) { child.on('error', function(err) {
assert(err); assert(err);
@ -1023,7 +1022,7 @@ describe('Call propagation', function() {
done = multiDone(done, 2); done = multiDone(done, 2);
var call; var call;
proxy_impl.bidiStream = function(parent) { proxy_impl.bidiStream = function(parent) {
var child = client.bidiStream(null, {parent: parent}); var child = client.bidiStream({parent: parent});
child.on('data', function() {}); child.on('data', function() {});
child.on('error', function(err) { child.on('error', function(err) {
assert(err); assert(err);
@ -1051,7 +1050,8 @@ describe('Call propagation', function() {
it('With a client stream call', function(done) { it('With a client stream call', function(done) {
done = multiDone(done, 2); done = multiDone(done, 2);
proxy_impl.clientStream = function(parent, callback) { proxy_impl.clientStream = function(parent, callback) {
client.clientStream(function(err, value) { var options = {parent: parent, propagate_flags: deadline_flags};
client.clientStream(options, function(err, value) {
try { try {
assert(err); assert(err);
assert(err.code === grpc.status.DEADLINE_EXCEEDED || assert(err.code === grpc.status.DEADLINE_EXCEEDED ||
@ -1060,7 +1060,7 @@ describe('Call propagation', function() {
callback(err, value); callback(err, value);
done(); done();
} }
}, null, {parent: parent, propagate_flags: deadline_flags}); });
}; };
proxy.addProtoService(test_service, proxy_impl); proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
@ -1069,15 +1069,15 @@ describe('Call propagation', function() {
grpc.credentials.createInsecure()); grpc.credentials.createInsecure());
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1); deadline.setSeconds(deadline.getSeconds() + 1);
proxy_client.clientStream(function(err, value) { proxy_client.clientStream({deadline: deadline}, function(err, value) {
done(); done();
}, null, {deadline: deadline}); });
}); });
it('With a bidi stream call', function(done) { it('With a bidi stream call', function(done) {
done = multiDone(done, 2); done = multiDone(done, 2);
proxy_impl.bidiStream = function(parent) { proxy_impl.bidiStream = function(parent) {
var child = client.bidiStream( var child = client.bidiStream(
null, {parent: parent, propagate_flags: deadline_flags}); {parent: parent, propagate_flags: deadline_flags});
child.on('data', function() {}); child.on('data', function() {});
child.on('error', function(err) { child.on('error', function(err) {
assert(err); assert(err);
@ -1093,7 +1093,7 @@ describe('Call propagation', function() {
grpc.credentials.createInsecure()); grpc.credentials.createInsecure());
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1); deadline.setSeconds(deadline.getSeconds() + 1);
var call = proxy_client.bidiStream(null, {deadline: deadline}); var call = proxy_client.bidiStream({deadline: deadline});
call.on('data', function() {}); call.on('data', function() {});
call.on('error', function(err) { call.on('error', function(err) {
done(); done();

@ -29,6 +29,7 @@
}, },
"bundledDependencies": ["node-pre-gyp"], "bundledDependencies": ["node-pre-gyp"],
"dependencies": { "dependencies": {
"arguejs": "^0.2.3",
"lodash": "^3.9.3", "lodash": "^3.9.3",
"nan": "^2.0.0", "nan": "^2.0.0",
"protobufjs": "^4.0.0" "protobufjs": "^4.0.0"

Loading…
Cancel
Save