Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/firestore/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export function configureFirestore(firestore: Firestore): void {
firestore._databaseId,
firestore._app?.options.appId || '',
firestore._persistenceKey,
firestore._app?.options.apiKey,
settings
);
if (!firestore._componentsProvider) {
Expand Down
3 changes: 2 additions & 1 deletion packages/firestore/src/core/database_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export class DatabaseInfo {
readonly autoDetectLongPolling: boolean,
readonly longPollingOptions: ExperimentalLongPollingOptions,
readonly useFetchStreams: boolean,
readonly isUsingEmulator: boolean
readonly isUsingEmulator: boolean,
readonly apiKey: string | undefined
) {}
}

Expand Down
8 changes: 6 additions & 2 deletions packages/firestore/src/core/firestore_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ export class FirestoreClient {
* an async I/O to complete).
*/
public asyncQueue: AsyncQueue,
private databaseInfo: DatabaseInfo,
/**
* @internal
* Exposed for testing
*/
public _databaseInfo: DatabaseInfo,
componentProvider?: {
_offline: OfflineComponentProvider;
_online: OnlineComponentProvider;
Expand All @@ -167,7 +171,7 @@ export class FirestoreClient {
get configuration(): ComponentConfiguration {
return {
asyncQueue: this.asyncQueue,
databaseInfo: this.databaseInfo,
databaseInfo: this._databaseInfo,
clientId: this.clientId,
authCredentials: this.authCredentials,
appCheckCredentials: this.appCheckCredentials,
Expand Down
5 changes: 4 additions & 1 deletion packages/firestore/src/lite-api/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function getDatastore(firestore: FirestoreService): Datastore {
firestore._databaseId,
firestore.app.options.appId || '',
firestore._persistenceKey,
firestore.app.options.apiKey,
firestore._freezeSettings()
);
const connection = newConnection(databaseInfo);
Expand Down Expand Up @@ -108,6 +109,7 @@ export function makeDatabaseInfo(
databaseId: DatabaseId,
appId: string,
persistenceKey: string,
apiKey: string | undefined,
settings: FirestoreSettingsImpl
): DatabaseInfo {
return new DatabaseInfo(
Expand All @@ -120,6 +122,7 @@ export function makeDatabaseInfo(
settings.experimentalAutoDetectLongPolling,
cloneLongPollingOptions(settings.experimentalLongPollingOptions),
settings.useFetchStreams,
settings.isUsingEmulator
settings.isUsingEmulator,
apiKey
);
}
18 changes: 13 additions & 5 deletions packages/firestore/src/platform/node/grpc_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ function createMetadata(
databasePath: string,
authToken: Token | null,
appCheckToken: Token | null,
appId: string
appId: string,
apiKey: string | undefined
): grpc.Metadata {
hardAssert(
authToken === null || authToken.type === 'OAuth',
Expand All @@ -69,6 +70,9 @@ function createMetadata(
// 11 from Google3.
metadata.set('Google-Cloud-Resource-Prefix', databasePath);
metadata.set('x-goog-request-params', databasePath);
if (apiKey) {
metadata.set('X-Goog-Api-Key', apiKey);
}
return metadata;
}

Expand Down Expand Up @@ -100,7 +104,8 @@ export class GrpcConnection implements Connection {
this.databasePath = `projects/${databaseInfo.databaseId.projectId}/databases/${databaseInfo.databaseId.database}`;
}

private ensureActiveStub(): GeneratedGrpcStub {
/** made protected for testing */
protected ensureActiveStub(): GeneratedGrpcStub {
if (!this.cachedStub) {
logDebug(LOG_TAG, 'Creating Firestore stub.');
const credentials = this.databaseInfo.ssl
Expand All @@ -127,7 +132,8 @@ export class GrpcConnection implements Connection {
this.databasePath,
authToken,
appCheckToken,
this.databaseInfo.appId
this.databaseInfo.appId,
this.databaseInfo.apiKey
);
const jsonRequest = { database: this.databasePath, ...request };

Expand Down Expand Up @@ -187,7 +193,8 @@ export class GrpcConnection implements Connection {
this.databasePath,
authToken,
appCheckToken,
this.databaseInfo.appId
this.databaseInfo.appId,
this.databaseInfo.apiKey
);
const jsonRequest = { ...request, database: this.databasePath };
const stream = stub[rpcName](jsonRequest, metadata);
Expand Down Expand Up @@ -239,7 +246,8 @@ export class GrpcConnection implements Connection {
this.databasePath,
authToken,
appCheckToken,
this.databaseInfo.appId
this.databaseInfo.appId,
this.databaseInfo.apiKey
);
const grpcStream = stub[rpcName](metadata);

Expand Down
10 changes: 8 additions & 2 deletions packages/firestore/src/remote/rest_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export abstract class RestConnection implements Connection {
protected readonly baseUrl: string;
private readonly databasePath: string;
private readonly requestParams: string;
private readonly apiKey: string | undefined;

get shouldResourcePathBeIncludedInRequest(): boolean {
// Both `invokeRPC()` and `invokeStreamingRPC()` use their `path` arguments to determine
Expand All @@ -82,6 +83,7 @@ export abstract class RestConnection implements Connection {
this.databaseId.database === DEFAULT_DATABASE_NAME
? `project_id=${projectId}`
: `project_id=${projectId}&database_id=${databaseId}`;
this.apiKey = databaseInfo.apiKey;
}

invokeRPC<Req, Resp>(
Expand Down Expand Up @@ -194,13 +196,17 @@ export abstract class RestConnection implements Connection {
_forwardCredentials: boolean
): Promise<Resp>;

private makeUrl(rpcName: string, path: string): string {
protected makeUrl(rpcName: string, path: string): string {
const urlRpcName = RPC_NAME_URL_MAPPING[rpcName];
debugAssert(
urlRpcName !== undefined,
'Unknown REST mapping for: ' + rpcName
);
return `${this.baseUrl}/${RPC_URL_VERSION}/${path}:${urlRpcName}`;
let url = `${this.baseUrl}/${RPC_URL_VERSION}/${path}:${urlRpcName}`;
if (this.apiKey) {
url = `${url}?key=${encodeURIComponent(this.apiKey)}`;
}
return url;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion packages/firestore/test/integration/api/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
enableIndexedDbPersistence,
setDoc,
memoryLocalCache,
getDocFromCache
getDocFromCache,
ensureFirestoreConfigured
} from '../util/firebase_export';
import { DEFAULT_SETTINGS } from '../util/settings';

Expand Down Expand Up @@ -200,4 +201,17 @@ describe('Firestore Provider', () => {

return terminate(firestore).then(() => terminate(firestore));
});

it('passes API key to database info', () => {
const app = initializeApp(
{ apiKey: 'fake-api-key-x', projectId: 'test-project' },
'test-app-getFirestore-x'
);
const fs = getFirestore(app);
ensureFirestoreConfigured(fs);

expect(fs._firestoreClient?._databaseInfo.apiKey).to.equal(
'fake-api-key-x'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export function getDefaultDatabaseInfo(): DatabaseInfo {
DEFAULT_SETTINGS.experimentalLongPollingOptions ?? {}
),
/*use FetchStreams= */ false,
/*isUsingEmulator=*/ false
/*isUsingEmulator=*/ false,
undefined
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('Fetch Connection', () => {
DatabaseId.empty(),
'',
'',
'',
new FirestoreSettingsImpl({
host: 'abc.cloudworkstations.dev'
})
Expand Down
81 changes: 81 additions & 0 deletions packages/firestore/test/unit/remote/grpc_connection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
* Copyright 2020 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 { Metadata } from '@grpc/grpc-js';
import { expect } from 'chai';

import { DatabaseId, DatabaseInfo } from '../../../src/core/database_info';
import { ResourcePath } from '../../../src/model/path';
import { GrpcConnection } from '../../../src/platform/node/grpc_connection';

export class TestGrpcConnection extends GrpcConnection {
mockStub = {
lastMetadata: null,
mockRpc(
req: unknown,
metadata: Metadata,
callback: (err: unknown, resp: unknown) => void
) {
this.lastMetadata = metadata;
callback(null, null);
}
} as {
lastMetadata: null | Metadata;
[index: string]: unknown;
};

protected ensureActiveStub(): unknown {
return this.mockStub;
}
}

describe('GrpcConnection', () => {
const testDatabaseInfo = new DatabaseInfo(
new DatabaseId('testproject'),
'test-app-id',
'persistenceKey',
'example.com',
/*ssl=*/ false,
/*forceLongPolling=*/ false,
/*autoDetectLongPolling=*/ false,
/*longPollingOptions=*/ {},
/*useFetchStreams=*/ false,
/*isUsingEmulator=*/ false,
'grpc-connection-test-api-key'
);
const connection = new TestGrpcConnection(
{ google: { firestore: { v1: {} } } },
testDatabaseInfo
);

it('Passes the API Key from DatabaseInfo to the grpc stub', async () => {
const request = {
database: 'projects/testproject/databases/(default)',
writes: []
};
await connection.invokeRPC(
'mockRpc',
ResourcePath.emptyPath(),
request,
null,
null
);
expect(
connection.mockStub.lastMetadata?.get('x-goog-api-key')
).to.deep.equal(['grpc-connection-test-api-key']);
});
});
5 changes: 3 additions & 2 deletions packages/firestore/test/unit/remote/rest_connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ describe('RestConnection', () => {
/*autoDetectLongPolling=*/ false,
/*longPollingOptions=*/ {},
/*useFetchStreams=*/ false,
/*isUsingEmulator=*/ false
/*isUsingEmulator=*/ false,
'rest-connection-test-api-key'
);
const connection = new TestRestConnection(testDatabaseInfo);

Expand All @@ -83,7 +84,7 @@ describe('RestConnection', () => {
null
);
expect(connection.lastUrl).to.equal(
'http://example.com/v1/projects/testproject/databases/(default)/documents:commit'
'http://example.com/v1/projects/testproject/databases/(default)/documents:commit?key=rest-connection-test-api-key'
);
});

Expand Down
3 changes: 2 additions & 1 deletion packages/firestore/test/unit/specs/spec_test_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ abstract class TestRunner {
/*autoDetectLongPolling=*/ false,
/*longPollingOptions=*/ {},
/*useFetchStreams=*/ false,
/*isUsingEmulator=*/ false
/*isUsingEmulator=*/ false,
'test-api-key'
);

// TODO(mrschmidt): During client startup in `firestore_client`, we block
Expand Down
Loading