Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting default call options for Node client #270

Merged
merged 1 commit into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion node/src/chaincodeevents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { CallOptions } from '@grpc/grpc-js';
import { ChaincodeEvent } from './chaincodeevent';
import { ServerStreamResponse } from './client';
import { MockGatewayGrpcClient } from './client.test';
Expand Down Expand Up @@ -57,7 +58,8 @@ async function readElements<T>(iter: AsyncIterable<T>, count: number): Promise<T
describe('Chaincode Events', () => {
const channelName = 'CHANNEL_NAME';
const signature = Buffer.from('SIGNATURE');


let chaincodeEventsOptions: () => CallOptions;
let client: MockGatewayGrpcClient;
let identity: Identity;
let signer: jest.Mock<Promise<Uint8Array>, Uint8Array[]>;
Expand All @@ -66,6 +68,12 @@ describe('Chaincode Events', () => {
let network: Network;

beforeEach(() => {
const now = new Date();
const callOptions = {
deadline: now.setHours(now.getHours() + 1),
}
chaincodeEventsOptions = () => callOptions; // Return a specific object to test modification

client = new MockGatewayGrpcClient();
identity = {
mspId: 'MSP_ID',
Expand All @@ -81,6 +89,7 @@ describe('Chaincode Events', () => {
signer,
hash,
client,
chaincodeEventsOptions,
};
gateway = internalConnect(options);
network = gateway.getNetwork(channelName);
Expand Down Expand Up @@ -138,6 +147,24 @@ describe('Chaincode Events', () => {
const actual = client.getChaincodeEventsOptions()[0];
expect(actual.deadline).toBe(deadline);
});

it('uses default call options', async () => {
await network.getChaincodeEvents('CHAINCODE');

const actual = client.getChaincodeEventsOptions()[0];
expect(actual.deadline).toBe(chaincodeEventsOptions().deadline);
});

it('default call options are not modified', async () => {
const expected = chaincodeEventsOptions().deadline;
const deadline = Date.now() + 1000;

await network.newChaincodeEventsRequest('CHAINCODE')
.getEvents({ deadline });

expect(chaincodeEventsOptions().deadline).toBe(expected);
});

});

describe('event delivery', () => {
Expand Down
2 changes: 1 addition & 1 deletion node/src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe('client', () => {

beforeEach(() => {
grpcClient = new MockGatewayGrpcClient();
gatewayClient = newGatewayClient(grpcClient);
gatewayClient = newGatewayClient(grpcClient, {});
});

it('evaluate', async () => {
Expand Down
34 changes: 17 additions & 17 deletions node/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { CallOptions, ClientUnaryCall, requestCallback } from '@grpc/grpc-js';
import { Message } from 'google-protobuf';
import { ConnectOptions } from './gateway';
import { newGatewayError } from './gatewayerror';
import { ChaincodeEventsResponse, CommitStatusResponse, EndorseRequest, EndorseResponse, EvaluateRequest, EvaluateResponse, SignedChaincodeEventsRequest, SignedCommitStatusRequest, SubmitRequest, SubmitResponse } from './protos/gateway/gateway_pb';

Expand Down Expand Up @@ -50,17 +51,15 @@ export interface GatewayGrpcClient {
makeServerStreamRequest<RequestType, ResponseType>(method: string, serialize: (value: RequestType) => Buffer, deserialize: (value: Buffer) => ResponseType, argument: RequestType, options?: CallOptions): ServerStreamResponse<ResponseType>;
}

function defaultCallOptions(): CallOptions {
return {};
}
type DefaultCallOptions = Pick<ConnectOptions, 'commitStatusOptions' | 'endorseOptions' | 'evaluateOptions' | 'submitOptions' | 'chaincodeEventsOptions'>;

class GatewayClientImpl implements GatewayClient {
#client: GatewayGrpcClient;
#defaultOptions: () => CallOptions;
readonly #client: GatewayGrpcClient;
readonly #defaultOptions: Readonly<DefaultCallOptions>;

constructor(client: GatewayGrpcClient, defaultOptions: () => CallOptions = defaultCallOptions) {
constructor(client: GatewayGrpcClient, defaultOptions: DefaultCallOptions) {
this.#client = client;
this.#defaultOptions = defaultOptions;
this.#defaultOptions = Object.assign({}, defaultOptions);
}

evaluate(request: EvaluateRequest, options?: Readonly<CallOptions>): Promise<EvaluateResponse> {
Expand All @@ -69,7 +68,7 @@ class GatewayClientImpl implements GatewayClient {
serialize,
deserializeEvaluateResponse,
request,
this.#buildOptions(options),
buildOptions(this.#defaultOptions.evaluateOptions, options),
newUnaryCallback(resolve, reject)
));
}
Expand All @@ -80,7 +79,7 @@ class GatewayClientImpl implements GatewayClient {
serialize,
deserializeEndorseResponse,
request,
this.#buildOptions(options),
buildOptions(this.#defaultOptions.endorseOptions, options),
newUnaryCallback(resolve, reject)
));
}
Expand All @@ -91,7 +90,7 @@ class GatewayClientImpl implements GatewayClient {
serialize,
deserializeSubmitResponse,
request,
this.#buildOptions(options),
buildOptions(this.#defaultOptions.submitOptions, options),
newUnaryCallback(resolve, reject)
));
}
Expand All @@ -102,7 +101,7 @@ class GatewayClientImpl implements GatewayClient {
serialize,
deserializeCommitStatusResponse,
request,
this.#buildOptions(options),
buildOptions(this.#defaultOptions.commitStatusOptions, options),
newUnaryCallback(resolve, reject)
));
}
Expand All @@ -113,17 +112,18 @@ class GatewayClientImpl implements GatewayClient {
serialize,
deserializeChaincodeEventsResponse,
request,
this.#buildOptions(options)
buildOptions(this.#defaultOptions.chaincodeEventsOptions, options)
);
return {
[Symbol.asyncIterator]: () => serverStream[Symbol.asyncIterator](),
close: () => serverStream.cancel(),
}
}

#buildOptions(options?: Readonly<CallOptions>): CallOptions {
return Object.assign({}, this.#defaultOptions(), options);
}
}

function buildOptions(defaultOptions: (() => CallOptions) | undefined, options?: Readonly<CallOptions>): CallOptions {
return Object.assign({}, defaultOptions?.(), options);
}

function newUnaryCallback<T>(resolve: (value: T) => void, reject: (reason: Error) => void): requestCallback<T> {
Expand Down Expand Up @@ -163,6 +163,6 @@ function deserializeChaincodeEventsResponse(bytes: Uint8Array): ChaincodeEventsR
return ChaincodeEventsResponse.deserializeBinary(bytes);
}

export function newGatewayClient(client: GatewayGrpcClient): GatewayClient {
return new GatewayClientImpl(client);
export function newGatewayClient(client: GatewayGrpcClient, defaultOptions: DefaultCallOptions): GatewayClient {
return new GatewayClientImpl(client, defaultOptions);
}
29 changes: 27 additions & 2 deletions node/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { Client } from '@grpc/grpc-js';
import { CallOptions, Client } from '@grpc/grpc-js';
import { GatewayClient, GatewayGrpcClient, newGatewayClient } from './client';
import { Hash } from './hash/hash';
import { Identity } from './identity/identity';
Expand Down Expand Up @@ -36,6 +36,31 @@ export interface ConnectOptions {
* Hash implementation used by the gateway to generate digital signatures.
*/
hash?: Hash;

/**
* Supplier of default call options for endorsements.
*/
endorseOptions?: () => CallOptions;

/**
* Supplier of default call options for evaluating transactions.
*/
evaluateOptions?: () => CallOptions;

/**
* Supplier of default call options for submit of transactions to the orderer.
*/
submitOptions?: () => CallOptions;

/**
* Supplier of default call options for retrieving transaction commit status.
*/
commitStatusOptions?: () => CallOptions;

/**
* Supplier of default call options for chaincode events.
*/
chaincodeEventsOptions?: () => CallOptions;
}

/**
Expand All @@ -60,7 +85,7 @@ export function internalConnect(options: InternalConnectOptions): Gateway {
}

const signingIdentity = new SigningIdentity(options);
const gatewayClient = newGatewayClient(options.client);
const gatewayClient = newGatewayClient(options.client, options);

return new GatewayImpl(gatewayClient, signingIdentity);
}
Expand Down
52 changes: 51 additions & 1 deletion node/src/proposal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { Metadata, ServiceError } from '@grpc/grpc-js';
import { CallOptions, Metadata, ServiceError } from '@grpc/grpc-js';
import { MockGatewayGrpcClient } from './client.test';
import { Contract } from './contract';
import { Gateway, internalConnect } from './gateway';
Expand Down Expand Up @@ -68,6 +68,8 @@ describe('Proposal', () => {
metadata: new Metadata(),
});

let evaluateOptions: () => CallOptions;
let endorseOptions: () => CallOptions;
let client: MockGatewayGrpcClient;
let identity: Identity;
let signer: jest.Mock<Promise<Uint8Array>, Uint8Array[]>;
Expand All @@ -77,6 +79,16 @@ describe('Proposal', () => {
let contract: Contract;

beforeEach(() => {
const now = new Date();
const evaluateCallOptions = {
deadline: now.setHours(now.getHours() + 1),
}
evaluateOptions = () => evaluateCallOptions; // Return a specific object to test modification
const endorseCallOptions = {
deadline: now.setHours(now.getHours() + 1),
}
endorseOptions = () => endorseCallOptions; // Return a specific object to test modification

client = new MockGatewayGrpcClient();
identity = {
mspId: 'MSP_ID',
Expand All @@ -92,6 +104,8 @@ describe('Proposal', () => {
signer,
hash,
client,
evaluateOptions,
endorseOptions
});
network = gateway.getNetwork('CHANNEL_NAME');
contract = network.getContract('CHAINCODE_NAME');
Expand Down Expand Up @@ -305,6 +319,23 @@ describe('Proposal', () => {
const actual = client.getEvaluateOptions()[0];
expect(actual.deadline).toBe(deadline);
});

it('uses default call options', async () => {
await contract.evaluate('TRANSACTION_NAME');

const actual = client.getEvaluateOptions()[0];
expect(actual.deadline).toBe(evaluateOptions().deadline);
});

it('default call options are not modified', async () => {
const expected = evaluateOptions().deadline;
const deadline = Date.now() + 1000;
const proposal = contract.newProposal('TRANSACTION_NAME');

await proposal.evaluate({ deadline });

expect(evaluateOptions().deadline).toBe(expected);
});
});

describe('endorse', () => {
Expand Down Expand Up @@ -468,5 +499,24 @@ describe('Proposal', () => {
const actual = client.getEndorseOptions()[0];
expect(actual.deadline).toBe(deadline);
});

it('uses default call options', async () => {
const proposal = contract.newProposal('TRANSACTION_NAME');

await proposal.endorse();

const actual = client.getEndorseOptions()[0];
expect(actual.deadline).toBe(endorseOptions().deadline);
});

it('default call options are not modified', async () => {
const expected = endorseOptions().deadline;
const deadline = Date.now() + 1000;
const proposal = contract.newProposal('TRANSACTION_NAME');

await proposal.endorse({ deadline });

expect(endorseOptions().deadline).toBe(expected);
});
});
});
7 changes: 2 additions & 5 deletions node/src/signingidentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { ConnectOptions } from './gateway';
import { Hash } from './hash/hash';
import { sha256 } from './hash/hashes';
import { Identity } from './identity/identity';
Expand All @@ -16,11 +17,7 @@ const undefinedSigner: Signer = () => {
throw new Error(undefinedSignerMessage);
}

export interface SigningIdentityOptions {
identity: Identity;
signer?: Signer;
hash?: Hash;
}
type SigningIdentityOptions = Pick<ConnectOptions, 'identity' | 'signer' | 'hash'>;

export class SigningIdentity {
readonly #identity: Identity;
Expand Down
Loading