Skip to content

Commit c12a71b

Browse files
Merge branch 'master' into mrschmidt/gcfix
2 parents edeba77 + 2c90aec commit c12a71b

File tree

129 files changed

+990
-431
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+990
-431
lines changed

.changeset/blue-suits-wait.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.changeset/fast-buses-scream.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/component': patch
3+
---
4+
5+
Store instance initialization options on the Provider.

.changeset/gold-ghosts-argue.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.changeset/wise-toys-care.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@firebase/database-types': minor
3+
'@firebase/database': minor
4+
'firebase': minor
5+
'@firebase/firestore-types': minor
6+
'@firebase/firestore': minor
7+
'@firebase/storage-types': minor
8+
'@firebase/storage': minor
9+
'@firebase/util': minor
10+
---
11+
12+
Implement mockUserToken for Storage and fix JWT format bugs.

.changeset/witty-brooms-relate.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

common/api-review/app-check-exp.api.md

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export class CustomProvider implements AppCheckProvider {
5151
getToken(): Promise<AppCheckTokenInternal>;
5252
// @internal (undocumented)
5353
initialize(app: FirebaseApp): void;
54+
// @internal (undocumented)
55+
isEqual(otherProvider: unknown): boolean;
5456
}
5557

5658
// @public
@@ -79,6 +81,8 @@ export class ReCaptchaV3Provider implements AppCheckProvider {
7981
getToken(): Promise<AppCheckTokenInternal>;
8082
// @internal (undocumented)
8183
initialize(app: FirebaseApp): void;
84+
// @internal (undocumented)
85+
isEqual(otherProvider: unknown): boolean;
8286
}
8387

8488
// @public

common/api-review/auth-exp.api.md

