Skip to content

Commit dcef2ae

Browse files
authored
chore: Add X-Goog-Api-Client metric header to outgoing authorized http requests (#2763)
* chore: Add `X-Goog-Api-Client` metric header to outgoing requests * fix lint * remove quota project id headers from HTTP/2
1 parent 5affb73 commit dcef2ae

18 files changed

+86
-74
lines changed

src/auth/auth-api-request.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import { ProjectConfig, ProjectConfigServerResponse, UpdateProjectConfigRequest
4747
/** Firebase Auth request header. */
4848
const FIREBASE_AUTH_HEADERS = {
4949
'X-Client-Version': `Node/Admin/${utils.getSdkVersion()}`,
50-
'X-Goog-Api-Client': `gl-node/${process.versions.node} fire-admin/${utils.getSdkVersion()}`
5150
};
5251
/** Firebase Auth request timeout duration in milliseconds. */
5352
const FIREBASE_AUTH_TIMEOUT = 25000;

src/messaging/messaging-api-request-internal.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ const FIREBASE_MESSAGING_TIMEOUT = 15000;
3131
const FIREBASE_MESSAGING_HTTP_METHOD: HttpMethod = 'POST';
3232
const FIREBASE_MESSAGING_HEADERS = {
3333
'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`,
34-
'X-Goog-Api-Client': `gl-node/${process.versions.node} fire-admin/${getSdkVersion()}`,
3534
'access_token_auth': 'true',
3635
};
3736

src/utils/api-request.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import url = require('url');
2626
import { EventEmitter } from 'events';
2727
import { Readable } from 'stream';
2828
import * as zlibmod from 'zlib';
29+
import { getMetricsHeader } from '../utils/index';
2930

3031
/** Http method type definition. */
3132
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
@@ -1085,6 +1086,9 @@ export class AuthorizedHttpClient extends HttpClient {
10851086
if (!requestCopy.httpAgent && this.app.options.httpAgent) {
10861087
requestCopy.httpAgent = this.app.options.httpAgent;
10871088
}
1089+
1090+
requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader()
1091+
10881092
return super.send(requestCopy);
10891093
});
10901094
}
@@ -1108,6 +1112,8 @@ export class AuthorizedHttp2Client extends Http2Client {
11081112
const authHeader = 'Authorization';
11091113
requestCopy.headers[authHeader] = `Bearer ${token}`;
11101114

1115+
requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader()
1116+
11111117
return super.send(requestCopy);
11121118
});
11131119
}

src/utils/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export function getSdkVersion(): string {
3232
return sdkVersion;
3333
}
3434

