Merge pull request #446 from murgatroid99/native_channel_API

Add Channel class and Client channel override options to public API
This commit is contained in:
Michael Lumish 2018-07-25 10:36:16 -07:00 committed by GitHub
commit e66462933a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 353 additions and 154 deletions

View File

@ -90,7 +90,8 @@
'PB_FIELD_32BIT',
'GPR_BACKWARDS_COMPATIBILITY_MODE',
'GRPC_ARES=0',
'GRPC_UV'
'GRPC_UV',
'GRPC_NODE_VERSION="1.14.0-dev"'
],
'conditions': [
['grpc_gcov=="true"', {

View File

@ -537,6 +537,8 @@ bool Call::HasInstance(Local<Value> val) {
return Nan::New(fun_tpl)->HasInstance(val);
}
grpc_call *Call::GetWrappedCall() { return this->wrapped_call; }
Local<Value> Call::WrapStruct(grpc_call *call) {
EscapableHandleScope scope;
if (call == NULL) {
@ -575,82 +577,20 @@ NAN_METHOD(Call::New) {
*/
if (info.IsConstructCall()) {
Call *call;
if (info[0]->IsExternal()) {
Local<External> ext = info[0].As<External>();
// This option is used for wrapping an existing call
grpc_call *call_value = reinterpret_cast<grpc_call *>(ext->Value());
call = new Call(call_value);
} else {
if (!Channel::HasInstance(info[0])) {
return Nan::ThrowTypeError("Call's first argument must be a Channel");
}
if (!info[1]->IsString()) {
return Nan::ThrowTypeError("Call's second argument must be a string");
}
if (!(info[2]->IsNumber() || info[2]->IsDate())) {
return Nan::ThrowTypeError(
"Call's third argument must be a date or a number");
}
// These arguments are at the end because they are optional
grpc_call *parent_call = NULL;
if (Call::HasInstance(info[4])) {
Call *parent_obj =
ObjectWrap::Unwrap<Call>(Nan::To<Object>(info[4]).ToLocalChecked());
parent_call = parent_obj->wrapped_call;
} else if (!(info[4]->IsUndefined() || info[4]->IsNull())) {
return Nan::ThrowTypeError(
"Call's fifth argument must be another call, if provided");
}
uint32_t propagate_flags = GRPC_PROPAGATE_DEFAULTS;
if (info[5]->IsUint32()) {
propagate_flags = Nan::To<uint32_t>(info[5]).FromJust();
} else if (!(info[5]->IsUndefined() || info[5]->IsNull())) {
return Nan::ThrowTypeError(
"Call's sixth argument must be propagate flags, if provided");
}
Local<Object> channel_object = Nan::To<Object>(info[0]).ToLocalChecked();
Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object);
if (channel->GetWrappedChannel() == NULL) {
return Nan::ThrowError("Call cannot be created from a closed channel");
}
double deadline = Nan::To<double>(info[2]).FromJust();
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
grpc_call *wrapped_call;
grpc_slice method =
CreateSliceFromString(Nan::To<String>(info[1]).ToLocalChecked());
if (info[3]->IsString()) {
grpc_slice *host = new grpc_slice;
*host =
CreateSliceFromString(Nan::To<String>(info[3]).ToLocalChecked());
wrapped_call = grpc_channel_create_call(
wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(),
method, host, MillisecondsToTimespec(deadline), NULL);
delete host;
} else if (info[3]->IsUndefined() || info[3]->IsNull()) {
wrapped_call = grpc_channel_create_call(
wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(),
method, NULL, MillisecondsToTimespec(deadline), NULL);
} else {
return Nan::ThrowTypeError("Call's fourth argument must be a string");
}
grpc_slice_unref(method);
call = new Call(wrapped_call);
Nan::Set(info.This(), Nan::New("channel_").ToLocalChecked(),
channel_object);
if (!info[0]->IsExternal()) {
return Nan::ThrowTypeError(
"Call can only be created with Channel.createCall");
}
Local<External> ext = info[0].As<External>();
// This option is used for wrapping an existing call
grpc_call *call_value = reinterpret_cast<grpc_call *>(ext->Value());
call = new Call(call_value);
call->Wrap(info.This());
info.GetReturnValue().Set(info.This());
return;
} else {
const int argc = 4;
Local<Value> argv[argc] = {info[0], info[1], info[2], info[3]};
MaybeLocal<Object> maybe_instance =
Nan::NewInstance(constructor->GetFunction(), argc, argv);
if (maybe_instance.IsEmpty()) {
// There's probably a pending exception
return;
} else {
info.GetReturnValue().Set(maybe_instance.ToLocalChecked());
}
return Nan::ThrowTypeError(
"Call can only be created with Channel.createCall");
}
}

View File

@ -52,6 +52,8 @@ class Call : public Nan::ObjectWrap {
/* Wrap a grpc_call struct in a javascript object */
static v8::Local<v8::Value> WrapStruct(grpc_call *call);
grpc_call *GetWrappedCall();
void CompleteBatch(bool is_final_op);
private:

View File

@ -28,6 +28,7 @@
#include "completion_queue.h"
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "slice.h"
#include "timeval.h"
namespace grpc {
@ -56,11 +57,24 @@ using v8::Value;
Callback *Channel::constructor;
Persistent<FunctionTemplate> Channel::fun_tpl;
static const char grpc_node_user_agent[] = "grpc-node/" GRPC_NODE_VERSION;
void PopulateUserAgentChannelArg(grpc_arg *arg) {
size_t key_len = sizeof(GRPC_ARG_PRIMARY_USER_AGENT_STRING);
size_t val_len = sizeof(grpc_node_user_agent);
arg->key = reinterpret_cast<char *>(calloc(key_len, sizeof(char)));
memcpy(arg->key, GRPC_ARG_PRIMARY_USER_AGENT_STRING, key_len);
arg->type = GRPC_ARG_STRING;
arg->value.string = reinterpret_cast<char *>(calloc(val_len, sizeof(char)));
memcpy(arg->value.string, grpc_node_user_agent, val_len);
}
bool ParseChannelArgs(Local<Value> args_val,
grpc_channel_args **channel_args_ptr) {
if (args_val->IsUndefined() || args_val->IsNull()) {
*channel_args_ptr = NULL;
return true;
// Treat null and undefined the same as an empty object
args_val = Nan::New<Object>();
}
if (!args_val->IsObject()) {
*channel_args_ptr = NULL;
@ -72,9 +86,17 @@ bool ParseChannelArgs(Local<Value> args_val,
Local<Object> args_hash = Nan::To<Object>(args_val).ToLocalChecked();
Local<Array> keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked();
channel_args->num_args = keys->Length();
/* This is an ugly hack to add in the user agent string argument if it wasn't
* passed by the user */
bool has_user_agent_arg = Nan::HasOwnProperty(
args_hash, Nan::New(GRPC_ARG_PRIMARY_USER_AGENT_STRING).ToLocalChecked()
).FromJust();
if (!has_user_agent_arg) {
channel_args->num_args += 1;
}
channel_args->args = reinterpret_cast<grpc_arg *>(
calloc(channel_args->num_args, sizeof(grpc_arg)));
for (unsigned int i = 0; i < channel_args->num_args; i++) {
for (unsigned int i = 0; i < keys->Length(); i++) {
Local<Value> key = Nan::Get(keys, i).ToLocalChecked();
Utf8String key_str(key);
if (*key_str == NULL) {
@ -88,10 +110,31 @@ bool ParseChannelArgs(Local<Value> args_val,
} else if (value->IsString()) {
Utf8String val_str(value);
channel_args->args[i].type = GRPC_ARG_STRING;
channel_args->args[i].value.string =
reinterpret_cast<char *>(calloc(val_str.length() + 1, sizeof(char)));
memcpy(channel_args->args[i].value.string, *val_str,
val_str.length() + 1);
/* Append the grpc-node user agent string after the application user agent
* string, and put the combination at the beginning of the user agent string
*/
if (strcmp(*key_str, GRPC_ARG_PRIMARY_USER_AGENT_STRING) == 0) {
/* val_str.length() is the string length and does not include the
* trailing 0 byte. sizeof(grpc_node_user_agent) is the array length,
* so it does include the trailing 0 byte. */
size_t val_str_len = val_str.length();
size_t user_agent_len = sizeof(grpc_node_user_agent);
/* This is the length of the two parts of the string, plus the space in
* between, plus the 0 at the end, which is included in user_agent_len.
*/
channel_args->args[i].value.string =
reinterpret_cast<char *>(calloc(val_str_len + user_agent_len + 1, sizeof(char)));
memcpy(channel_args->args[i].value.string, *val_str,
val_str.length());
channel_args->args[i].value.string[val_str_len] = ' ';
memcpy(channel_args->args[i].value.string + val_str_len + 1,
grpc_node_user_agent, user_agent_len);
} else {
channel_args->args[i].value.string =
reinterpret_cast<char *>(calloc(val_str.length() + 1, sizeof(char)));
memcpy(channel_args->args[i].value.string, *val_str,
val_str.length() + 1);
}
} else {
// The value does not match either of the accepted types
return false;
@ -100,6 +143,11 @@ bool ParseChannelArgs(Local<Value> args_val,
reinterpret_cast<char *>(calloc(key_str.length() + 1, sizeof(char)));
memcpy(channel_args->args[i].key, *key_str, key_str.length() + 1);
}
/* Add a standard user agent string argument if none was provided */
if (!has_user_agent_arg) {
size_t index = channel_args->num_args - 1;
PopulateUserAgentChannelArg(&channel_args->args[index]);
}
return true;
}
@ -141,6 +189,7 @@ void Channel::Init(Local<Object> exports) {
Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState);
Nan::SetPrototypeMethod(tpl, "watchConnectivityState",
WatchConnectivityState);
Nan::SetPrototypeMethod(tpl, "createCall", CreateCall);
fun_tpl.Reset(tpl);
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, Nan::New("Channel").ToLocalChecked(), ctr);
@ -268,5 +317,69 @@ NAN_METHOD(Channel::WatchConnectivityState) {
CompletionQueueNext();
}
NAN_METHOD(Channel::CreateCall) {
/* Arguments:
* 0: Method
* 1: Deadline
* 2: host
* 3: parent Call
* 4: propagation flags
*/
if (!HasInstance(info.This())) {
return Nan::ThrowTypeError(
"createCall can only be called on Channel objects");
}
if (!info[0]->IsString()){
return Nan::ThrowTypeError("createCall's first argument must be a string");
}
if (!(info[1]->IsNumber() || info[1]->IsDate())) {
return Nan::ThrowTypeError(
"createcall's second argument must be a date or a number");
}
// These arguments are at the end because they are optional
grpc_call *parent_call = NULL;
if (Call::HasInstance(info[3])) {
Call *parent_obj =
ObjectWrap::Unwrap<Call>(Nan::To<Object>(info[3]).ToLocalChecked());
parent_call = parent_obj->GetWrappedCall();
} else if (!(info[3]->IsUndefined() || info[3]->IsNull())) {
return Nan::ThrowTypeError(
"createCall's fourth argument must be another call, if provided");
}
uint32_t propagate_flags = GRPC_PROPAGATE_DEFAULTS;
if (info[4]->IsUint32()) {
propagate_flags = Nan::To<uint32_t>(info[4]).FromJust();
} else if (!(info[4]->IsUndefined() || info[4]->IsNull())) {
return Nan::ThrowTypeError(
"createCall's fifth argument must be propagate flags, if provided");
}
Channel *channel = ObjectWrap::Unwrap<Channel>(info.This());
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
if (wrapped_channel == NULL) {
return Nan::ThrowError("Cannot createCall with a closed Channel");
}
grpc_slice method =
CreateSliceFromString(Nan::To<String>(info[0]).ToLocalChecked());
double deadline = Nan::To<double>(info[1]).FromJust();
grpc_call *wrapped_call = NULL;
if (info[2]->IsString()) {
grpc_slice *host = new grpc_slice;
*host =
CreateSliceFromString(Nan::To<String>(info[3]).ToLocalChecked());
wrapped_call = grpc_channel_create_call(
wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(),
method, host, MillisecondsToTimespec(deadline), NULL);
delete host;
} else if (info[2]->IsUndefined() || info[2]->IsNull()) {
wrapped_call = grpc_channel_create_call(
wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(),
method, NULL, MillisecondsToTimespec(deadline), NULL);
} else {
return Nan::ThrowTypeError("createCall's third argument must be a string");
}
grpc_slice_unref(method);
info.GetReturnValue().Set(Call::WrapStruct(wrapped_call));
}
} // namespace node
} // namespace grpc

View File

@ -56,6 +56,7 @@ class Channel : public Nan::ObjectWrap {
static NAN_METHOD(GetTarget);
static NAN_METHOD(GetConnectivityState);
static NAN_METHOD(WatchConnectivityState);
static NAN_METHOD(CreateCall);
static Nan::Callback *constructor;
static Nan::Persistent<v8::FunctionTemplate> fun_tpl;

View File

@ -1083,11 +1083,6 @@ declare module "grpc" {
waitForReady(deadline: Deadline, callback: (error: Error | null) => void): void;
}
/**
* A gRPC channel.
*/
export type Channel = any;
/**
* Options that can be set on a call.
*/
@ -1470,4 +1465,62 @@ declare module "grpc" {
*/
recvMessageWithContext(context: object): void;
}
}
export enum connectivityState {
IDLE = 0,
CONNECTING = 1,
READY = 2,
TRANSIENT_FAILURE = 3,
SHUTDOWN = 4
}
export class Channel {
/**
* This constructor API is almost identical to the Client constructor,
* except that some of the options for the Client constructor are not valid
* here.
* @param target The address of the server to connect to
* @param credentials Channel credentials to use when connecting
* @param options A map of channel options that will be passed to the core
*/
constructor(target: string, credentials: ChannelCredentials, options: {[key:string]: string|number});
/**
* Close the channel. This has the same functionality as the existing grpc.Client.prototype.close
*/
close(): void;
/**
* Return the target that this channel connects to
*/
getTarget(): string;
/**
* Get the channel's current connectivity state.
* @param tryToConnect If true, the channel will start connecting if it is
* idle. Otherwise, idle channels will only start connecting when a
* call starts.
*/
getConnectivityState(tryToConnect: boolean): connectivityState;
/**
* Watch for connectivity state changes.
* @param currentState The state to watch for transitions from. This should
* always be populated by calling getConnectivityState immediately
* before.
* @param deadline A deadline for waiting for a state change
* @param callback Called with no error when a state change, or with an
* error if the deadline passes without a state change.
*/
watchConnectivityState(currentState: connectivityState, deadline: Date|number, callback: (error?: Error) => void);
/**
* Create a call object. Call is an opaque type that is used by the Client
* and Server classes. This function is called by the gRPC library when
* starting a request. Implementers should return an instance of Call that
* is returned from calling createCall on an instance of the provided
* Channel class.
* @param method The full method string to request.
* @param deadline The call deadline
* @param host A host string override for making the request
* @param parentCall A server call to propagate some information from
* @param propagateFlags A bitwise combination of elements of grpc.propagate
* that indicates what information to propagate from parentCall.
*/
createCall(method: string, deadline: Date|number, host: string|null, parentCall: Call|null, propagateFlags: number|null): Call;
}
}

View File

@ -236,6 +236,8 @@ exports.logVerbosity = constants.logVerbosity;
exports.methodTypes = constants.methodTypes;
exports.connectivityState = constants.connectivityState;
exports.credentials = require('./src/credentials.js');
/**
@ -294,3 +296,84 @@ exports.closeClient = function closeClient(client_obj) {
};
exports.Client = client.Client;
/**
* @typedef {Object.<string, string | number>} grpc~ChannelOptions
*/
/**
* This constructor API is almost identical to the Client constructor,
* except that some of the options for the Client constructor are not valid
* here.
* @constructor Channel
* @memberof grpc
* @param {string} target The address of the server to connect to
* @param {grpc.ChannelCredentials} credentials Channel credentials to use when connecting
* @param {grpc~ChannelOptions} options A map of channel options that will be passed to the core
*/
exports.Channel = grpc.Channel;
/**
* Close the channel. This has the same functionality as the existing grpc.Client#close
* @name grpc.Channel#close
* @kind function
*/
/**
* Return the target that this channel connects to
* @name grpc.Channel#getTarget
* @kind function
* @return {string} The target
*/
/**
* Get the channel's current connectivity state.
* @name grpc.Channel#getConnectivityState
* @kind function
* @param {boolean} tryToConnect If true, the channel will start connecting if it is
* idle. Otherwise, idle channels will only start connecting when a
* call starts.
* @return {grpc.connectivityState} The current connectivity state
*/
/**
* @callback grpc.Channel~watchConnectivityStateCallback
* @param {Error?} error
*/
/**
* Watch for connectivity state changes.
* @name grpc.Channel#watchConnectivityState
* @kind function
* @param {grpc.ConnectivityState} currentState The state to watch for
* transitions from. This should always be populated by calling
* getConnectivityState immediately before.
* @param {grpc~Deadline} deadline A deadline for waiting for a state change
* @param {grpc.Channel~watchConnectivityStateCallback} callback Called with no
* error when the state changes, or with an error if the deadline passes
* without a state change
*/
/**
* @name grpc~Call
* @kind class
*/
/**
* Create a call object. Call is an opaque type used by the {@link grpc.Client}
* and {@link grpc.Server} classes. This function is called by the gRPC library
* when starting a request. Implementers should return an instance of Call that
* is returned from calling createCall on an instance of the provided Channel
* class.
* @name grpc.Channel#createCall
* @kind function
* @param {string} method The full method string to request
* @param {grpc~Deadline} deadline The call deadline
* @param {string|null} host A host string override for making the request
* @param {grpc~Call|null} parentCall A server call to propagate some
* information from
* @param {number|null} propagateFlags A bitwise combination of elements of
* {@link grpc.propagate} that indicates what information to propagate
* from parentCall
* @return {grpc~Call}
*/

View File

@ -369,15 +369,6 @@ function Client(address, credentials, options) {
if (!options) {
options = {};
}
/* Append the grpc-node user agent string after the application user agent
* string, and put the combination at the beginning of the user agent string
*/
if (options['grpc.primary_user_agent']) {
options['grpc.primary_user_agent'] += ' ';
} else {
options['grpc.primary_user_agent'] = '';
}
options['grpc.primary_user_agent'] += 'grpc-node/' + version;
// Resolve interceptor options and assign interceptors to each method
if (_.isArray(options.interceptor_providers) && _.isArray(options.interceptors)) {
@ -392,12 +383,23 @@ function Client(address, credentials, options) {
.resolveInterceptorProviders(self.$interceptor_providers, method_definition)
.concat(self.$interceptors);
});
// Exclude interceptor options which have already been consumed
let channelOverride = options.channelOverride;
let channelFactoryOverride = options.channelFactoryOverride;
// Exclude channel options which have already been consumed
var channel_options = _.omit(options,
['interceptors', 'interceptor_providers']);
['interceptors', 'interceptor_providers',
'channelOverride', 'channelFactoryOverride']);
/* Private fields use $ as a prefix instead of _ because it is an invalid
* prefix of a method name */
this.$channel = new grpc.Channel(address, credentials, channel_options);
if (channelOverride) {
this.$channel = options.channelOverride;
} else {
if (channelFactoryOverride) {
this.$channel = channelFactoryOverride(address, credentials, channel_options);
} else {
this.$channel = new grpc.Channel(address, credentials, channel_options);
}
}
}
exports.Client = Client;

View File

@ -648,8 +648,8 @@ function getCall(channel, path, options) {
if (deadline === undefined) {
deadline = Infinity;
}
var call = new grpc.Call(channel, path, deadline, host,
parent, propagate_flags);
var call = channel.createCall(path, deadline, host,
parent, propagate_flags);
if (credentials) {
call.setCredentials(credentials);
}

View File

@ -249,3 +249,18 @@ exports.methodTypes = {
SERVER_STREAMING: 2,
BIDI_STREAMING: 3
};
/**
* Connectivity state values
* @memberof grpc
* @alias grpc.connectivityState
* @readonly
* @enum {number}
*/
exports.connectivityState = {
IDLE: 0,
CONNECTING: 1,
READY: 2,
TRANSIENT_FAILURE: 3,
SHUTDOWN: 4
};

View File

@ -82,7 +82,8 @@
'PB_FIELD_32BIT',
'GPR_BACKWARDS_COMPATIBILITY_MODE',
'GRPC_ARES=0',
'GRPC_UV'
'GRPC_UV',
'GRPC_NODE_VERSION="${settings.get('node_version', settings.version)}"'
],
'conditions': [
['grpc_gcov=="true"', {

View File

@ -49,60 +49,48 @@ describe('call', function() {
after(function() {
server.forceShutdown();
});
describe('constructor', function() {
it('should reject anything less than 3 arguments', function() {
describe('factory', function() {
it('should reject anything less than 2 arguments', function() {
assert.throws(function() {
new grpc.Call();
channel.createCall();
}, TypeError);
assert.throws(function() {
new grpc.Call(channel);
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, 'method');
channel.createCall('method');
}, TypeError);
});
it('should succeed with a Channel, a string, and a date or number',
it('should succeed with a string and a date or number',
function() {
assert.doesNotThrow(function() {
new grpc.Call(channel, 'method', new Date());
channel.createCall('method', new Date());
});
assert.doesNotThrow(function() {
new grpc.Call(channel, 'method', 0);
channel.createCall('method', 0);
});
});
it('should accept an optional fourth string parameter', function() {
it('should accept an optional third string parameter', function() {
assert.doesNotThrow(function() {
new grpc.Call(channel, 'method', new Date(), 'host_override');
channel.createCall('method', new Date(), 'host_override');
});
});
it('should fail with a closed channel', function() {
var local_channel = new grpc.Channel('hostname', insecureCreds);
local_channel.close();
assert.throws(function() {
new grpc.Call(channel, 'method');
local_channel.createCall('method', 0);
});
});
it('should fail with other types', function() {
assert.throws(function() {
new grpc.Call({}, 'method', 0);
channel.createCall(null, 0);
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, null, 0);
channel.createCall('method', 'now');
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, 'method', 'now');
}, TypeError);
});
it('should succeed without the new keyword', function() {
assert.doesNotThrow(function() {
var call = grpc.Call(channel, 'method', new Date());
assert(call instanceof grpc.Call);
});
});
});
describe('deadline', function() {
it('should time out immediately with negative deadline', function(done) {
var call = new grpc.Call(channel, 'method', -Infinity);
var call = channel.createCall('method', -Infinity);
var batch = {};
batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(batch, function(err, response) {
@ -114,7 +102,7 @@ describe('call', function() {
});
describe('startBatch', function() {
it('should fail without an object and a function', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
call.startBatch();
});
@ -126,7 +114,7 @@ describe('call', function() {
});
});
it('should succeed with an empty object', function(done) {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.doesNotThrow(function() {
call.startBatch({}, function(err) {
assert.ifError(err);
@ -137,7 +125,7 @@ describe('call', function() {
});
describe('startBatch with metadata', function() {
it('should succeed with a map of strings to string arrays', function(done) {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.doesNotThrow(function() {
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'],
@ -150,7 +138,7 @@ describe('call', function() {
});
});
it('should succeed with a map of strings to buffer arrays', function(done) {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.doesNotThrow(function() {
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] = {
@ -165,7 +153,7 @@ describe('call', function() {
});
});
it('should fail with other parameter types', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] = undefined;
@ -190,7 +178,7 @@ describe('call', function() {
});
describe('startBatch with message', function() {
it('should fail with null argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_MESSAGE] = null;
@ -198,7 +186,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail with numeric argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_MESSAGE] = 5;
@ -206,7 +194,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail with string argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_MESSAGE] = 'value';
@ -216,7 +204,7 @@ describe('call', function() {
});
describe('startBatch with status', function() {
it('should fail without a code', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -227,7 +215,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail without details', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -238,7 +226,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail without metadata', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -249,7 +237,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail with incorrectly typed code argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -261,7 +249,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail with incorrectly typed details argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -273,7 +261,7 @@ describe('call', function() {
}, TypeError);
});
it('should fail with incorrectly typed metadata argument', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.throws(function() {
var batch = {};
batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -287,7 +275,7 @@ describe('call', function() {
});
describe('cancel', function() {
it('should succeed', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.doesNotThrow(function() {
call.cancel();
});
@ -296,30 +284,30 @@ describe('call', function() {
describe('cancelWithStatus', function() {
it('should reject anything other than an integer and a string', function() {
assert.doesNotThrow(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
call.cancelWithStatus(1, 'details');
});
assert.throws(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
call.cancelWithStatus();
});
assert.throws(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
call.cancelWithStatus('');
});
assert.throws(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
call.cancelWithStatus(5, {});
});
});
it('should reject the OK status code', function() {
assert.throws(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
call.cancelWithStatus(0, 'details');
});
});
it('should result in the call ending with a status', function(done) {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
var batch = {};
batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(batch, function(err, response) {
@ -332,7 +320,7 @@ describe('call', function() {
});
describe('getPeer', function() {
it('should return a string', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
var call = channel.createCall('method', getDeadline(1));
assert.strictEqual(typeof call.getPeer(), 'string');
});
});

View File

@ -19,7 +19,7 @@
'use strict';
var assert = require('assert');
var grpc = require('../src/grpc_extension');
var grpc = require('..');
/**
* This is used for testing functions with multiple asynchronous calls that
@ -41,7 +41,7 @@ function multiDone(done, count) {
}
};
}
var insecureCreds = grpc.ChannelCredentials.createInsecure();
var insecureCreds = grpc.credentials.createInsecure();
describe('channel', function() {
describe('constructor', function() {

View File

@ -61,7 +61,7 @@ describe('end-to-end', function() {
it('should start and end a request without error', function(complete) {
var done = multiDone(complete, 2);
var status_text = 'xyz';
var call = new grpc.Call(channel,
var call = channel.createCall(
'dummy_method',
Infinity);
var client_batch = {};
@ -111,7 +111,7 @@ describe('end-to-end', function() {
it('should successfully send and receive metadata', function(complete) {
var done = multiDone(complete, 2);
var status_text = 'xyz';
var call = new grpc.Call(channel,
var call = channel.createCall(
'dummy_method',
Infinity);
var client_batch = {};
@ -167,7 +167,7 @@ describe('end-to-end', function() {
var reply_text = 'server_response';
var done = multiDone(complete, 2);
var status_text = 'success';
var call = new grpc.Call(channel,
var call = channel.createCall(
'dummy_method',
Infinity);
var client_batch = {};
@ -222,7 +222,7 @@ describe('end-to-end', function() {
var done = multiDone(complete, 2);
var requests = ['req1', 'req2'];
var status_text = 'xyz';
var call = new grpc.Call(channel,
var call = channel.createCall(
'dummy_method',
Infinity);
var client_batch = {};