+19-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ export function applyActionCode(auth: Auth, oobCode: string): Promise<void>;
8282
export interface Auth {
8383
readonly config: Config;
8484
readonly currentUser: User | null;
85-
// Warning: (ae-forgotten-export) The symbol "EmulatorConfig" needs to be exported by the entry point index.d.ts
8685
readonly emulatorConfig: EmulatorConfig | null;
8786
languageCode: string | null;
8887
readonly name: string;
@@ -328,6 +327,16 @@ export class EmailAuthProvider implements AuthProvider {
328327
readonly providerId: "password";
329328
}
330329

330+
// @public
331+
export interface EmulatorConfig {
332+
readonly host: string;
333+
readonly options: {
334+
readonly disableWarnings: boolean;
335+
};
336+
readonly port: number | null;
337+
readonly protocol: string;
338+
}
339+
331340
export { ErrorFn }
332341

333342
// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts
@@ -463,6 +472,8 @@ export interface MultiFactorUser {
463472
unenroll(option: MultiFactorInfo | string): Promise<void>;
464473
}
465474

475+
export { NextFn }
476+
466477
// @public
467478
export type NextOrObserver<T> = NextFn<T | null> | Observer<T | null>;
468479

@@ -634,12 +645,17 @@ export function reauthenticateWithPopup(user: User, provider: AuthProvider, reso
634645
// @public
635646
export function reauthenticateWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise<never>;
636647

648+
// @public
649+
export interface RecaptchaParameters {
650+
// (undocumented)
651+
[key: string]: any;
652+
}
653+
637654
// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.d.ts
638655
//
639656
// @public
640657
export class RecaptchaVerifier implements ApplicationVerifierInternal {
641-
// Warning: (ae-forgotten-export) The symbol "Parameters" needs to be exported by the entry point index.d.ts
642-
constructor(containerOrId: HTMLElement | string, parameters: Parameters_2, authExtern: Auth);
658+
constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth);
643659
clear(): void;
644660
// Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.d.ts
645661
//

common/api-review/database.api.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function child(parent: DatabaseReference, path: string): DatabaseReferenc
1212

1313
// @public
1414
export function connectDatabaseEmulator(db: Database, host: string, port: number, options?: {
15-
mockUserToken?: EmulatorMockTokenOptions;
15+
mockUserToken?: EmulatorMockTokenOptions | string;
1616
}): void;
1717

1818
// @public

common/api-review/firestore-lite.api.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class CollectionReference<T = DocumentData> extends Query<T> {
5151

5252
// @public
5353
export function connectFirestoreEmulator(firestore: Firestore, host: string, port: number, options?: {
54-
mockUserToken?: EmulatorMockTokenOptions;
54+
mockUserToken?: EmulatorMockTokenOptions | string;
5555
}): void;
5656

5757
// @public

common/api-review/firestore.api.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class CollectionReference<T = DocumentData> extends Query<T> {
5757

5858
// @public
5959
export function connectFirestoreEmulator(firestore: Firestore, host: string, port: number, options?: {
60-
mockUserToken?: EmulatorMockTokenOptions;
60+
mockUserToken?: EmulatorMockTokenOptions | string;
6161
}): void;
6262

6363
// @public

common/api-review/storage.api.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
88
import { CompleteFn } from '@firebase/util';
9+
import { EmulatorMockTokenOptions } from '@firebase/util';
910
import { FirebaseApp } from '@firebase/app';
1011
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
1112
import { FirebaseError } from '@firebase/util';
@@ -16,7 +17,9 @@ import { Subscribe } from '@firebase/util';
1617
import { Unsubscribe } from '@firebase/util';
1718

1819
// @public
19-
export function connectStorageEmulator(storage: FirebaseStorage, host: string, port: number): void;
20+
export function connectStorageEmulator(storage: FirebaseStorage, host: string, port: number, options?: {
21+
mockUserToken?: EmulatorMockTokenOptions | string;
22+
}): void;
2023

2124
// @public
2225
export function deleteObject(ref: StorageReference): Promise<void>;
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { expect } from 'chai';
19+
import { SinonStub, stub } from 'sinon';
20+
import '../testing/setup';
21+
import { getFullApp } from '../testing/get-fake-firebase-services';
22+
import { getAnalytics, initializeAnalytics } from './api';
23+
import { FirebaseApp, deleteApp } from '@firebase/app-exp';
24+
import { AnalyticsError } from './errors';
25+
import * as init from './initialize-analytics';
26+
const fakeAppParams = { appId: 'abcdefgh12345:23405', apiKey: 'AAbbCCdd12345' };
27+
28+
describe('FirebaseAnalytics API tests', () => {
29+
let initStub: SinonStub = stub();
30+
let app: FirebaseApp;
31+
32+
beforeEach(() => {
33+
initStub = stub(init, '_initializeAnalytics').resolves(
34+
'FAKE_MEASUREMENT_ID'
35+
);
36+
});
37+
38+
afterEach(async () => {
39+
await initStub();
40+
initStub.restore();
41+
if (app) {
42+
return deleteApp(app);
43+
}
44+
});
45+
46+
after(() => {
47+
delete window['gtag'];
48+
delete window['dataLayer'];
49+
});
50+
51+
it('initializeAnalytics() with same (no) options returns same instance', () => {
52+
app = getFullApp(fakeAppParams);
53+
const analyticsInstance = initializeAnalytics(app);
54+
const newInstance = initializeAnalytics(app);
55+
expect(analyticsInstance).to.equal(newInstance);
56+
});
57+
it('initializeAnalytics() with same options returns same instance', () => {
58+
app = getFullApp(fakeAppParams);
59+
const analyticsInstance = initializeAnalytics(app, {
60+
config: { 'send_page_view': false }
61+
});
62+
const newInstance = initializeAnalytics(app, {
63+
config: { 'send_page_view': false }
64+
});
65+
expect(analyticsInstance).to.equal(newInstance);
66+
});
67+
it('initializeAnalytics() with different options throws', () => {
68+
app = getFullApp(fakeAppParams);
69+
initializeAnalytics(app, {
70+
config: { 'send_page_view': false }
71+
});
72+
expect(() =>
73+
initializeAnalytics(app, {
74+
config: { 'send_page_view': true }
75+
})
76+
).to.throw(AnalyticsError.ALREADY_INITIALIZED);
77+
});
78+
it('initializeAnalytics() with different options (one undefined) throws', () => {
79+
app = getFullApp(fakeAppParams);
80+
initializeAnalytics(app);
81+
expect(() =>
82+
initializeAnalytics(app, {
83+
config: { 'send_page_view': true }
84+
})
85+
).to.throw(AnalyticsError.ALREADY_INITIALIZED);
86+
});
87+
it('getAnalytics() returns same instance created by previous getAnalytics()', () => {
88+
app = getFullApp(fakeAppParams);
89+
const analyticsInstance = getAnalytics(app);
90+
expect(getAnalytics(app)).to.equal(analyticsInstance);
91+
});
92+
it('getAnalytics() returns same instance created by initializeAnalytics()', () => {
93+
app = getFullApp(fakeAppParams);
94+
const analyticsInstance = initializeAnalytics(app);
95+
expect(getAnalytics(app)).to.equal(analyticsInstance);
96+
});
97+
});

packages-exp/analytics-exp/src/api.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import {
3232
validateIndexedDBOpenable,
3333
areCookiesEnabled,
3434
isBrowserExtension,
35-
getModularInstance
35+
getModularInstance,
36+
deepEqual
3637
} from '@firebase/util';
3738
import { ANALYTICS_TYPE } from './constants';
3839
import {
@@ -97,7 +98,12 @@ export function initializeAnalytics(
9798
ANALYTICS_TYPE
9899
);
99100
if (analyticsProvider.isInitialized()) {
100-
throw ERROR_FACTORY.create(AnalyticsError.ALREADY_INITIALIZED);
101+
const existingInstance = analyticsProvider.getImmediate();
102+
if (deepEqual(options, analyticsProvider.getOptions())) {
103+
return existingInstance;
104+
} else {
105+
throw ERROR_FACTORY.create(AnalyticsError.ALREADY_INITIALIZED);
106+
}
101107
}
102108
const analyticsInstance = analyticsProvider.initialize({ options });
103109
return analyticsInstance;

packages-exp/analytics-exp/src/errors.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ const ERRORS: ErrorMap<AnalyticsError> = {
3636
' already exists. ' +
3737
'Only one Firebase Analytics instance can be created for each appId.',
3838
[AnalyticsError.ALREADY_INITIALIZED]:
39-
'Firebase Analytics has already been initialized. ' +
40-
'initializeAnalytics() must only be called once. getAnalytics() can be used ' +
39+
'initializeAnalytics() cannot be called again with different options than those ' +
40+
'it was initially called with. It can be called again with the same options to ' +
41+
'return the existing instance, or getAnalytics() can be used ' +
4142
'to get a reference to the already-intialized instance.',
4243
[AnalyticsError.ALREADY_INITIALIZED_SETTINGS]:
4344
'Firebase Analytics has already been initialized.' +

packages-exp/analytics-exp/src/factory.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { getOrCreateDataLayer, wrapOrCreateGtag } from './helpers';
2121
import { AnalyticsError, ERROR_FACTORY } from './errors';
2222
import { _FirebaseInstallationsInternal } from '@firebase/installations-exp';
2323
import { areCookiesEnabled, isBrowserExtension } from '@firebase/util';
24-
import { initializeAnalytics } from './initialize-analytics';
24+
import { _initializeAnalytics } from './initialize-analytics';
2525
import { logger } from './logger';
2626
import { FirebaseApp, _FirebaseService } from '@firebase/app-exp';
2727

@@ -221,7 +221,7 @@ export function factory(
221221
}
222222
// Async but non-blocking.
223223
// This map reflects the completion state of all promises for each appId.
224-
initializationPromisesMap[appId] = initializeAnalytics(
224+
initializationPromisesMap[appId] = _initializeAnalytics(
225225
app,
226226
dynamicConfigPromisesList,
227227
measurementIdToAppId,

packages-exp/analytics-exp/src/index.test.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ let clock: sinon.SinonFakeTimers;
4848
let fakeInstallations: _FirebaseInstallationsInternal;
4949

5050
// Fake indexedDB.open() request
51-
let fakeRequest = {
51+
const fakeRequest = {
5252
onsuccess: () => {},
5353
result: {
5454
close: () => {}
@@ -67,13 +67,7 @@ function stubFetch(status: number, body: object): void {
6767
// Stub indexedDB.open() because sinon's clock does not know
6868
// how to wait for the real indexedDB callbacks to resolve.
6969
function stubIdbOpen(): void {
70-
(fakeRequest = {
71-
onsuccess: () => {},
72-
result: {
73-
close: () => {}
74-
}
75-
}),
76-
(idbOpenStub = stub(indexedDB, 'open').returns(fakeRequest as any));
70+
idbOpenStub = stub(indexedDB, 'open').returns(fakeRequest as any);
7771
}
7872

7973
describe('FirebaseAnalytics instance tests', () => {

packages-exp/analytics-exp/src/initialize-analytics.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import { expect } from 'chai';
1919
import { SinonStub, stub } from 'sinon';
2020
import '../testing/setup';
21-
import { initializeAnalytics } from './initialize-analytics';
21+
import { _initializeAnalytics } from './initialize-analytics';
2222
import {
2323
getFakeApp,
2424
getFakeInstallations
@@ -65,7 +65,7 @@ describe('initializeAnalytics()', () => {
6565
});
6666
it('gets FID and measurement ID and calls gtag config with them', async () => {
6767
stubFetch();
68-
await initializeAnalytics(
68+
await _initializeAnalytics(
6969
app,
7070
dynamicPromisesList,
7171
measurementIdToAppId,
@@ -81,7 +81,7 @@ describe('initializeAnalytics()', () => {
8181
});
8282
it('calls gtag config with options if provided', async () => {
8383
stubFetch();
84-
await initializeAnalytics(
84+
await _initializeAnalytics(
8585
app,
8686
dynamicPromisesList,
8787
measurementIdToAppId,
@@ -99,7 +99,7 @@ describe('initializeAnalytics()', () => {
9999
});
100100
it('puts dynamic fetch promise into dynamic promises list', async () => {
101101
stubFetch();
102-
await initializeAnalytics(
102+
await _initializeAnalytics(
103103
app,
104104
dynamicPromisesList,
105105
measurementIdToAppId,
@@ -113,7 +113,7 @@ describe('initializeAnalytics()', () => {
113113
});
114114
it('puts dynamically fetched measurementId into lookup table', async () => {
115115
stubFetch();
116-
await initializeAnalytics(
116+
await _initializeAnalytics(
117117
app,
118118
dynamicPromisesList,
119119
measurementIdToAppId,
@@ -126,7 +126,7 @@ describe('initializeAnalytics()', () => {
126126
it('warns on local/fetched measurement ID mismatch', async () => {
127127
stubFetch();
128128
const consoleStub = stub(console, 'warn');
129-
await initializeAnalytics(
129+
await _initializeAnalytics(
130130
getFakeApp({ ...fakeAppParams, measurementId: 'old-measurement-id' }),
131131
dynamicPromisesList,
132132
measurementIdToAppId,

packages-exp/analytics-exp/src/initialize-analytics.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async function validateIndexedDB(): Promise<boolean> {
6565
*
6666
* @returns Measurement ID.
6767
*/
68-
export async function initializeAnalytics(
68+
export async function _initializeAnalytics(
6969
app: FirebaseApp,
7070
dynamicConfigPromisesList: Array<
7171
Promise<DynamicConfig | MinimalDynamicConfig>

0 commit comments

Comments
 (0)