35+
export function getMetricsHeader(): string {
36+
return `gl-node/${process.versions.node} fire-admin/${getSdkVersion()}`
37+
}
38+
3539
/**
3640
* Renames properties on an object given a mapping from old to new property names.
3741
*

test/unit/app-check/app-check-api-client-internal.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import * as sinon from 'sinon';
2323
import { HttpClient } from '../../../src/utils/api-request';
2424
import * as utils from '../utils';
2525
import * as mocks from '../../resources/mocks';
26-
import { getSdkVersion } from '../../../src/utils';
26+
import { getMetricsHeader, getSdkVersion } from '../../../src/utils';
2727

2828
import { FirebaseApp } from '../../../src/app/firebase-app';
2929
import { AppCheckApiClient, FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal';
@@ -46,6 +46,7 @@ describe('AppCheckApiClient', () => {
4646
'Authorization': 'Bearer mock-token',
4747
'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`,
4848
'x-goog-user-project': 'test-project',
49+
'X-Goog-Api-Client': getMetricsHeader(),
4950
};
5051

5152
const noProjectId = 'Failed to determine project ID. Initialize the SDK with service '

test/unit/auth/auth-api-request.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error
4242
import { ActionCodeSettingsBuilder } from '../../../src/auth/action-code-settings-builder';
4343
import { SAMLConfigServerResponse } from '../../../src/auth/auth-config';
4444
import { expectUserImportResult } from './user-import-builder.spec';
45-
import { getSdkVersion } from '../../../src/utils/index';
45+
import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index';
4646
import {
4747
UserImportRecord, OIDCAuthProviderConfig, SAMLAuthProviderConfig, OIDCUpdateAuthProviderRequest,
4848
SAMLUpdateAuthProviderRequest, UserIdentifier, UpdateRequest, UpdateMultiFactorInfoRequest,
@@ -863,12 +863,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => {
863863
const mockAccessToken: string = utils.generateRandomAccessToken();
864864
const expectedHeaders: {[key: string]: string} = {
865865
'X-Client-Version': `Node/Admin/${getSdkVersion()}`,
866-
'X-Goog-Api-Client': `gl-node/${process.versions.node} fire-admin/${getSdkVersion()}`,
866+
'X-Goog-Api-Client': getMetricsHeader(),
867867
'Authorization': 'Bearer ' + mockAccessToken,
868868
};
869869
const expectedHeadersEmulator: {[key: string]: string} = {
870870
'X-Client-Version': `Node/Admin/${getSdkVersion()}`,
871-
'X-Goog-Api-Client': `gl-node/${process.versions.node} fire-admin/${getSdkVersion()}`,
871+
'X-Goog-Api-Client': getMetricsHeader(),
872872
'Authorization': 'Bearer owner',
873873
};
874874
const callParams = (path: string, method: any, data: any): HttpRequestConfig => {

test/unit/data-connect/data-connect-api-client-internal.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { DataConnectApiClient, FirebaseDataConnectError }
2828
from '../../../src/data-connect/data-connect-api-client-internal';
2929
import { FirebaseApp } from '../../../src/app/firebase-app';
3030
import { ConnectorConfig } from '../../../src/data-connect';
31-
import { getSdkVersion } from '../../../src/utils';
31+
import { getMetricsHeader, getSdkVersion } from '../../../src/utils';
3232

3333
describe('DataConnectApiClient', () => {
3434

@@ -44,6 +44,7 @@ describe('DataConnectApiClient', () => {
4444
'Authorization': 'Bearer mock-token',
4545
'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`,
4646
'x-goog-user-project': 'test-project',
47+
'X-Goog-Api-Client': getMetricsHeader(),
4748
};
4849

4950
const noProjectId = 'Failed to determine project ID. Initialize the SDK with service '

test/unit/database/database.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Database, DatabaseService } from '../../../src/database/database';
2727
import { ServiceAccountCredential } from '../../../src/app/credential-internal';
2828
import * as utils from '../utils';
2929
import { HttpClient, HttpRequestConfig } from '../../../src/utils/api-request';
30+
import { getMetricsHeader } from '../../../src/utils';
3031

3132
describe('Database', () => {
3233
let mockApp: FirebaseApp;
@@ -332,6 +333,7 @@ describe('Database', () => {
332333
url,
333334
headers: {
334335
Authorization: 'Bearer ' + mockAccessToken,
336+
'X-Goog-Api-Client': getMetricsHeader(),
335337
},
336338
};
337339

@@ -485,6 +487,7 @@ describe('Database', () => {
485487
headers: {
486488
'Authorization': 'Bearer ' + mockAccessToken,
487489
'content-type': 'application/json; charset=utf-8',
490+
'X-Goog-Api-Client': getMetricsHeader(),
488491
},
489492
data,
490493
};

test/unit/eventarc/eventarc.spec.ts

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import * as mocks from '../../resources/mocks';
2727
import * as utils from '../utils';
2828
import * as chai from 'chai';
2929
import chaiExclude from 'chai-exclude';
30-
import { getSdkVersion } from '../../../src/utils/index';
30+
import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index';
3131

3232
const expect = chai.expect;
3333
chai.use(chaiExclude);
@@ -56,6 +56,14 @@ describe('eventarc', () => {
5656
let mockApp: FirebaseApp;
5757
let eventarc: Eventarc;
5858

59+
const getExpectedHeaders = (mockAccessToken: string): object => {
60+
return {
61+
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
62+
Authorization: 'Bearer ' + mockAccessToken,
63+
'X-Goog-Api-Client': getMetricsHeader(),
64+
}
65+
}
66+
5967
before(() => {
6068
mockApp = mocks.app();
6169
eventarc = new Eventarc(mockApp);
@@ -133,10 +141,7 @@ describe('eventarc', () => {
133141
method: 'POST',
134142
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
135143
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
136-
headers: {
137-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
138-
Authorization: 'Bearer ' + mockAccessToken
139-
}
144+
headers: getExpectedHeaders(mockAccessToken)
140145
});
141146
});
142147

@@ -151,10 +156,7 @@ describe('eventarc', () => {
151156
method: 'POST',
152157
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
153158
data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`,
154-
headers: {
155-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
156-
Authorization: 'Bearer ' + mockAccessToken
157-
}
159+
headers: getExpectedHeaders(mockAccessToken)
158160
});
159161
});
160162
});
@@ -196,10 +198,7 @@ describe('eventarc', () => {
196198
method: 'POST',
197199
url: 'https://eventarcpublishing.googleapis.com/v1/projects/other-project-id/locations/us-west1/channels/my-channel2:publishEvents',
198200
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
199-
headers: {
200-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
201-
Authorization: 'Bearer ' + mockAccessToken
202-
}
201+
headers: getExpectedHeaders(mockAccessToken)
203202
});
204203
});
205204

@@ -214,10 +213,7 @@ describe('eventarc', () => {
214213
method: 'POST',
215214
url: 'https://eventarcpublishing.googleapis.com/v1/projects/other-project-id/locations/us-west1/channels/my-channel2:publishEvents',
216215
data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`,
217-
headers: {
218-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
219-
Authorization: 'Bearer ' + mockAccessToken
220-
}
216+
headers: getExpectedHeaders(mockAccessToken)
221217
});
222218
});
223219
});
@@ -259,10 +255,7 @@ describe('eventarc', () => {
259255
method: 'POST',
260256
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-west1/channels/my-channel:publishEvents',
261257
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
262-
headers: {
263-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
264-
Authorization: 'Bearer ' + mockAccessToken
265-
}
258+
headers: getExpectedHeaders(mockAccessToken)
266259
});
267260
});
268261

@@ -277,10 +270,7 @@ describe('eventarc', () => {
277270
method: 'POST',
278271
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-west1/channels/my-channel:publishEvents',
279272
data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`,
280-
headers: {
281-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
282-
Authorization: 'Bearer ' + mockAccessToken
283-
}
273+
headers: getExpectedHeaders(mockAccessToken)
284274
});
285275
});
286276
});
@@ -322,10 +312,7 @@ describe('eventarc', () => {
322312
method: 'POST',
323313
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/my-channel:publishEvents',
324314
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
325-
headers: {
326-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
327-
Authorization: 'Bearer ' + mockAccessToken
328-
}
315+
headers: getExpectedHeaders(mockAccessToken)
329316
});
330317
});
331318

@@ -340,10 +327,7 @@ describe('eventarc', () => {
340327
method: 'POST',
341328
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/my-channel:publishEvents',
342329
data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`,
343-
headers: {
344-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
345-
Authorization: 'Bearer ' + mockAccessToken
346-
}
330+
headers: getExpectedHeaders(mockAccessToken)
347331
});
348332
});
349333
});
@@ -478,10 +462,7 @@ describe('eventarc', () => {
478462
method: 'POST',
479463
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
480464
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
481-
headers: {
482-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
483-
Authorization: 'Bearer ' + mockAccessToken
484-
}
465+
headers: getExpectedHeaders(mockAccessToken)
485466
});
486467
});
487468

@@ -498,10 +479,7 @@ describe('eventarc', () => {
498479
method: 'POST',
499480
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
500481
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
501-
headers: {
502-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
503-
Authorization: 'Bearer ' + mockAccessToken
504-
}
482+
headers: getExpectedHeaders(mockAccessToken)
505483
});
506484
});
507485
});
@@ -542,10 +520,7 @@ describe('eventarc', () => {
542520
method: 'POST',
543521
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
544522
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
545-
headers: {
546-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
547-
Authorization: 'Bearer ' + mockAccessToken
548-
}
523+
headers: getExpectedHeaders(mockAccessToken)
549524
});
550525
});
551526

@@ -562,10 +537,7 @@ describe('eventarc', () => {
562537
method: 'POST',
563538
url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents',
564539
data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`,
565-
headers: {
566-
'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(),
567-
Authorization: 'Bearer ' + mockAccessToken
568-
}
540+
headers: getExpectedHeaders(mockAccessToken)
569541
});
570542
});
571543
});

test/unit/extensions/extensions-api-client-internal.spec.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import * as utils from '../utils';
2222
import * as mocks from '../../resources/mocks';
2323
import { FirebaseApp } from '../../../src/app/firebase-app';
2424
import { ExtensionsApiClient, FirebaseExtensionsError } from '../../../src/extensions/extensions-api-client-internal';
25-
import { HttpClient, HttpRequestConfig } from '../../../src/utils/api-request';
25+
import { HttpClient } from '../../../src/utils/api-request';
2626
import { SettableProcessingState } from '../../../src/extensions/extensions-api';
27+
import { getMetricsHeader, getSdkVersion } from '../../../src/utils';
2728

2829
const testProjectId = 'test-project';
2930
const testInstanceId = 'test-instance';
@@ -38,6 +39,13 @@ describe('Extension API client', () => {
3839
projectId: 'test-project',
3940
serviceAccountId: '[email protected]'
4041
};
42+
43+
const EXPECTED_HEADERS = {
44+
'Authorization': 'Bearer mock-token',
45+
'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`,
46+
'x-goog-user-project': 'test-project',
47+
'X-Goog-Api-Client': getMetricsHeader(),
48+
}
4149

4250
before(() => {
4351
app = mocks.appWithOptions(mockOptions);
@@ -71,14 +79,19 @@ describe('Extension API client', () => {
7179
detailMessage: 'done processing',
7280
},
7381
}
74-
const expected = sinon.match((req: HttpRequestConfig) => {
75-
const url = 'https://firebaseextensions.googleapis.com/' +
76-
'v1beta/projects/test-project/instances/test-instance/runtimeData';
77-
return req.method == 'PATCH' && req.url == url && req.data == testRuntimeData;
78-
}, 'Incorrect URL or Method');
79-
httpClientStub.withArgs(expected).resolves(utils.responseFrom(testRuntimeData, 200));
80-
await expect(apiClient.updateRuntimeData(testProjectId, testInstanceId, testRuntimeData))
81-
.to.eventually.deep.equal(testRuntimeData);
82+
const url = 'https://firebaseextensions.googleapis.com/' +
83+
'v1beta/projects/test-project/instances/test-instance/runtimeData';
84+
httpClientStub = httpClientStub.resolves(utils.responseFrom(testRuntimeData, 200));
85+
return apiClient.updateRuntimeData(testProjectId, testInstanceId, testRuntimeData)
86+
.then((runtimeData) => {
87+
expect(runtimeData).to.deep.equal(testRuntimeData)
88+
expect(httpClientStub).to.have.been.calledOnce.and.calledWith({
89+
method: 'PATCH',
90+
url: url,
91+
headers: EXPECTED_HEADERS,
92+
data: testRuntimeData
93+
})
94+
})
8295
});
8396

8497
it('should convert errors in FirebaseErrors', async () => {

test/unit/functions/functions-api-client-internal.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import * as chai from 'chai';
2222
import * as sinon from 'sinon';
2323
import * as utils from '../utils';
2424
import * as mocks from '../../resources/mocks';
25-
import { getSdkVersion } from '../../../src/utils';
25+
import { getSdkVersion, getMetricsHeader } from '../../../src/utils';
2626

2727
import { FirebaseApp } from '../../../src/app/firebase-app';
2828
import { FirebaseFunctionsError, FunctionsApiClient, Task } from '../../../src/functions/functions-api-client-internal';
@@ -47,6 +47,7 @@ describe('FunctionsApiClient', () => {
4747
'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`,
4848
'Authorization': 'Bearer mock-token',
4949
'x-goog-user-project': 'test-project',
50+
'X-Goog-Api-Client': getMetricsHeader(),
5051
};
5152

5253
const noProjectId = 'Failed to determine project ID. Initialize the SDK with service '

test/unit/installations/installations-request-handler.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import * as mocks from '../../resources/mocks';
2929
import { FirebaseApp } from '../../../src/app/firebase-app';
3030
import { HttpClient } from '../../../src/utils/api-request';
3131
import { FirebaseInstallationsRequestHandler } from '../../../src/installations/installations-request-handler';
32+
import { getMetricsHeader } from '../../../src/utils';
3233

3334
chai.should();
3435
chai.use(sinonChai);
@@ -57,6 +58,7 @@ describe('FirebaseInstallationsRequestHandler', () => {
5758
mockApp = mocks.app();
5859
expectedHeaders = {
5960
Authorization: 'Bearer ' + mockAccessToken,
61+
'X-Goog-Api-Client': getMetricsHeader(),
6062
};
6163
return mockApp.INTERNAL.getToken();
6264
});

0 commit comments

Comments
 (0)