From 6deb2beb6af1c132d2964e16cf59138eadaab941 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Wed, 9 Sep 2026 14:09:00 -0700 Subject: [PATCH 1/3] fix(gax): forward fallback flag in fallback createApiCall --- core/packages/gax/src/fallback.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/packages/gax/src/fallback.ts b/core/packages/gax/src/fallback.ts index 32d122b3a67..42511008366 100644 --- a/core/packages/gax/src/fallback.ts +++ b/core/packages/gax/src/fallback.ts @@ -447,7 +447,7 @@ export function createApiCall( ); }; } - return _createApiCall(func, settings, descriptor); + return _createApiCall(func, settings, descriptor, _fallback ?? true); } export {protobuf}; From 0a610c5602c65ecdd295cc6bd9abe82cfc25826c Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Wed, 9 Sep 2026 14:12:32 -0700 Subject: [PATCH 2/3] test(gax): add unit tests verifying fallback flag pass-through for HTTP calls --- core/packages/gax/src/fallback.ts | 2 +- core/packages/gax/test/unit/apiCallable.ts | 111 +++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/core/packages/gax/src/fallback.ts b/core/packages/gax/src/fallback.ts index 42511008366..fa9d154348e 100644 --- a/core/packages/gax/src/fallback.ts +++ b/core/packages/gax/src/fallback.ts @@ -447,7 +447,7 @@ export function createApiCall( ); }; } - return _createApiCall(func, settings, descriptor, _fallback ?? true); + return _createApiCall(func, settings, descriptor, true); } export {protobuf}; diff --git a/core/packages/gax/test/unit/apiCallable.ts b/core/packages/gax/test/unit/apiCallable.ts index d1c7bb20183..14bb51282e1 100644 --- a/core/packages/gax/test/unit/apiCallable.ts +++ b/core/packages/gax/test/unit/apiCallable.ts @@ -22,6 +22,7 @@ import * as sinon from 'sinon'; import {CancellableStream, GRPCCall, RequestType} from '../../src/apitypes'; import {createApiCall as realCreateApiCall} from '../../src/createApiCall'; +import {createApiCall as fallbackCreateApiCall} from '../../src/fallback'; import {StreamDescriptor} from '../../src/descriptor'; import {StreamType} from '../../src/streamingCalls/streaming'; import * as gax from '../../src/gax'; @@ -575,6 +576,116 @@ describe('createApiCall', () => { assert.strictEqual(span.attributes['gcp.method.type'], 'http'); }); + it('passes fallback flag through when using fallback createApiCall with default options', async () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED = 'true'; + const traceAttemptSpy = sinon.spy(tracerHelper, 'traceAttempt'); + + const settings = new gax.CallSettings({ + apiName: 'google.example.v1.Echo', + enableTelemetryTracing: true, + otherArgs: { + internalTelemetryInfo: telemetryInfo, + internalMethodName: 'Echo', + }, + }); + + function func( + argument: {}, + metadata: {}, + options: {}, + callback: (err: GoogleError | null, resp?: unknown) => void, + ) { + callback(null, {data: 'hello'}); + return { + cancel: () => {}, + }; + } + + const apiCall = fallbackCreateApiCall(func, settings); + await apiCall({}, undefined); + + assert.strictEqual(traceAttemptSpy.calledOnce, true); + const [dynamicArgs] = traceAttemptSpy.firstCall.args; + assert.strictEqual(dynamicArgs.rpcType, 'http'); + + const spans = harness.getSpans('google-gax'); + assert.strictEqual(spans.length, 1); + const span = spans[0]; + assert.strictEqual(span.name, 'EchoClient.Echo'); + assert.strictEqual(span.ended, true); + assert.strictEqual(span.attributes['gcp.method.type'], 'http'); + }); + + it('passes explicit _fallback through when using fallback createApiCall', async () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED = 'true'; + const traceAttemptSpy = sinon.spy(tracerHelper, 'traceAttempt'); + + const settings = new gax.CallSettings({ + apiName: 'google.example.v1.Echo', + enableTelemetryTracing: true, + otherArgs: { + internalTelemetryInfo: telemetryInfo, + internalMethodName: 'Echo', + }, + }); + + function func( + argument: {}, + metadata: {}, + options: {}, + callback: (err: GoogleError | null, resp?: unknown) => void, + ) { + callback(null, {data: 'hello'}); + return { + cancel: () => {}, + }; + } + + const apiCall = fallbackCreateApiCall(func, settings, undefined, 'rest'); + await apiCall({}, undefined); + + assert.strictEqual(traceAttemptSpy.calledOnce, true); + const [dynamicArgs] = traceAttemptSpy.firstCall.args; + assert.strictEqual(dynamicArgs.rpcType, 'http'); + + const spans = harness.getSpans('google-gax'); + assert.strictEqual(spans.length, 1); + const span = spans[0]; + assert.strictEqual(span.attributes['gcp.method.type'], 'http'); + }); + + it('passes fallback flag and isStreamingCall as true for server-streaming fallback calls', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED = 'true'; + const traceAttemptSpy = sinon.spy(tracerHelper, 'traceAttempt'); + + const settings = new gax.CallSettings({ + apiName: 'google.example.v1.Echo', + enableTelemetryTracing: true, + otherArgs: { + internalTelemetryInfo: telemetryInfo, + internalMethodName: 'Echo', + }, + }); + + const spy = sinon.spy(() => { + const s = new PassThrough({objectMode: true}); + s.push(null); + return Object.assign(s, {cancel: () => {}}); + }); + + const apiCall = fallbackCreateApiCall( + spy as unknown as GRPCCall, + settings, + new StreamDescriptor(StreamType.SERVER_STREAMING, true), + ); + void apiCall({}, undefined); + + assert.strictEqual(traceAttemptSpy.calledOnce, true); + const [dynamicArgs, , , isStreamingCall] = traceAttemptSpy.firstCall.args; + assert.strictEqual(dynamicArgs.rpcType, 'http'); + assert.strictEqual(isStreamingCall, true); + }); + it('sets rpcType to grpc when _fallback is boolean false', async () => { process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED = 'true'; const settings = new gax.CallSettings({ From 6b5bd9774615dc675b7e1720021d55adbba2d3fe Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Wed, 9 Sep 2026 14:16:30 -0700 Subject: [PATCH 3/3] refactor(gax): rename realCreateApiCall to gaxCreateApiCall in unit tests --- core/packages/gax/test/unit/apiCallable.ts | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/packages/gax/test/unit/apiCallable.ts b/core/packages/gax/test/unit/apiCallable.ts index 14bb51282e1..f41143b3f7c 100644 --- a/core/packages/gax/test/unit/apiCallable.ts +++ b/core/packages/gax/test/unit/apiCallable.ts @@ -21,7 +21,7 @@ import {afterEach, beforeEach, describe, it} from 'mocha'; import * as sinon from 'sinon'; import {CancellableStream, GRPCCall, RequestType} from '../../src/apitypes'; -import {createApiCall as realCreateApiCall} from '../../src/createApiCall'; +import {createApiCall as gaxCreateApiCall} from '../../src/createApiCall'; import {createApiCall as fallbackCreateApiCall} from '../../src/fallback'; import {StreamDescriptor} from '../../src/descriptor'; import {StreamType} from '../../src/streamingCalls/streaming'; @@ -381,7 +381,7 @@ describe('createApiCall', () => { return {cancel: () => {}}; } - const apiCall = realCreateApiCall(func, settings); + const apiCall = gaxCreateApiCall(func, settings); await apiCall({param: 'test'}, undefined); assert.strictEqual(traceAttemptSpy.calledOnce, true); @@ -417,7 +417,7 @@ describe('createApiCall', () => { return Object.assign(s, {cancel: () => {}}); }); - const apiCall = realCreateApiCall( + const apiCall = gaxCreateApiCall( spy as unknown as GRPCCall, settings, new StreamDescriptor(StreamType.SERVER_STREAMING, true), @@ -450,7 +450,7 @@ describe('createApiCall', () => { return {cancel: () => {}}; } - const apiCall = realCreateApiCall(func, settings); + const apiCall = gaxCreateApiCall(func, settings); await apiCall({}, undefined); assert.strictEqual(traceAttemptSpy.calledOnce, true); @@ -485,7 +485,7 @@ describe('createApiCall', () => { return {cancel: () => {}}; } - const apiCall = realCreateApiCall(func, settings); + const apiCall = gaxCreateApiCall(func, settings); await apiCall({}, undefined); assert.strictEqual(traceAttemptSpy.called, false); @@ -515,7 +515,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings); + const apiCall = gaxCreateApiCall(func, settings); const [response] = (await apiCall({}, undefined)) as [ {data: string}, unknown, @@ -565,7 +565,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings, undefined, true); + const apiCall = gaxCreateApiCall(func, settings, undefined, true); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -709,7 +709,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings, undefined, false); + const apiCall = gaxCreateApiCall(func, settings, undefined, false); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -741,7 +741,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings, undefined, 'rest'); + const apiCall = gaxCreateApiCall(func, settings, undefined, 'rest'); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -773,7 +773,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings, undefined, 'proto'); + const apiCall = gaxCreateApiCall(func, settings, undefined, 'proto'); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -815,7 +815,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, defaults.echo); + const apiCall = gaxCreateApiCall(func, defaults.echo); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -863,7 +863,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(failingFunc, settings); + const apiCall = gaxCreateApiCall(failingFunc, settings); const promise = apiCall({}, undefined); assert.strictEqual(harness.getSpans('google-gax').length, 0); @@ -911,7 +911,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(asyncFunc, settings); + const apiCall = gaxCreateApiCall(asyncFunc, settings); const promise = apiCall({}, undefined); // Verify the span is not ended prematurely while the call is in flight @@ -958,7 +958,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(cancellableFunc, settings); + const apiCall = gaxCreateApiCall(cancellableFunc, settings); const promise = apiCall({}, undefined); assert.strictEqual(typeof promise.cancel, 'function'); promise.cancel(); @@ -996,7 +996,7 @@ describe('createApiCall', () => { }; } - const apiCall = realCreateApiCall(func, settings); + const apiCall = gaxCreateApiCall(func, settings); await apiCall({}, undefined); const spans = harness.getSpans('google-gax'); @@ -1024,7 +1024,7 @@ describe('createApiCall', () => { return Object.assign(s, {cancel: () => {}}); }); - const apiCall = realCreateApiCall( + const apiCall = gaxCreateApiCall( spy as unknown as GRPCCall, settings, new StreamDescriptor(StreamType.SERVER_STREAMING, true), @@ -1075,7 +1075,7 @@ describe('createApiCall', () => { return Object.assign(s, {cancel: () => {}}); }); - const apiCall = realCreateApiCall( + const apiCall = gaxCreateApiCall( spy as unknown as GRPCCall, settings, new StreamDescriptor(StreamType.SERVER_STREAMING, true),