Skip to content

Commit a382f19

Browse files
Merge master into release
2 parents 2d8073f + 1fbc4c4 commit a382f19

Some content is hidden

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

48 files changed

+1146
-214
lines changed

.changeset/cool-grapes-attend.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/firestore': patch
3+
'firebase': patch
4+
---
5+
6+
Fix Firestore failing to raise initial snapshot from empty local cache result

.changeset/cool-seas-confess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/database": patch
3+
---
4+
5+
Fixed `endBefore` and `push` documentation typos in RTDB

.changeset/large-lamps-reflect.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@firebase/storage": patch
3+
---
4+
5+
Fixed bug where upload status wasn't being checked after an upload failure.
6+
Implemented exponential backoff and max retry strategy.

.changeset/metal-goats-unite.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@firebase/firestore': minor
3+
'@firebase/firestore-compat': minor
4+
'@firebase/webchannel-wrapper': minor
5+
'firebase': minor
6+
---
7+
8+
Set withCredentials=true when making requests via non-streaming RPCs, like is done for streaming RPCs.

.changeset/slow-forks-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/analytics': patch
3+
---
4+
5+
Update to allow for multiple instance of gtag with different datalayer names

.changeset/strong-squids-dress.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/util': patch
3+
---
4+
5+
Remove `__FIREBASE_DEFAULTS_PATH__` option for now, as the current implementation causes Webpack warnings. Also fix `process.env` check to work in environments where `process` exists but `process.env` does not.

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,26 @@ database. When prompted to select the set of initial security rules, select
113113
any option (e.g. "Start in Production Mode") since these permission settings
114114
will be overwritten below.
115115

116+
#### Storage Setup
117+
118+
Visit the "Storage" section of the console and create a storage bucket. In
119+
order to run the tests, you will need to update your bucket's CORS rules.
120+
121+
1. Create a new file called `cors.json` with the contents:
122+
```json
123+
[
124+
{
125+
"origin": ["http://localhost:8089"],
126+
"method": ["GET"],
127+
"maxAgeSeconds": 3600
128+
}
129+
]
130+
```
131+
2. Install `gsutil` from https://cloud.google.com/storage/docs/gsutil_install
132+
3. Run `gsutil cors set cors.json gs://<your-cloud-storage-bucket>`
133+
134+
For more information, visit https://firebase.google.com/docs/storage/web/download-files#cors_configuration
135+
116136
#### Authentication Support
117137

118138
Visit the authentication config in your project and enable the `Anonymous`

