Skip to content

Commit

Permalink
fix: replace proto-over-HTTP with REGAPIC
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-fenster committed Jul 11, 2023
1 parent 82aaa39 commit fac986b
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 131 deletions.
19 changes: 4 additions & 15 deletions client-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,31 +99,20 @@ binary tranport based on HTTP/2. It's Node.js implementation,

#### HTTP/1.1 REST API mode

- `options.fallback`: `true`, `"rest"`, or `false`, use HTTP fallback mode.
- `options.fallback`: `true` or `false`, use HTTP fallback mode.
Default value is `false`, unless the `window` object is defined.
For compatibility, you can pass any non-empty string, it will be considered
a `true` value.

If you need to use the client library in non-Node.js environment or when gRPC
cannot be used for any reason, you can use the HTTP/1.1 fallback mode. In this
mode, a special browser-compatible transport implementation is used instead of
gRPC transport.

There are two supported gRPC fallback modes:

- set `options.fallback` to `"rest"`: the library will send and receive JSON
payload, HTTP/1.1 REST API endpoints will be used. This mode is recommended
for fallback.

- set `options.fallback` to `true`: the library will send and receive serialized
protobuf payload to special endpoints accepting serialized protobufs over
HTTP/1.1.
gRPC transport. It will send and receive JSONs over HTTP.

In browser context (if the `window` object is defined) the fallback mode is
enabled automatically; set `options.fallback` to `false` if you need to override
this behavior.

Note that `options.fallback` accepts boolean values (`true` and `false`) for
compatibility only. We recommend using `"rest"` to use HTTP/1.1 instead of gRPC.

## Calling API methods

