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 requsted, disable pretty-print JSON responses
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// If minifyJson feature is requsted, disable pretty-print JSON responses
// If minifyJson feature is requested, disable pretty-print JSON responses

Copy link
Author

Choose a reason for hiding this comment

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

Thanks, fixed.

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
126 changes: 111 additions & 15 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 @@ -202,7 +207,10 @@ describe('REGAPIC', () => {

gaxGrpc.createStub(libraryService, stubOptions).then(libStub => {
libStub.getShelf(requestObject, {}, {}, (err?: {}, result?: {}) => {
assert.strictEqual(spy.getCall(0).returnValue?.queryString, '');
assert.doesNotMatch(
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure what this is testing (what is $alt)? If possible, could we search for what we are looking for? This will also allow readability in the code (it will help future maintainers understand what the behavior should look like). Or, if we also want to test it does not contain $alt, let's also add a positive-looking test. It's also a bit confusing to test the first call OR an empty string?

Copy link
Author

Choose a reason for hiding this comment

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

Good call. I reverted these changes. They were artifacts from a different implementation, but I left them in because I thought they made the tests more robust. However, with your comment I see the downsides.

spy.getCall(0).returnValue?.queryString ?? '',
/\$alt/
);
assert.strictEqual(err, null);
assert.strictEqual(
'shelf-name',
Expand Down Expand Up @@ -237,7 +245,10 @@ describe('REGAPIC', () => {
);
gaxGrpc.createStub(libraryService, stubOptions).then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(spy.getCall(0).returnValue?.queryString, '');
assert.doesNotMatch(
Copy link
Contributor

Choose a reason for hiding this comment

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

same comment as above.

Copy link
Author

Choose a reason for hiding this comment

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

same

spy.getCall(0).returnValue?.queryString ?? '',
/\$alt/
);
assert.strictEqual(err, null);
done();
});
Expand All @@ -264,7 +275,10 @@ describe('REGAPIC', () => {
);
gaxGrpc.createStub(libraryService, stubOptions).then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(spy.getCall(0).returnValue?.queryString, '');
assert.doesNotMatch(
spy.getCall(0).returnValue?.queryString ?? '',
/\$alt/
);
assert.strictEqual(err, null);
done();
});
Expand Down Expand Up @@ -296,9 +310,9 @@ describe('REGAPIC', () => {
.createStub(libraryService, stubOptions)
.then(libStub => {
libStub.getShelf(requestObject, {}, {}, (err?: {}, result?: {}) => {
assert.strictEqual(
spy.getCall(0).returnValue?.queryString,
'$alt=json%3Benum-encoding=int'
assert.match(
spy.getCall(0).returnValue?.queryString ?? '',
/\$alt=json%3Benum-encoding=int(&.*)?$/
);
assert.strictEqual(err, null);
assert.strictEqual(
Expand Down Expand Up @@ -336,9 +350,9 @@ describe('REGAPIC', () => {
.createStub(libraryService, stubOptions)
.then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(
spy.getCall(0).returnValue?.queryString,
'$alt=json%3Benum-encoding=int'
assert.match(
spy.getCall(0).returnValue?.queryString ?? '',
/\$alt=json%3Benum-encoding=int(&.*)?$/
);
assert.strictEqual(err, null);
done();
Expand Down Expand Up @@ -371,9 +385,9 @@ describe('REGAPIC', () => {
.createStub(libraryService, stubOptions)
.then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(
spy.getCall(0).returnValue?.queryString,
'queryStringParameter=must-be-preserved&$alt=json%3Benum-encoding=int'
assert.match(
spy.getCall(0).returnValue?.queryString ?? '',
/^queryStringParameter=must-be-preserved(&.*)?&\$alt=json%3Benum-encoding=int(&.*)?$/
);
assert.strictEqual(err, null);
done();
Expand Down Expand Up @@ -403,9 +417,9 @@ describe('REGAPIC', () => {
.createStub(libraryService, stubOptions)
.then(libStub => {
libStub.createShelf(requestObject, {}, {}, (err?: {}) => {
assert.strictEqual(
spy.getCall(0).returnValue?.queryString,
'$alt=json%3Benum-encoding=int'
assert.match(
spy.getCall(0).returnValue?.queryString ?? '',
/\$alt=json%3Benum-encoding=int(&.*)?$/
);
assert.strictEqual(err, null);
done();
Expand Down Expand Up @@ -569,4 +583,86 @@ 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.match(
spy.getCall(0).returnValue?.queryString ?? '',
Copy link
Contributor

Choose a reason for hiding this comment

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

In general I don't think we should be unsure of what we're matching (I don't think we should be using the nullish coalescing operator in tests)

Copy link
Author

Choose a reason for hiding this comment

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

I updated this. It was just a way to force the argument to be a string.

/\$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.doesNotMatch(
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