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

feat: Add minifyJson param to request minified JSON responses when using the REST fallback #1632

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
5 changes: 4 additions & 1 deletion gax/src/fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class GrpcClient {
private static protoCache = new Map<string, protobuf.Root>();
httpRules?: Array<google.api.IHttpRule>;
numericEnums: boolean;
minifyJson: boolean;
sofisl marked this conversation as resolved.
Show resolved Hide resolved

/**
* In rare cases users might need to deallocate all memory consumed by loaded protos.
Expand Down Expand Up @@ -144,6 +145,7 @@ export class GrpcClient {
this.grpcVersion = require('../../package.json').version;
this.httpRules = (options as GrpcClientOptions).httpRules;
this.numericEnums = (options as GrpcClientOptions).numericEnums ?? false;
this.minifyJson = (options as GrpcClientOptions).minifyJson ?? false;
}

/**
Expand Down Expand Up @@ -343,7 +345,8 @@ export class GrpcClient {
this.authClient,
encoder,
decoder,
this.numericEnums
this.numericEnums,
this.minifyJson
);

return serviceStub;
Expand Down
10 changes: 9 additions & 1 deletion gax/src/fallbackRest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export function encodeRequest(
servicePath: string,
servicePort: number,
request: {},
numericEnums: boolean
numericEnums: boolean,
minifyJson: boolean
): FetchParameters {
const headers: {[key: string]: string} = {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -61,6 +62,13 @@ export function encodeRequest(
'$alt=json%3Benum-encoding=int';
}

// If minifyJson feature is requested, disable pretty-print JSON responses
if (minifyJson) {
transcoded.queryString =
(transcoded.queryString ? `${transcoded.queryString}&` : '') +
'$prettyPrint=0';
}

// Converts httpMethod to method that permitted in standard Fetch API spec
// https://fetch.spec.whatwg.org/#methods
const method = transcoded.httpMethod.toUpperCase() as FetchParametersMethod;
Expand Down
9 changes: 6 additions & 3 deletions gax/src/fallbackServiceStub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ export function generateServiceStub(
servicePath: string,
servicePort: number,
request: {},
numericEnums: boolean
numericEnums: boolean,
minifyJson: boolean
) => FetchParameters,
responseDecoder: (
rpc: protobuf.Method,
ok: boolean,
response: Buffer | ArrayBuffer
) => {},
numericEnums: boolean
numericEnums: boolean,
minifyJson: boolean
) {
const fetch = hasWindowFetch()
? window.fetch
Expand Down Expand Up @@ -100,7 +102,8 @@ export function generateServiceStub(
servicePath,
servicePort,
request,
numericEnums
numericEnums,
minifyJson
);
} catch (err) {
// we could not encode parameters; pass error to the callback
Expand Down
1 change: 1 addition & 0 deletions gax/src/grpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const COMMON_PROTO_FILES: string[] = commonProtoFiles.map(file =>
export interface GrpcClientOptions extends GoogleAuthOptions {
auth?: GoogleAuth;
grpc?: GrpcModule;
minifyJson?: boolean;
protoJson?: protobuf.Root;
httpRules?: Array<google.api.IHttpRule>;
numericEnums?: boolean;
Expand Down
103 changes: 101 additions & 2 deletions gax/test/unit/regapic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const opts = {
describe('REGAPIC', () => {
let gaxGrpc: GrpcClient,
gaxGrpcNumericEnums: GrpcClient,
gaxGrpcMinifyJson: GrpcClient,
protos: protobuf.NamespaceBase,
libProtos: protobuf.NamespaceBase,
echoService: protobuf.Service,
Expand All @@ -67,6 +68,10 @@ describe('REGAPIC', () => {
...opts,
numericEnums: true,
});
gaxGrpcMinifyJson = new GrpcClient({
...opts,
minifyJson: true,
});
protos = gaxGrpc.loadProto(echoProtoJson);
echoService = protos.lookupService('Echo');
const TEST_JSON = path.resolve(
Expand Down Expand Up @@ -337,8 +342,12 @@ describe('REGAPIC', () => {
.then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(
spy.getCall(0).returnValue?.queryString,
'$alt=json%3Benum-encoding=int'
'string',
typeof spy.getCall(0).returnValue?.queryString
);
assert.match(
<string>spy.getCall(0).returnValue?.queryString,
/\$alt=json%3Benum-encoding=int(&.*)?$/
);
assert.strictEqual(err, null);
done();
Expand Down Expand Up @@ -569,4 +578,94 @@ describe('REGAPIC', () => {
}, /* catch: */ done);
});
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also add a test that confirms the response was minified? it seems like we're testing data integrity and that we're sending the prettyPrint request, but maybe we could also see if the actual size of the response is smaller. We have a similar test here that could be useful!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this test would be useful, but I think it requires a system test that hits a real service endpoint. It's not clear to me how to implement that.

describe('should support json minification', () => {
it('should send prettyPrint=0 when json minification is requested', done => {
const requestObject = {name: 'shelves/shelf-name'};
const responseObject = {
name: 'shelf-name',
theme: 'shelf-theme',
type: 100, // unknown enum value
};
const spy = sinon.spy(transcoding, 'transcode');
// incomplete types for nodeFetch, so...
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sinon.stub(nodeFetch, 'Promise' as any).returns(
Promise.resolve({
ok: true,
arrayBuffer: () => {
return Promise.resolve(Buffer.from(JSON.stringify(responseObject)));
},
})
);

gaxGrpcMinifyJson
.createStub(libraryService, stubOptions)
.then(libStub => {
libStub.getShelf(requestObject, {}, {}, (err?: {}, result?: {}) => {
assert.strictEqual(
'string',
typeof spy.getCall(0).returnValue?.queryString
);
assert.match(
<string>spy.getCall(0).returnValue?.queryString,
/\$prettyPrint=0(&.*)?$/
);
assert.strictEqual(err, null);
assert.strictEqual(
'shelf-name',
(result as {name: {}; theme: {}; type: {}}).name
);
assert.strictEqual(
100,
(result as {name: {}; theme: {}; type: {}}).type
);
done();
});
}, /* catch: */ done);
});

it('should not send prettyPrint setting when json minification is not requested', done => {
const requestObject = {name: 'shelves/shelf-name'};
const responseObject = {
name: 'shelf-name',
theme: 'shelf-theme',
type: 100, // unknown enum value
};
const spy = sinon.spy(transcoding, 'transcode');
// incomplete types for nodeFetch, so...
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sinon.stub(nodeFetch, 'Promise' as any).returns(
Promise.resolve({
ok: true,
arrayBuffer: () => {
return Promise.resolve(Buffer.from(JSON.stringify(responseObject)));
},
})
);

gaxGrpc.createStub(libraryService, stubOptions).then(libStub => {
libStub.getShelf(requestObject, {}, {}, (err?: {}, result?: {}) => {
assert.strictEqual(
'string',
typeof spy.getCall(0).returnValue?.queryString
);
assert.doesNotMatch(
<string>spy.getCall(0).returnValue?.queryString,
/prettyPrint/
);
assert.strictEqual(err, null);
assert.strictEqual(
'shelf-name',
(result as {name: {}; theme: {}; type: {}}).name
);
assert.strictEqual(
100,
(result as {name: {}; theme: {}; type: {}}).type
);
done();
});
}, /* catch: */ done);
});
});
});
Loading