In all examples below we assume that `client` is an instance of the client
Expand Down
40 changes: 12 additions & 28 deletions src/fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {GaxCall, GRPCCall} from './apitypes';
import {Descriptor, StreamDescriptor} from './descriptor';
import {createApiCall as _createApiCall} from './createApiCall';
import {FallbackServiceError} from './googleError';
import * as fallbackProto from './fallbackProto';
import * as fallbackRest from './fallbackRest';
import {isNodeJS} from './featureDetection';
import {generateServiceStub} from './fallbackServiceStub';
Expand Down Expand Up @@ -94,7 +93,7 @@ export type AuthClient =
export class GrpcClient {
auth?: OAuth2Client | GoogleAuth;
authClient?: AuthClient;
fallback: boolean | 'rest' | 'proto';
fallback: boolean;
grpcVersion: string;
private static protoCache = new Map<string, protobuf.Root>();
httpRules?: Array<google.api.IHttpRule>;
Expand All @@ -119,7 +118,11 @@ export class GrpcClient {

constructor(
options: (GrpcClientOptions | {auth: OAuth2Client}) & {
fallback?: boolean | 'rest' | 'proto';
/**

Check failure on line 121 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `··`
* Fallback mode to use instead of gRPC.

Check failure on line 122 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `··`
* A string is accepted for compatibility, all non-empty string values enable the HTTP REST fallback.

Check failure on line 123 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `··`
*/

Check failure on line 124 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `··`
fallback?: boolean | string;

Check failure on line 125 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `··`
} = {}
) {
if (!isNodeJS()) {
Expand All @@ -135,7 +138,7 @@ export class GrpcClient {
(options.auth as GoogleAuth) ||
new GoogleAuth(options as GoogleAuthOptions);
}
this.fallback = options.fallback !== 'rest' ? 'proto' : 'rest';
this.fallback = options.fallback ? true : false;
this.grpcVersion = require('../../package.json').version;
this.httpRules = (options as GrpcClientOptions).httpRules;
this.numericEnums = (options as GrpcClientOptions).numericEnums ?? false;
Expand Down Expand Up @@ -316,14 +319,8 @@ export class GrpcClient {
servicePort = 443;
}

const encoder =
this.fallback === 'rest'
? fallbackRest.encodeRequest
: fallbackProto.encodeRequest;
const decoder =
this.fallback === 'rest'
? fallbackRest.decodeResponse
: fallbackProto.decodeResponse;
const encoder = fallbackRest.encodeRequest;
const decoder = fallbackRest.decodeResponse;
const serviceStub = generateServiceStub(
methods,
protocol,
Expand Down Expand Up @@ -362,7 +359,7 @@ export class GrpcClient {
export function lro(options: GrpcClientOptions) {
options = Object.assign({scopes: []}, options);
if (options.protoJson) {
options = Object.assign(options, {fallback: 'rest'});
options = Object.assign(options, {fallback: true});
}
const gaxGrpc = new GrpcClient(options);
return new OperationsClientBuilder(gaxGrpc, options.protoJson);
Expand Down Expand Up @@ -396,11 +393,9 @@ export function createApiCall(
func: Promise<GRPCCall> | GRPCCall,
settings: gax.CallSettings,
descriptor?: Descriptor,
fallback?: boolean | 'proto' | 'rest'
fallback?: boolean | string

Check warning on line 396 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

'fallback' is defined but never used
): GaxCall {
if (
(!fallback || fallback === 'rest') &&
descriptor &&
if (descriptor &&

Check failure on line 398 in src/fallback.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Insert `⏎····`
'streaming' in descriptor &&
(descriptor as StreamDescriptor).type !== StreamType.SERVER_STREAMING
) {
Expand All @@ -410,17 +405,6 @@ export function createApiCall(
);
};
}
if (
(fallback === 'proto' || fallback === true) && // for legacy reasons, fallback === true means 'proto'
descriptor &&
'streaming' in descriptor
) {
return () => {
throw new Error(
'The gRPC-fallback (proto over HTTP) transport currently does not support streaming calls.'
);
};
}
return _createApiCall(func, settings, descriptor);
}

Expand Down
71 changes: 0 additions & 71 deletions src/fallbackProto.ts

This file was deleted.

26 changes: 13 additions & 13 deletions test/test-application/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,24 @@ async function testShowcase() {
},
} as unknown as GoogleAuth;

const fallbackClientOpts = {

Check failure on line 44 in test/test-application/src/index.ts

View workflow job for this annotation

GitHub Actions / lint-gax

Delete `⏎`
const restClientOpts = {
fallback: true,
protocol: 'http',
port: 1337,
port: 7469,
auth: fakeGoogleAuth,
};

const restClientOpts = {
const restClientOptsCompat = {
fallback: 'rest' as const,
protocol: 'http',
port: 7469,
auth: fakeGoogleAuth,
};

const grpcClient = new EchoClient(grpcClientOpts);
const fallbackClient = new EchoClient(fallbackClientOpts);
const restClient = new EchoClient(restClientOpts);
const restClientCompat = new EchoClient(restClientOptsCompat);

// assuming gRPC server is started locally
await testEcho(grpcClient);
Expand All @@ -69,22 +70,21 @@ async function testShowcase() {
await testChat(grpcClient);
await testWait(grpcClient);

await testEcho(fallbackClient);
await testEchoError(fallbackClient);
await testExpandThrows(fallbackClient); // fallback does not support server streaming
await testPagedExpand(fallbackClient);
await testPagedExpandAsync(fallbackClient);
await testCollectThrows(fallbackClient); // fallback does not support client streaming
await testChatThrows(fallbackClient); // fallback does not support bidi streaming
await testWait(fallbackClient);

await testEcho(restClient);
await testExpand(restClient); // REGAPIC supports server streaming
await testPagedExpand(restClient);
await testPagedExpandAsync(restClient);
await testCollectThrows(restClient); // REGAPIC does not support client streaming
await testChatThrows(restClient); // REGAPIC does not support bidi streaming
await testWait(restClient);

await testEcho(restClientCompat);
await testExpand(restClientCompat); // REGAPIC supports server streaming
await testPagedExpand(restClientCompat);
await testPagedExpandAsync(restClientCompat);
await testCollectThrows(restClientCompat); // REGAPIC does not support client streaming
await testChatThrows(restClientCompat); // REGAPIC does not support bidi streaming
await testWait(restClientCompat);
}

async function testEcho(client: EchoClient) {
Expand Down
21 changes: 17 additions & 4 deletions test/unit/grpc-fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ describe('grpc-fallback', () => {
Promise.resolve({
ok: true,
arrayBuffer: () => {
return Promise.resolve(responseType.encode(response).finish());
return Promise.resolve(Buffer.from(JSON.stringify(response)));
},
})
);
Expand All @@ -287,10 +287,23 @@ describe('grpc-fallback', () => {
it('should handle an API error', done => {
const requestObject = {content: 'test-content'};
// example of an actual google.rpc.Status error message returned by Language API
const fixtureName = path.resolve(__dirname, '..', 'fixtures', 'error.bin');
const errorBin = fs.readFileSync(fixtureName);
const expectedMessage =
'3 INVALID_ARGUMENT: One of content, or gcs_content_uri must be set.';
const jsonError = {
code: 400, // Bad request
message: expectedMessage,
details: [
{
'@type': 'type.googleapis.com/google.rpc.BadRequest',
fieldViolations: [
{
field: 'document.content',
description: 'Must have some text content to annotate.',
},
],
},
],
};
const expectedError = {
code: 3,
details: [
Expand All @@ -309,7 +322,7 @@ describe('grpc-fallback', () => {
Promise.resolve({
ok: false,
arrayBuffer: () => {
return Promise.resolve(errorBin);
return Promise.resolve(Buffer.from(JSON.stringify(jsonError)));
},
})
);
Expand Down

0 comments on commit fac986b

Please sign in to comment.