common/api-review/storage.api.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export class _FirebaseStorageImpl implements FirebaseStorage {
8383
// Warning: (ae-forgotten-export) The symbol "Request" needs to be exported by the entry point index.d.ts
8484
//
8585
// (undocumented)
86-
_makeRequest<I extends ConnectionType, O>(requestInfo: RequestInfo_2<I, O>, requestFactory: () => Connection<I>, authToken: string | null, appCheckToken: string | null): Request_2<O>;
86+
_makeRequest<I extends ConnectionType, O>(requestInfo: RequestInfo_2<I, O>, requestFactory: () => Connection<I>, authToken: string | null, appCheckToken: string | null, retry?: boolean): Request_2<O>;
8787
// (undocumented)
8888
makeRequestWithTokens<I extends ConnectionType, O>(requestInfo: RequestInfo_2<I, O>, requestFactory: () => Connection<I>): Promise<O>;
8989
_makeStorageReference(loc: _Location): _Reference;
@@ -319,11 +319,13 @@ export class _UploadTask {
319319
_blob: _FbsBlob;
320320
cancel(): boolean;
321321
catch<T>(onRejected: (p1: StorageError_2) => T | Promise<T>): Promise<T>;
322+
// (undocumented)
323+
isExponentialBackoffExpired(): boolean;
322324
// Warning: (ae-forgotten-export) The symbol "Metadata" needs to be exported by the entry point index.d.ts
323325
_metadata: Metadata | null;
324326
// Warning: (ae-forgotten-export) The symbol "Unsubscribe" needs to be exported by the entry point index.d.ts
325327
// Warning: (ae-forgotten-export) The symbol "Subscribe" needs to be exported by the entry point index.d.ts
326-
on(type: _TaskEvent, nextOrObserver?: StorageObserver<UploadTaskSnapshot> | null | ((snapshot: UploadTaskSnapshot) => unknown), error?: ((a: StorageError_2) => unknown) | null, completed?: Unsubscribe_2 | null): Unsubscribe_2 | Subscribe_2<UploadTaskSnapshot>;
328+
on(type: _TaskEvent, nextOrObserver?: StorageObserver<UploadTaskSnapshot> | null | ((snapshot: UploadTaskSnapshot) => unknown), error?: ((a: StorageError_2) => unknown) | null, completed?: CompleteFn | null): Unsubscribe_2 | Subscribe_2<UploadTaskSnapshot>;
327329
pause(): boolean;
328330
resume(): boolean;
329331
get snapshot(): UploadTaskSnapshot;

packages/analytics/src/helpers.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
import { GtagCommand } from './constants';
3030
import { Deferred } from '@firebase/util';
3131
import { ConsentSettings } from './public-types';
32+
import { removeGtagScripts } from '../testing/gtag-script-util';
3233

3334
const fakeMeasurementId = 'abcd-efgh-ijkl';
3435
const fakeAppId = 'my-test-app-1234';
@@ -46,6 +47,10 @@ const fakeDynamicConfig: DynamicConfig = {
4647
const fakeDynamicConfigPromises = [Promise.resolve(fakeDynamicConfig)];
4748

4849
describe('Gtag wrapping functions', () => {
50+
afterEach(() => {
51+
removeGtagScripts();
52+
});
53+
4954
it('getOrCreateDataLayer is able to create a new data layer if none exists', () => {
5055
delete window['dataLayer'];
5156
expect(getOrCreateDataLayer('dataLayer')).to.deep.equal([]);
@@ -57,14 +62,24 @@ describe('Gtag wrapping functions', () => {
5762
});
5863

5964
it('insertScriptIfNeeded inserts script tag', () => {
60-
expect(findGtagScriptOnPage()).to.be.null;
61-
insertScriptTag('customDataLayerName', fakeMeasurementId);
62-
const scriptTag = findGtagScriptOnPage();
65+
const customDataLayerName = 'customDataLayerName';
66+
expect(findGtagScriptOnPage(customDataLayerName)).to.be.null;
67+
insertScriptTag(customDataLayerName, fakeMeasurementId);
68+
const scriptTag = findGtagScriptOnPage(customDataLayerName);
6369
expect(scriptTag).to.not.be.null;
6470
expect(scriptTag!.src).to.contain(`l=customDataLayerName`);
6571
expect(scriptTag!.src).to.contain(`id=${fakeMeasurementId}`);
6672
});
6773

74+
// The test above essentially already touches this functionality but it is still valuable
75+
it('findGtagScriptOnPage returns gtag instance with matching data layer name', () => {
76+
const defaultDataLayerName = 'dataLayer';
77+
insertScriptTag(defaultDataLayerName, fakeMeasurementId);
78+
const scriptTag = findGtagScriptOnPage(defaultDataLayerName);
79+
expect(scriptTag!.src).to.contain(`l=${defaultDataLayerName}`);
80+
expect(findGtagScriptOnPage('NON_EXISTENT_DATA_LAYER_ID')).to.be.null;
81+
});
82+
6883
describe('wrapOrCreateGtag() when user has not previously inserted a gtag script tag on this page', () => {
6984
afterEach(() => {
7085
delete window['gtag'];

packages/analytics/src/helpers.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,12 +318,19 @@ export function wrapOrCreateGtag(
318318
}
319319

320320
/**
321-
* Returns first script tag in DOM matching our gtag url pattern.
321+
* Returns the script tag in the DOM matching both the gtag url pattern
322+
* and the provided data layer name.
322323
*/
323-
export function findGtagScriptOnPage(): HTMLScriptElement | null {
324+
export function findGtagScriptOnPage(
325+
dataLayerName: string
326+
): HTMLScriptElement | null {
324327
const scriptTags = window.document.getElementsByTagName('script');
325328
for (const tag of Object.values(scriptTags)) {
326-
if (tag.src && tag.src.includes(GTAG_URL)) {
329+
if (
330+
tag.src &&
331+
tag.src.includes(GTAG_URL) &&
332+
tag.src.includes(dataLayerName)
333+
) {
327334
return tag;
328335
}
329336
}

packages/analytics/src/index.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { FirebaseApp } from '@firebase/app';
2727
import { GtagCommand } from './constants';
2828
import { findGtagScriptOnPage } from './helpers';
29-
import { removeGtagScript } from '../testing/gtag-script-util';
29+
import { removeGtagScripts } from '../testing/gtag-script-util';
3030
import { Deferred } from '@firebase/util';
3131
import { AnalyticsError } from './errors';
3232
import { logEvent } from './api';
@@ -150,7 +150,7 @@ describe('FirebaseAnalytics instance tests', () => {
150150
after(() => {
151151
delete window['gtag'];
152152
delete window['dataLayer'];
153-
removeGtagScript();
153+
removeGtagScripts();
154154
fetchStub.restore();
155155
clock.restore();
156156
idbOpenStub.restore();
@@ -208,7 +208,7 @@ describe('FirebaseAnalytics instance tests', () => {
208208
afterEach(() => {
209209
delete window['gtag'];
210210
delete window['dataLayer'];
211-
removeGtagScript();
211+
removeGtagScripts();
212212
fetchStub.restore();
213213
clock.restore();
214214
warnStub.restore();
@@ -304,7 +304,7 @@ describe('FirebaseAnalytics instance tests', () => {
304304
after(() => {
305305
delete window[customGtagName];
306306
delete window[customDataLayerName];
307-
removeGtagScript();
307+
removeGtagScripts();
308308
fetchStub.restore();
309309
clock.restore();
310310
idbOpenStub.restore();
@@ -349,13 +349,13 @@ describe('FirebaseAnalytics instance tests', () => {
349349
// Successfully resolves fake IDB open request.
350350
fakeRequest.onsuccess();
351351
await initializationPromisesMap[fakeAppParams.appId];
352-
expect(findGtagScriptOnPage()).to.not.be.null;
352+
expect(findGtagScriptOnPage('dataLayer')).to.not.be.null;
353353
expect(typeof window['gtag']).to.equal('function');
354354
expect(Array.isArray(window['dataLayer'])).to.be.true;
355355

356356
delete window['gtag'];
357357
delete window['dataLayer'];
358-
removeGtagScript();
358+
removeGtagScripts();
359359
fetchStub.restore();
360360
idbOpenStub.restore();
361361
});

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { DynamicConfig } from './types';
2828
import { FirebaseApp } from '@firebase/app';
2929
import { Deferred } from '@firebase/util';
3030
import { _FirebaseInstallationsInternal } from '@firebase/installations';
31-
import { removeGtagScript } from '../testing/gtag-script-util';
31+
import { removeGtagScripts } from '../testing/gtag-script-util';
3232
import { setDefaultEventParameters } from './api';
3333
import {
3434
defaultConsentSettingsForInit,
@@ -68,7 +68,7 @@ describe('initializeAnalytics()', () => {
6868
});
6969
afterEach(() => {
7070
fetchStub.restore();
71-
removeGtagScript();
71+
removeGtagScripts();
7272
});
7373
it('gets FID and measurement ID and calls gtag config with them', async () => {
7474
stubFetch();

packages/analytics/src/initialize-analytics.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@ export async function _initializeAnalytics(
119119
fidPromise
120120
]);
121121

122-
// Detect if user has already put the gtag <script> tag on this page.
123-
if (!findGtagScriptOnPage()) {
122+
// Detect if user has already put the gtag <script> tag on this page with the passed in
123+
// data layer name.
124+
if (!findGtagScriptOnPage(dataLayerName)) {
124125
insertScriptTag(dataLayerName, dynamicConfig.measurementId);
125126
}
126127

packages/analytics/testing/gtag-script-util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
import { GTAG_URL } from '../src/constants';
1818

19-
export function removeGtagScript(): void {
19+
export function removeGtagScripts(): void {
2020
const scriptTags = window.document.getElementsByTagName('script');
2121
for (const tag of Object.values(scriptTags)) {
2222
if (tag.src) {

packages/auth/src/platform_browser/strategies/redirect.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,7 @@ export async function _linkWithRedirect(
218218
*
219219
* @remarks
220220
* If sign-in succeeded, returns the signed in user. If sign-in was unsuccessful, fails with an
221-
* error. If no redirect operation was called, returns a {@link UserCredential}
222-
* with a null `user`.
221+
* error. If no redirect operation was called, returns `null`.
223222
*
224223
* @example
225224
* ```javascript

packages/database/src/api/Reference_impl.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,8 @@ export interface ThenableReferenceImpl
578578
* resulting list of items is chronologically sorted. The keys are also
579579
* designed to be unguessable (they contain 72 random bits of entropy).
580580
*
581-
* See {@link https://firebase.google.com/docs/database/web/lists-of-data#append_to_a_list_of_data | Append to a list of data}
582-
* </br>See {@link ttps://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html | The 2^120 Ways to Ensure Unique Identifiers}
581+
* See {@link https://firebase.google.com/docs/database/web/lists-of-data#append_to_a_list_of_data | Append to a list of data}.
582+
* See {@link https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html | The 2^120 Ways to Ensure Unique Identifiers}.
583583
*
584584
* @param parent - The parent location.
585585
* @param value - Optional value to be written at the generated location.
@@ -1782,8 +1782,8 @@ class QueryEndBeforeConstraint extends QueryConstraint {
17821782
*
17831783
* The ending point is exclusive. If only a value is provided, children
17841784
* with a value less than the specified value will be included in the query.
1785-
* If a key is specified, then children must have a value lesss than or equal
1786-
* to the specified value and a a key name less than the specified key.
1785+
* If a key is specified, then children must have a value less than or equal
1786+
* to the specified value and a key name less than the specified key.
17871787
*
17881788
* @param value - The value to end before. The argument type depends on which
17891789
* `orderBy*()` function was used in this query. Specify a value that matches

packages/firebase/compat/index.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,8 +2613,7 @@ declare namespace firebase.auth {
26132613
* Returns a UserCredential from the redirect-based sign-in flow.
26142614
*
26152615
* If sign-in succeeded, returns the signed in user. If sign-in was
2616-
* unsuccessful, fails with an error. If no redirect operation was called,
2617-
* returns a UserCredential with a null User.
2616+
* unsuccessful, fails with an error. If no redirect operation was called, returns `null`.
26182617
*
26192618
* <h4>Error Codes</h4>
26202619
* <dl>

packages/firestore-compat/src/index.console.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ import {
2222
Firestore as FirestoreExp,
2323
FirestoreError,
2424
_EmptyAuthCredentialsProvider,
25-
_EmptyAppCheckTokenProvider
25+
_EmptyAppCheckTokenProvider,
26+
Query as ExpQuery,
27+
getCountFromServer
2628
} from '@firebase/firestore';
29+
import { Compat } from '@firebase/util';
2730

2831
import {
2932
Firestore as FirestoreCompat,
@@ -98,6 +101,15 @@ export class Firestore extends FirestoreCompat {
98101
new MemoryPersistenceProvider()
99102
);
100103
}
104+
105+
INTERNAL = {
106+
delete: () => this.terminate(),
107+
count: (query: Compat<ExpQuery<unknown>>) => {
108+
return getCountFromServer(query._delegate).then(response => {
109+
return response.data().count;
110+
});
111+
}
112+
};
101113
}
102114

103115
function databaseIdFromFirestoreDatabase(

packages/firestore/src/core/event_manager.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ export class QueryListener {
307307
snap.mutatedKeys,
308308
snap.fromCache,
309309
snap.syncStateChanged,
310-
/* excludesMetadataChanges= */ true
310+
/* excludesMetadataChanges= */ true,
311+
snap.hasCachedResults
311312
);
312313
}
313314
let raisedEvent = false;
@@ -371,8 +372,13 @@ export class QueryListener {
371372
return false;
372373
}
373374

374-
// Raise data from cache if we have any documents or we are offline
375-
return !snap.docs.isEmpty() || onlineState === OnlineState.Offline;
375+
// Raise data from cache if we have any documents, have cached results before,
376+
// or we are offline.
377+
return (
378+
!snap.docs.isEmpty() ||
379+
snap.hasCachedResults ||
380+
onlineState === OnlineState.Offline
381+
);
376382
}
377383

378384
private shouldRaiseEvent(snap: ViewSnapshot): boolean {
@@ -405,7 +411,8 @@ export class QueryListener {
405411
snap.query,
406412
snap.docs,
407413
snap.mutatedKeys,
408-
snap.fromCache
414+
snap.fromCache,
415+
snap.hasCachedResults
409416
);
410417
this.raisedInitialEvent = true;
411418
this.queryObserver.next(snap);

0 commit comments

Comments
 (0)