Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 2393 - Add environment check to Analytics Module #3165

Merged
merged 47 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0843a6e
updateDoc()/deleteDoc() signature fix (#3147)
schmidt-sebastian Jun 2, 2020
0c426c3
Transaction/WriteBatch signature fix (#3151)
schmidt-sebastian Jun 2, 2020
fd0c0a3
Take WriteStream offline when IndexedDB is unavailable (#2995)
schmidt-sebastian Jun 2, 2020
9512b48
Do not build firestore lite in build because it breaks regular releas…
Feiyang1 Jun 3, 2020
b5c7b78
add pre script for build:release (#3161)
Feiyang1 Jun 3, 2020
e10388c
Add setLogLevel() (#3154)
schmidt-sebastian Jun 3, 2020
3ac0fe3
Add DocumentReference (#3123)
schmidt-sebastian Jun 3, 2020
a377c68
issue #2393 fix for analytics module
XuechunHou Jun 4, 2020
5932e87
removed unnecessary sw check within isSupported method for analytics
XuechunHou Jun 4, 2020
541bb0c
resolved merge conflicts
XuechunHou Jun 5, 2020
0090a18
using raw indexDB api to open a dummy database
XuechunHou Jun 8, 2020
8e979a3
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 8, 2020
7d419f1
added console log for reading code
XuechunHou Jun 9, 2020
08e4a6f
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 17, 2020
571e514
fix for issue-2393
XuechunHou Jun 17, 2020
b22c1fc
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 17, 2020
dc75ec1
removed unused import
XuechunHou Jun 17, 2020
64d8be7
fixed so that correct type of variable of errorInfo required errorFa…
XuechunHou Jun 17, 2020
099816a
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 23, 2020
c8cc946
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 24, 2020
d16cbbd
fixed isSupported export
XuechunHou Jun 24, 2020
ad09146
addressed feedback
XuechunHou Jun 24, 2020
948a5da
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 24, 2020
a1103e1
removed unnecessary console log
XuechunHou Jun 24, 2020
f341f94
removed console logs
XuechunHou Jun 24, 2020
111d792
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jun 30, 2020
c9f137b
addressed feedback
XuechunHou Jun 30, 2020
1fa549c
revert unrelated files
XuechunHou Jun 30, 2020
9d12700
Create clean-numbers-flow.md
XuechunHou Jun 30, 2020
2b00fc1
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 2, 2020
c5e2596
Merge branch 'issue-2393-analytics' of https://github.com/firebase/fi…
XuechunHou Jul 2, 2020
e30d0ec
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 6, 2020
d7b3359
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 7, 2020
12852f9
bring functions to util
XuechunHou Jul 7, 2020
e9eb751
convert validateIndexedDBOpenable to async
XuechunHou Jul 7, 2020
e00edfa
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 8, 2020
68387e6
trying to fix async error throwing
XuechunHou Jul 8, 2020
17973b6
brought indexedDB check to factory method
XuechunHou Jul 9, 2020
44e5aeb
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 9, 2020
1321327
fixed grammar error
XuechunHou Jul 10, 2020
6debf5d
break down functions
XuechunHou Jul 13, 2020
21a4f7c
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 13, 2020
db750f8
take indexedDB check funcitons out to factory method
XuechunHou Jul 13, 2020
4dec8d9
changed error names
XuechunHou Jul 13, 2020
9d46212
removed eslint comment
XuechunHou Jul 14, 2020
51303b0
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk …
XuechunHou Jul 14, 2020
64df7e5
revert license change
XuechunHou Jul 14, 2020
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
9 changes: 9 additions & 0 deletions .changeset/clean-numbers-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@firebase/analytics": minor
"firebase": minor
---

Issue 2393 fix - analytics module

- Added a public method `isSupported` to Analytics module which returns true if current browser context supports initialization of analytics module.
- Added runtime checks to Analytics module that validate if cookie is enabled in current browser and if current browser environment supports indexedDB functionalities.
34 changes: 31 additions & 3 deletions packages/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import {
ComponentContainer
} from '@firebase/component';
import { ERROR_FACTORY, AnalyticsError } from './src/errors';

import {
isIndexedDBAvailable,
validateIndexedDBOpenable,
areCookiesEnabled
} from '@firebase/util';
import { name, version } from './package.json';

declare global {
Expand All @@ -45,6 +49,7 @@ declare global {
* Type constant for Firebase Analytics.
*/
const ANALYTICS_TYPE = 'analytics';

export function registerAnalytics(instance: _FirebaseNamespace): void {
instance.INTERNAL.registerComponent(
new Component(
Expand All @@ -55,13 +60,13 @@ export function registerAnalytics(instance: _FirebaseNamespace): void {
const installations = container
.getProvider('installations')
.getImmediate();

return factory(app, installations);
},
ComponentType.PUBLIC
).setServiceProps({
settings,
EventName
EventName,
isSupported
})
);

Expand Down Expand Up @@ -97,8 +102,31 @@ registerAnalytics(firebase as _FirebaseNamespace);
declare module '@firebase/app-types' {
interface FirebaseNamespace {
analytics(app?: FirebaseApp): FirebaseAnalytics;
isSupported(): Promise<boolean>;
}
interface FirebaseApp {
analytics(): FirebaseAnalytics;
}
}

/**
* this is a public static method provided to users that wraps three different checks:
*
* 1. check if cookie is enabled in current browser.
* 2. check if IndexedDB is supported by the browser environment.
* 3. check if the current browser context is valid for using IndexedDB.
*/
async function isSupported(): Promise<boolean> {
if (!areCookiesEnabled()) {
return false;
}
if (!isIndexedDBAvailable()) {
return false;
}
try {
const isDBOpenable: boolean = await validateIndexedDBOpenable();
return isDBOpenable;
} catch (error) {
return false;
}
}
2 changes: 1 addition & 1 deletion packages/analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
],
"reportDir": "./coverage/node"
}
}
}
16 changes: 14 additions & 2 deletions packages/analytics/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export const enum AnalyticsError {
NO_GA_ID = 'no-ga-id',
ALREADY_EXISTS = 'already-exists',
ALREADY_INITIALIZED = 'already-initialized',
INTEROP_COMPONENT_REG_FAILED = 'interop-component-reg-failed'
INTEROP_COMPONENT_REG_FAILED = 'interop-component-reg-failed',
INDEXED_DB_UNSUPPORTED = 'indexedDB-unsupported',
INVALID_INDEXED_DB_CONTEXT = 'invalid-indexedDB-context',
COOKIES_NOT_ENABLED = 'cookies-not-enabled'
}

const ERRORS: ErrorMap<AnalyticsError> = {
Expand All @@ -39,12 +42,21 @@ const ERRORS: ErrorMap<AnalyticsError> = {
'settings() must be called before initializing any Analytics instance' +
'or it will have no effect.',
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]:
'Firebase Analytics Interop Component failed to instantiate'
'Firebase Analytics Interop Component failed to instantiate',
[AnalyticsError.INDEXED_DB_UNSUPPORTED]:
'IndexedDB is not supported by current browswer',
[AnalyticsError.INVALID_INDEXED_DB_CONTEXT]:
"Environment doesn't support IndexedDB: {$errorInfo}. " +
'Wrap initialization of analytics in analytics.isSupported() ' +
'to prevent initialization in unsupported environments',
[AnalyticsError.COOKIES_NOT_ENABLED]:
'Cookies are not enabled in this browser environment. Analytics requires cookies to be enabled.'
};

interface ErrorParams {
[AnalyticsError.ALREADY_EXISTS]: { id: string };
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]: { reason: Error };
[AnalyticsError.INVALID_INDEXED_DB_CONTEXT]: { errorInfo: string };
}

export const ERROR_FACTORY = new ErrorFactory<AnalyticsError, ErrorParams>(
Expand Down
18 changes: 18 additions & 0 deletions packages/analytics/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ import { ANALYTICS_ID_FIELD } from './constants';
import { AnalyticsError, ERROR_FACTORY } from './errors';
import { FirebaseApp } from '@firebase/app-types';
import { FirebaseInstallations } from '@firebase/installations-types';
import {
isIndexedDBAvailable,
validateIndexedDBOpenable,
areCookiesEnabled
} from '@firebase/util';

/**
* Maps gaId to FID fetch promises.
Expand Down Expand Up @@ -117,6 +122,19 @@ export function factory(
app: FirebaseApp,
installations: FirebaseInstallations
): FirebaseAnalytics {
if (!areCookiesEnabled()) {
throw ERROR_FACTORY.create(AnalyticsError.COOKIES_NOT_ENABLED);
}
if (!isIndexedDBAvailable()) {
throw ERROR_FACTORY.create(AnalyticsError.INDEXED_DB_UNSUPPORTED);
}
// Async but non-blocking.
validateIndexedDBOpenable().catch(error => {
throw ERROR_FACTORY.create(AnalyticsError.INVALID_INDEXED_DB_CONTEXT, {
errorInfo: error
});
});

const analyticsId = app.options[ANALYTICS_ID_FIELD];
if (!analyticsId) {
throw ERROR_FACTORY.create(AnalyticsError.NO_GA_ID);
Expand Down
1 change: 0 additions & 1 deletion packages/analytics/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
} from './constants';
import { FirebaseInstallations } from '@firebase/installations-types';
import { logger } from './logger';

/**
* Initialize the analytics instance in gtag.js by calling config command with fid.
*
Expand Down
10 changes: 10 additions & 0 deletions packages/firebase/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5083,6 +5083,16 @@ declare namespace firebase.analytics {
id?: string;
name?: string;
}

/**
* An async function that returns true if current browser context supports initialization of analytics module
* (`firebase.analytics()`).
*
* Returns false otherwise.
*
*
*/
function isSupported(): Promise<boolean>;
}

declare namespace firebase.auth.Auth {
Expand Down
55 changes: 55 additions & 0 deletions packages/util/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,58 @@ export function isSafari(): boolean {
!navigator.userAgent.includes('Chrome')
);
}

/**
* This method checks if indexedDB is supported by current browser
* @return true if indexedDB is supported by current browser
*/
export function isIndexedDBAvailable(): boolean {
if (!('indexedDB' in window) || indexedDB === null) {
return false;
}
return true;
}

/**
* This method validates browser context for indexedDB by opening a dummy indexedDB database and reject
* if errors occur during the database open operation.
*/
export function validateIndexedDBOpenable(): Promise<boolean> {
return new Promise((resolve, reject) => {
try {
let preExist: boolean = true;
const DB_CHECK_NAME =
'validate-browser-context-for-indexeddb-analytics-module';
const request = window.indexedDB.open(DB_CHECK_NAME);
request.onsuccess = () => {
request.result.close();
// delete database only when it doesn't pre-exist
if (!preExist) {
window.indexedDB.deleteDatabase(DB_CHECK_NAME);
}
resolve(true);
};
request.onupgradeneeded = () => {
preExist = false;
};

request.onerror = () => {
reject(request.error?.message || '');
};
} catch (error) {
reject(error);
}
});
}

/**
*
* This method checks whether cookie is enabled within current browser
* @return true if cookie is enabled within current browser
*/
export function areCookiesEnabled(): boolean {
if (!navigator || !navigator.cookieEnabled) {
return false;
}
return true;
}