From 6fd08a36a5324c0ed98d73dc638985197f48fb0a Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 18:17:33 +0100 Subject: [PATCH 1/8] fix(ci): use allow-listed codecov-action@v4 ref instead of a SHA pin --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54a1f115..3415f738 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -234,7 +234,7 @@ jobs: - name: Upload coverage to codecov if: matrix.dart-version == 'stable' continue-on-error: true - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f + uses: codecov/codecov-action@v4 with: files: coverage.lcov flags: unittests From 005b4a72ec2069bf586e1a5c6cbe4a1876246ba4 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 21:38:35 +0100 Subject: [PATCH 2/8] test(ci): try pinning codecov-action to the actual v4 release SHA --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3415f738..88402967 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -234,7 +234,7 @@ jobs: - name: Upload coverage to codecov if: matrix.dart-version == 'stable' continue-on-error: true - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4 with: files: coverage.lcov flags: unittests From 01a5a8529d731a8039ff51b09dccc3af87a561b2 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 22:03:23 +0100 Subject: [PATCH 3/8] Revert "test(ci): try pinning codecov-action to the actual v4 release SHA" This reverts commit 005b4a72ec2069bf586e1a5c6cbe4a1876246ba4. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88402967..3415f738 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -234,7 +234,7 @@ jobs: - name: Upload coverage to codecov if: matrix.dart-version == 'stable' continue-on-error: true - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4 + uses: codecov/codecov-action@v4 with: files: coverage.lcov flags: unittests From 63e980eade811b0acc17c9a6c407408d914b6767 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 22:20:51 +0100 Subject: [PATCH 4/8] feat(firestore): add Settings.headers to attach custom request headers --- packages/google_cloud_firestore/CHANGELOG.md | 1 + .../lib/src/firestore.dart | 10 ++++++ .../lib/src/firestore_http_client.dart | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/packages/google_cloud_firestore/CHANGELOG.md b/packages/google_cloud_firestore/CHANGELOG.md index 7114b835..f00db6df 100644 --- a/packages/google_cloud_firestore/CHANGELOG.md +++ b/packages/google_cloud_firestore/CHANGELOG.md @@ -1,5 +1,6 @@ ## Unreleased +- Added `Settings.headers` to attach custom HTTP headers to every outgoing Firestore request. - Fixed intermittent `ClientException: Connection closed before full header was received` on queries and aggregations under high concurrency; these now retry with backoff. - Fixed `Firestore.getAll()` retrying transient errors indefinitely; it now retries a bounded number of times before surfacing the error. - Updated `Transaction.delete` and `Transaction.update` type constraints to accept `DocumentReference`. (thanks to @Levin-Me) diff --git a/packages/google_cloud_firestore/lib/src/firestore.dart b/packages/google_cloud_firestore/lib/src/firestore.dart index 50dcf795..fd2b7fbe 100644 --- a/packages/google_cloud_firestore/lib/src/firestore.dart +++ b/packages/google_cloud_firestore/lib/src/firestore.dart @@ -116,6 +116,7 @@ class Settings { this.ignoreUndefinedProperties = false, this.useBigInt = false, this.environmentOverride, + this.headers, }); /// The project ID from the Google Developer's Console, e.g. 'grape-spaceship-123'. @@ -186,6 +187,13 @@ class Settings { /// ``` final Map? environmentOverride; + /// Additional HTTP headers to send with every Firestore request. + /// + /// Useful for attaching usage-tracking or tracing headers (e.g. + /// `X-Firebase-Client`, `X-Goog-Api-Client`) without needing to supply a + /// custom HTTP client. + final Map? headers; + /// Creates a copy of this Settings with the given fields replaced. Settings copyWith({ String? projectId, @@ -196,6 +204,7 @@ class Settings { bool? ignoreUndefinedProperties, bool? useBigInt, Map? environmentOverride, + Map? headers, }) { return Settings( projectId: projectId ?? this.projectId, @@ -207,6 +216,7 @@ class Settings { ignoreUndefinedProperties ?? this.ignoreUndefinedProperties, useBigInt: useBigInt ?? this.useBigInt, environmentOverride: environmentOverride ?? this.environmentOverride, + headers: headers ?? this.headers, ); } diff --git a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart index f41c86cb..c36e4cde 100644 --- a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart +++ b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart @@ -42,6 +42,33 @@ class _RequestImpl extends BaseRequest { } } +/// HTTP client wrapper that adds custom headers to every outgoing Firestore +/// request. +/// +/// Used to attach [Settings.headers] (e.g. usage-tracking headers set by a +/// caller like the Firebase Admin SDK) without altering how the underlying +/// client authenticates requests. +@internal +class FirestoreRequestClient extends BaseClient + implements googleapis_auth.AuthClient { + FirestoreRequestClient(this._client, this._headers); + + final googleapis_auth.AuthClient _client; + final Map _headers; + + @override + googleapis_auth.AccessCredentials get credentials => _client.credentials; + + @override + Future send(BaseRequest request) { + request.headers.addAll(_headers); + return _client.send(request); + } + + @override + void close() => _client.close(); +} + /// HTTP client wrapper that adds Firebase emulator authentication. /// /// This client wraps another HTTP client and automatically adds the @@ -156,6 +183,15 @@ class FirestoreHttpClient { /// Creates the appropriate HTTP client based on emulator configuration. Future _createClient() async { + final client = await _createBaseClient(); + + final headers = _settings.headers; + if (headers == null || headers.isEmpty) return client; + + return FirestoreRequestClient(client, headers); + } + + Future _createBaseClient() async { if (_isUsingEmulator) { // Emulator: Create unauthenticated client. return EmulatorClient(Client()); From 14f9e46bdc368c91b8a19f43805580947910bd79 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 23:21:22 +0100 Subject: [PATCH 5/8] fix(firestore): append caller headers instead of overwriting existing X-Goog-Api-Client value --- .../lib/src/firestore_http_client.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart index c36e4cde..e61d7861 100644 --- a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart +++ b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart @@ -48,6 +48,12 @@ class _RequestImpl extends BaseRequest { /// Used to attach [Settings.headers] (e.g. usage-tracking headers set by a /// caller like the Firebase Admin SDK) without altering how the underlying /// client authenticates requests. +/// +/// Values are appended (space-joined) onto any existing header of the same +/// name rather than replacing it, since the transport layer +/// (`package:google_cloud_rpc`) already sets its own `X-Goog-Api-Client` +/// value identifying this library — matching the multi-token convention +/// that header uses (e.g. `gl-dart/3.9 gax/1.0 gccl/0.5 fire-admin/0.5`). @internal class FirestoreRequestClient extends BaseClient implements googleapis_auth.AuthClient { @@ -61,7 +67,12 @@ class FirestoreRequestClient extends BaseClient @override Future send(BaseRequest request) { - request.headers.addAll(_headers); + for (final entry in _headers.entries) { + final existing = request.headers[entry.key]; + request.headers[entry.key] = existing == null + ? entry.value + : '$existing ${entry.value}'; + } return _client.send(request); } From d210b9d655a8ab72cec580dcfb19854d7c393324 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 23:29:03 +0100 Subject: [PATCH 6/8] test(firestore): add unit tests for FirestoreRequestClient header merging --- .../test/firestore_request_client_test.dart | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 packages/google_cloud_firestore/test/firestore_request_client_test.dart diff --git a/packages/google_cloud_firestore/test/firestore_request_client_test.dart b/packages/google_cloud_firestore/test/firestore_request_client_test.dart new file mode 100644 index 00000000..14c76d8f --- /dev/null +++ b/packages/google_cloud_firestore/test/firestore_request_client_test.dart @@ -0,0 +1,128 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:google_cloud_firestore/src/firestore_http_client.dart'; +import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:test/test.dart'; + +/// Wraps a plain [Client] (such as [MockClient]) so it satisfies the +/// [googleapis_auth.AuthClient] interface [FirestoreRequestClient] requires. +class _FakeAuthClient extends BaseClient implements googleapis_auth.AuthClient { + _FakeAuthClient(this._inner); + + final Client _inner; + bool closed = false; + + @override + googleapis_auth.AccessCredentials get credentials => + throw UnimplementedError(); + + @override + Future send(BaseRequest request) => _inner.send(request); + + @override + void close() { + closed = true; + _inner.close(); + } +} + +void main() { + group('FirestoreRequestClient', () { + test('sets a header that has no existing value', () async { + late Request captured; + final mock = MockClient((request) async { + captured = request; + return Response('{}', 200); + }); + + final client = FirestoreRequestClient(_FakeAuthClient(mock), { + 'X-Firebase-Client': 'fire-admin-dart/1.0.0', + }); + + await client.get(Uri.parse('https://firestore.googleapis.com/')); + + expect(captured.headers['X-Firebase-Client'], 'fire-admin-dart/1.0.0'); + }); + + test( + 'appends to (rather than overwrites) an existing header value', + () async { + late Request captured; + final mock = MockClient((request) async { + captured = request; + return Response('{}', 200); + }); + + final client = FirestoreRequestClient(_FakeAuthClient(mock), { + 'X-Goog-Api-Client': 'fire-admin/1.0.0', + }); + + final request = Request( + 'GET', + Uri.parse('https://firestore.googleapis.com/'), + ); + // Simulates the value package:google_cloud_rpc's ServiceClient + // already sets on every request before this wrapper runs. + request.headers['X-Goog-Api-Client'] = + 'gl-dart/3.9 gax/1.0 rest/1.0 gapic/1.0'; + + await client.send(request); + + expect( + captured.headers['X-Goog-Api-Client'], + 'gl-dart/3.9 gax/1.0 rest/1.0 gapic/1.0 fire-admin/1.0.0', + ); + }, + ); + + test('applies multiple headers independently', () async { + late Request captured; + final mock = MockClient((request) async { + captured = request; + return Response('{}', 200); + }); + + final client = FirestoreRequestClient(_FakeAuthClient(mock), { + 'X-Firebase-Client': 'fire-admin-dart/1.0.0', + 'X-Goog-Api-Client': 'fire-admin/1.0.0', + }); + + await client.get(Uri.parse('https://firestore.googleapis.com/')); + + expect(captured.headers['X-Firebase-Client'], 'fire-admin-dart/1.0.0'); + expect(captured.headers['X-Goog-Api-Client'], 'fire-admin/1.0.0'); + }); + + test('delegates close() to the inner client', () { + final mock = MockClient((request) async => Response('{}', 200)); + final inner = _FakeAuthClient(mock); + final client = FirestoreRequestClient(inner, const {}); + + client.close(); + + expect(inner.closed, isTrue); + }); + + test('delegates credentials to the inner client', () { + final mock = MockClient((request) async => Response('{}', 200)); + final inner = _FakeAuthClient(mock); + final client = FirestoreRequestClient(inner, const {}); + + expect(() => client.credentials, throwsUnimplementedError); + }); + }); +} From 2ac3e1ae24bed4f0d6d014a46722316534fc25a5 Mon Sep 17 00:00:00 2001 From: demolaf Date: Thu, 23 Jul 2026 23:36:24 +0100 Subject: [PATCH 7/8] fix(firestore): address review feedback on Settings.headers --- packages/google_cloud_firestore/lib/src/firestore.dart | 4 +++- .../google_cloud_firestore/lib/src/firestore_http_client.dart | 2 +- .../test/firestore_request_client_test.dart | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/google_cloud_firestore/lib/src/firestore.dart b/packages/google_cloud_firestore/lib/src/firestore.dart index fd2b7fbe..ea290f45 100644 --- a/packages/google_cloud_firestore/lib/src/firestore.dart +++ b/packages/google_cloud_firestore/lib/src/firestore.dart @@ -231,7 +231,8 @@ class Settings { ssl == other.ssl && credential == other.credential && ignoreUndefinedProperties == other.ignoreUndefinedProperties && - useBigInt == other.useBigInt; + useBigInt == other.useBigInt && + const MapEquality().equals(headers, other.headers); @override int get hashCode => Object.hash( @@ -240,6 +241,7 @@ class Settings { host, ssl, credential, + const MapEquality().hash(headers), ignoreUndefinedProperties, useBigInt, ); diff --git a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart index e61d7861..eb6ce9b1 100644 --- a/packages/google_cloud_firestore/lib/src/firestore_http_client.dart +++ b/packages/google_cloud_firestore/lib/src/firestore_http_client.dart @@ -199,7 +199,7 @@ class FirestoreHttpClient { final headers = _settings.headers; if (headers == null || headers.isEmpty) return client; - return FirestoreRequestClient(client, headers); + return FirestoreRequestClient(client, Map.unmodifiable(headers)); } Future _createBaseClient() async { diff --git a/packages/google_cloud_firestore/test/firestore_request_client_test.dart b/packages/google_cloud_firestore/test/firestore_request_client_test.dart index 14c76d8f..53e57adb 100644 --- a/packages/google_cloud_firestore/test/firestore_request_client_test.dart +++ b/packages/google_cloud_firestore/test/firestore_request_client_test.dart @@ -75,8 +75,7 @@ void main() { 'GET', Uri.parse('https://firestore.googleapis.com/'), ); - // Simulates the value package:google_cloud_rpc's ServiceClient - // already sets on every request before this wrapper runs. + // Simulates the value package:google_cloud_rpc already sets. request.headers['X-Goog-Api-Client'] = 'gl-dart/3.9 gax/1.0 rest/1.0 gapic/1.0'; From e4300807b759972106f0cbd78f3fd33f11a12a3b Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:17:56 +0100 Subject: [PATCH 8/8] fix(admin): attach usage-tracking headers to Firestore requests (#308) * fix(admin): attach usage-tracking headers to Firestore requests * refactor(admin): share usage-tracking header values between FirebaseUserAgentClient and Firestore * fix(admin): avoid duplicate gl-dart tag when tagging Firestore requests * fix(admin): address review feedback, merge rather than replace user headers * docs(admin): wrap CHANGELOG entry to match surrounding style * refactor(admin): extract shared identity strings to avoid duplicating them across the two header maps --- packages/firebase_admin_sdk/CHANGELOG.md | 2 ++ packages/firebase_admin_sdk/lib/src/app.dart | 1 - .../src/app/firebase_user_agent_client.dart | 4 +-- .../lib/src/firestore/firestore.dart | 5 +++ .../lib/src/utils/utils.dart | 32 +++++++++++++++++ .../test/unit/firestore/firestore_test.dart | 34 +++++++++++++++++++ 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/packages/firebase_admin_sdk/CHANGELOG.md b/packages/firebase_admin_sdk/CHANGELOG.md index 48d8af3e..3aeb2f75 100644 --- a/packages/firebase_admin_sdk/CHANGELOG.md +++ b/packages/firebase_admin_sdk/CHANGELOG.md @@ -1,5 +1,7 @@ ## 0.5.4-wip +- Fixed `Firestore` requests not carrying the SDK's usage-tracking headers + (`X-Firebase-Client`, `X-Goog-Api-Client`). - Update dependency `googleapis_auth: ^2.3.3` to fix `auth/insufficient-permission` errors with Application Default Credentials that have no quota project set. diff --git a/packages/firebase_admin_sdk/lib/src/app.dart b/packages/firebase_admin_sdk/lib/src/app.dart index 11c330fc..086e3dd6 100644 --- a/packages/firebase_admin_sdk/lib/src/app.dart +++ b/packages/firebase_admin_sdk/lib/src/app.dart @@ -35,7 +35,6 @@ import '../messaging.dart'; import '../security_rules.dart'; import '../storage.dart'; import 'utils/utils.dart'; -import 'version.g.dart'; part 'app/app_exception.dart'; part 'app/app_options.dart'; diff --git a/packages/firebase_admin_sdk/lib/src/app/firebase_user_agent_client.dart b/packages/firebase_admin_sdk/lib/src/app/firebase_user_agent_client.dart index 5b964f2e..c627e48b 100644 --- a/packages/firebase_admin_sdk/lib/src/app/firebase_user_agent_client.dart +++ b/packages/firebase_admin_sdk/lib/src/app/firebase_user_agent_client.dart @@ -32,9 +32,7 @@ class FirebaseUserAgentClient extends BaseClient @override Future send(BaseRequest request) { - request.headers['X-Firebase-Client'] = 'fire-admin-dart/$packageVersion'; - request.headers['X-Goog-Api-Client'] = - 'gl-dart/$dartVersion fire-admin/$packageVersion'; + request.headers.addAll(firebaseUserAgentHeaders); return _client.send(request); } diff --git a/packages/firebase_admin_sdk/lib/src/firestore/firestore.dart b/packages/firebase_admin_sdk/lib/src/firestore/firestore.dart index f47b5c30..0df9a4ee 100644 --- a/packages/firebase_admin_sdk/lib/src/firestore/firestore.dart +++ b/packages/firebase_admin_sdk/lib/src/firestore/firestore.dart @@ -19,6 +19,7 @@ import 'package:google_cloud_firestore/google_cloud_firestore.dart' import 'package:meta/meta.dart'; import '../app.dart'; +import '../utils/utils.dart'; /// Default database ID used by Firestore const String kDefaultDatabaseId = '(default)'; @@ -152,6 +153,10 @@ class Firestore implements FirebaseService { settings = settings.copyWith(projectId: projectId); } + settings = settings.copyWith( + headers: {...firestoreUsageTrackingHeaders, ...?settings.headers}, + ); + return settings; } diff --git a/packages/firebase_admin_sdk/lib/src/utils/utils.dart b/packages/firebase_admin_sdk/lib/src/utils/utils.dart index 9ecf51a6..6a66c4a7 100644 --- a/packages/firebase_admin_sdk/lib/src/utils/utils.dart +++ b/packages/firebase_admin_sdk/lib/src/utils/utils.dart @@ -14,10 +14,42 @@ import 'dart:io'; +import '../version.g.dart'; + /// The current Dart SDK version in semver format (e.g. "3.3.0"). String get dartVersion => Platform.version.split(RegExp('[^0-9]')).take(3).join('.'); +/// The Firebase Admin SDK's `X-Firebase-Client` identity. +const String _fireAdminFirebaseClientId = 'fire-admin-dart/$packageVersion'; + +/// The Firebase Admin SDK's tag within `X-Goog-Api-Client`. +const String _fireAdminApiClientTag = 'fire-admin/$packageVersion'; + +/// Headers that identify a request as originating from this SDK, for +/// Firebase backend usage tracking. +/// +/// Used by `FirebaseUserAgentClient`, which wraps HTTP clients (Auth, +/// Messaging, and other services that go through `FirebaseApp.client`) that +/// don't already set a client-identification header of their own. +final Map firebaseUserAgentHeaders = Map.unmodifiable({ + 'X-Firebase-Client': _fireAdminFirebaseClientId, + 'X-Goog-Api-Client': 'gl-dart/$dartVersion $_fireAdminApiClientTag', +}); + +/// Headers to attach to Firestore requests to identify them as originating +/// from this SDK, for Firebase backend usage tracking. +/// +/// Unlike [firebaseUserAgentHeaders], `X-Goog-Api-Client` here omits the +/// `gl-dart/{version}` runtime tag: `google_cloud_firestore`'s +/// `FirestoreRequestClient` appends this value onto one that +/// `package:google_cloud_rpc` already set (which already carries that tag), +/// so repeating it here would duplicate it. +const Map firestoreUsageTrackingHeaders = { + 'X-Firebase-Client': _fireAdminFirebaseClientId, + 'X-Goog-Api-Client': _fireAdminApiClientTag, +}; + /// Generates the update mask for the provided object. /// Note this will ignore the last key with value `null`. List _generateUpdateMask(Object? obj, String root) { diff --git a/packages/firebase_admin_sdk/test/unit/firestore/firestore_test.dart b/packages/firebase_admin_sdk/test/unit/firestore/firestore_test.dart index 63de8ab7..c833f03e 100644 --- a/packages/firebase_admin_sdk/test/unit/firestore/firestore_test.dart +++ b/packages/firebase_admin_sdk/test/unit/firestore/firestore_test.dart @@ -16,6 +16,7 @@ import 'dart:io'; import 'package:firebase_admin_sdk/firebase_admin_sdk.dart'; import 'package:firebase_admin_sdk/src/firestore/firestore.dart'; +import 'package:firebase_admin_sdk/src/utils/utils.dart'; import 'package:google_cloud_firestore/google_cloud_firestore.dart' as gfs; import 'package:googleapis_auth/auth_io.dart' as auth; import 'package:mocktail/mocktail.dart'; @@ -254,6 +255,39 @@ void main() { }); }); + group('usage-tracking headers', () { + test('are attached when no user headers are provided', () { + final settings = firestoreService.buildSettingsForTesting('db', null); + + expect(settings.headers, firestoreUsageTrackingHeaders); + }); + + test('are merged with, not replaced by, user-provided headers', () { + final settings = firestoreService.buildSettingsForTesting( + 'db', + const gfs.Settings(headers: {'X-Trace-Id': 'abc123'}), + ); + + expect(settings.headers, { + ...firestoreUsageTrackingHeaders, + 'X-Trace-Id': 'abc123', + }); + }); + + test('can be overridden by a user-provided header of the same name', () { + final settings = firestoreService.buildSettingsForTesting( + 'db', + const gfs.Settings(headers: {'X-Firebase-Client': 'custom-value'}), + ); + + expect(settings.headers!['X-Firebase-Client'], 'custom-value'); + expect( + settings.headers!['X-Goog-Api-Client'], + firestoreUsageTrackingHeaders['X-Goog-Api-Client'], + ); + }); + }); + group('lifecycle', () { test('should terminate all databases on delete', () async { final db1 = firestoreService.getDatabase('lifecycle-1');