diff --git a/src/fallbackRest.ts b/src/fallbackRest.ts index 62d44bde2..64a54dc37 100644 --- a/src/fallbackRest.ts +++ b/src/fallbackRest.ts @@ -62,7 +62,8 @@ export function encodeRequest( // If numeric enums feature is requested, add extra parameter to the query string if (numericEnums) { transcoded.queryString = - (transcoded.queryString ? '&' : '') + '$alt=json%3Benum-encoding=int'; + (transcoded.queryString ? `${transcoded.queryString}&` : '') + + '$alt=json%3Benum-encoding=int'; } // Converts httpMethod to method that permitted in standard Fetch API spec diff --git a/test/fixtures/google/example/library/v1/library.proto b/test/fixtures/google/example/library/v1/library.proto index 5b953686f..fea4359db 100644 --- a/test/fixtures/google/example/library/v1/library.proto +++ b/test/fixtures/google/example/library/v1/library.proto @@ -148,6 +148,9 @@ message Shelf { message CreateShelfRequest { // The shelf to create. Shelf shelf = 1; + + // Some query string parameter + string query_string_parameter = 2; } // Request message for LibraryService.GetShelf. diff --git a/test/fixtures/library.json b/test/fixtures/library.json index 0a768ecee..62507b0f3 100644 --- a/test/fixtures/library.json +++ b/test/fixtures/library.json @@ -1,4 +1,5 @@ { + "_comment": "To regenerate: npx pbjs -t json -p build/protos -p test/fixtures google/example/library/v1/library.proto > test/fixtures/library.json", "nested": { "google": { "nested": { @@ -231,6 +232,10 @@ "shelf": { "type": "Shelf", "id": 1 + }, + "queryStringParameter": { + "type": "string", + "id": 2 } } }, @@ -385,6 +390,14 @@ } }, "api": { + "options": { + "go_package": "google.golang.org/genproto/googleapis/api/annotations;annotations", + "java_multiple_files": true, + "java_outer_classname": "HttpProto", + "java_package": "com.google.api", + "objc_class_prefix": "GAPI", + "cc_enable_arenas": true + }, "nested": { "http": { "type": "HttpRule", @@ -397,6 +410,10 @@ "rule": "repeated", "type": "HttpRule", "id": 1 + }, + "fullyDecodeReservedExpansion": { + "type": "bool", + "id": 2 } } }, @@ -414,6 +431,10 @@ } }, "fields": { + "selector": { + "type": "string", + "id": 1 + }, "get": { "type": "string", "id": 2 @@ -438,14 +459,14 @@ "type": "CustomHttpPattern", "id": 8 }, - "selector": { - "type": "string", - "id": 1 - }, "body": { "type": "string", "id": 7 }, + "responseBody": { + "type": "string", + "id": 12 + }, "additionalBindings": { "rule": "repeated", "type": "HttpRule", @@ -468,6 +489,15 @@ } }, "protobuf": { + "options": { + "go_package": "google.golang.org/protobuf/types/descriptorpb", + "java_package": "com.google.protobuf", + "java_outer_classname": "DescriptorProtos", + "csharp_namespace": "Google.Protobuf.Reflection", + "objc_class_prefix": "GPB", + "cc_enable_arenas": true, + "optimize_for": "SPEED" + }, "nested": { "FileDescriptorSet": { "fields": { @@ -540,6 +570,10 @@ "syntax": { "type": "string", "id": 12 + }, + "edition": { + "type": "string", + "id": 13 } } }, @@ -604,6 +638,10 @@ "end": { "type": "int32", "id": 2 + }, + "options": { + "type": "ExtensionRangeOptions", + "id": 3 } } }, @@ -621,6 +659,21 @@ } } }, + "ExtensionRangeOptions": { + "fields": { + "uninterpretedOption": { + "rule": "repeated", + "type": "UninterpretedOption", + "id": 999 + } + }, + "extensions": [ + [ + 1000, + 536870911 + ] + ] + }, "FieldDescriptorProto": { "fields": { "name": { @@ -662,6 +715,10 @@ "options": { "type": "FieldOptions", "id": 8 + }, + "proto3Optional": { + "type": "bool", + "id": 17 } }, "nested": { @@ -722,6 +779,30 @@ "options": { "type": "EnumOptions", "id": 3 + }, + "reservedRange": { + "rule": "repeated", + "type": "EnumReservedRange", + "id": 4 + }, + "reservedName": { + "rule": "repeated", + "type": "string", + "id": 5 + } + }, + "nested": { + "EnumReservedRange": { + "fields": { + "start": { + "type": "int32", + "id": 1 + }, + "end": { + "type": "int32", + "id": 2 + } + } } } }, @@ -778,11 +859,17 @@ }, "clientStreaming": { "type": "bool", - "id": 5 + "id": 5, + "options": { + "default": false + } }, "serverStreaming": { "type": "bool", - "id": 6 + "id": 6, + "options": { + "default": false + } } } }, @@ -798,7 +885,10 @@ }, "javaMultipleFiles": { "type": "bool", - "id": 10 + "id": 10, + "options": { + "default": false + } }, "javaGenerateEqualsAndHash": { "type": "bool", @@ -809,7 +899,10 @@ }, "javaStringCheckUtf8": { "type": "bool", - "id": 27 + "id": 27, + "options": { + "default": false + } }, "optimizeFor": { "type": "OptimizeMode", @@ -824,23 +917,45 @@ }, "ccGenericServices": { "type": "bool", - "id": 16 + "id": 16, + "options": { + "default": false + } }, "javaGenericServices": { "type": "bool", - "id": 17 + "id": 17, + "options": { + "default": false + } }, "pyGenericServices": { "type": "bool", - "id": 18 + "id": 18, + "options": { + "default": false + } + }, + "phpGenericServices": { + "type": "bool", + "id": 42, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 23 + "id": 23, + "options": { + "default": false + } }, "ccEnableArenas": { "type": "bool", - "id": 31 + "id": 31, + "options": { + "default": true + } }, "objcClassPrefix": { "type": "string", @@ -850,6 +965,26 @@ "type": "string", "id": 37 }, + "swiftPrefix": { + "type": "string", + "id": 39 + }, + "phpClassPrefix": { + "type": "string", + "id": 40 + }, + "phpNamespace": { + "type": "string", + "id": 41 + }, + "phpMetadataNamespace": { + "type": "string", + "id": 44 + }, + "rubyPackage": { + "type": "string", + "id": 45 + }, "uninterpretedOption": { "rule": "repeated", "type": "UninterpretedOption", @@ -882,15 +1017,24 @@ "fields": { "messageSetWireFormat": { "type": "bool", - "id": 1 + "id": 1, + "options": { + "default": false + } }, "noStandardDescriptorAccessor": { "type": "bool", - "id": 2 + "id": 2, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "mapEntry": { "type": "bool", @@ -909,9 +1053,25 @@ ] ], "reserved": [ + [ + 4, + 4 + ], + [ + 5, + 5 + ], + [ + 6, + 6 + ], [ 8, 8 + ], + [ + 9, + 9 ] ] }, @@ -937,15 +1097,31 @@ }, "lazy": { "type": "bool", - "id": 5 + "id": 5, + "options": { + "default": false + } + }, + "unverifiedLazy": { + "type": "bool", + "id": 15, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "weak": { "type": "bool", - "id": 10 + "id": 10, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -1005,7 +1181,10 @@ }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -1018,13 +1197,22 @@ 1000, 536870911 ] + ], + "reserved": [ + [ + 5, + 5 + ] ] }, "EnumValueOptions": { "fields": { "deprecated": { "type": "bool", - "id": 1 + "id": 1, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -1043,7 +1231,10 @@ "fields": { "deprecated": { "type": "bool", - "id": 33 + "id": 33, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -1062,7 +1253,17 @@ "fields": { "deprecated": { "type": "bool", - "id": 33 + "id": 33, + "options": { + "default": false + } + }, + "idempotencyLevel": { + "type": "IdempotencyLevel", + "id": 34, + "options": { + "default": "IDEMPOTENCY_UNKNOWN" + } }, "uninterpretedOption": { "rule": "repeated", @@ -1075,7 +1276,16 @@ 1000, 536870911 ] - ] + ], + "nested": { + "IdempotencyLevel": { + "values": { + "IDEMPOTENCY_UNKNOWN": 0, + "NO_SIDE_EFFECTS": 1, + "IDEMPOTENT": 2 + } + } + } }, "UninterpretedOption": { "fields": { @@ -1191,6 +1401,19 @@ "end": { "type": "int32", "id": 4 + }, + "semantic": { + "type": "Semantic", + "id": 5 + } + }, + "nested": { + "Semantic": { + "values": { + "NONE": 0, + "SET": 1, + "ALIAS": 2 + } } } } diff --git a/test/unit/regapic.ts b/test/unit/regapic.ts index 7f2604f14..d09a15116 100644 --- a/test/unit/regapic.ts +++ b/test/unit/regapic.ts @@ -279,6 +279,41 @@ describe('REGAPIC', () => { }, /* catch: */ done); }); + it('should preserve query string when appending numeric enums parameter', done => { + const shelf = { + name: 'shelf-name', + theme: 'shelf-theme', + type: 'TYPEONE', + }; + const requestObject = { + shelf: shelf, + queryStringParameter: 'must-be-preserved', + }; + 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(shelf))); + }, + }) + ); + gaxGrpcNumericEnums + .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.strictEqual(err, null); + done(); + }); + }, /* catch: */ done); + }); + it('should request numeric enums if passed as an unknown number', done => { const shelf = { name: 'shelf-name',