From 374e222609fbbe041f6889e7531d4365e1230a48 Mon Sep 17 00:00:00 2001 From: XuechunHou Date: Wed, 12 Aug 2020 16:26:02 -0600 Subject: [PATCH 1/6] added indexedDB check for remote-config --- packages/firebase/compat/index.d.ts | 7 +++ packages/remote-config/src/api2.ts | 23 ++++++++- packages/remote-config/src/errors.ts | 7 ++- packages/remote-config/src/register.ts | 8 ++- packages/remote-config/src/storage/storage.ts | 51 +++++++++++-------- 5 files changed, 71 insertions(+), 25 deletions(-) diff --git a/packages/firebase/compat/index.d.ts b/packages/firebase/compat/index.d.ts index d9d017b81c2..fcc7e34c842 100644 --- a/packages/firebase/compat/index.d.ts +++ b/packages/firebase/compat/index.d.ts @@ -2031,6 +2031,13 @@ declare namespace firebase.remoteConfig { * Defines levels of Remote Config logging. */ export type LogLevel = 'debug' | 'error' | 'silent'; + /** + * An async function that returns true if current browser context supports + * initialization of `RemoteConfig` instance (`firebase.remoteConfig()`). + * + * Returns false otherwise. + */ + export function isSupported(): Promise; } declare namespace firebase.functions { diff --git a/packages/remote-config/src/api2.ts b/packages/remote-config/src/api2.ts index ca45119a0ef..6ff218ff4c9 100644 --- a/packages/remote-config/src/api2.ts +++ b/packages/remote-config/src/api2.ts @@ -17,7 +17,7 @@ import { RemoteConfig } from './public_types'; import { activate, fetchConfig } from './api'; -import { getModularInstance } from '@firebase/util'; +import { getModularInstance, isIndexedDBAvailable, validateIndexedDBOpenable } from '@firebase/util'; // This API is put in a separate file, so we can stub fetchConfig and activate in tests. // It's not possible to stub standalone functions from the same module. @@ -39,3 +39,24 @@ export async function fetchAndActivate( await fetchConfig(remoteConfig); return activate(remoteConfig); } + +/** + * This is a public static method provided to users that wraps two different checks: + * + * 1. check if IndexedDB is supported by the browser environment. + * 2. check if the current browser context is valid for using IndexedDB. + * @public + * + */ + export async function isSupported(): Promise { + if (!isIndexedDBAvailable()) { + return false; + } + + try { + const isDBOpenable: boolean = await validateIndexedDBOpenable(); + return isDBOpenable; + } catch (error) { + return false; + } +} \ No newline at end of file diff --git a/packages/remote-config/src/errors.ts b/packages/remote-config/src/errors.ts index d4be9a09f76..93e5bc5405c 100644 --- a/packages/remote-config/src/errors.ts +++ b/packages/remote-config/src/errors.ts @@ -30,7 +30,8 @@ export const enum ErrorCode { FETCH_TIMEOUT = 'fetch-timeout', FETCH_THROTTLE = 'fetch-throttle', FETCH_PARSE = 'fetch-client-parse', - FETCH_STATUS = 'fetch-status' + FETCH_STATUS = 'fetch-status', + INDEXED_DB_UNSUPPORTED = 'indexed-db-unsupported' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { @@ -64,7 +65,9 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { 'Fetch client could not parse response.' + ' Original error: {$originalErrorMessage}.', [ErrorCode.FETCH_STATUS]: - 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.' + 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.', + [ErrorCode.INDEXED_DB_UNSUPPORTED]: + 'Indexed DB is not supported by current browser' }; // Note this is effectively a type system binding a code to params. This approach overlaps with the diff --git a/packages/remote-config/src/register.ts b/packages/remote-config/src/register.ts index b06f76b380a..7aa773d3879 100644 --- a/packages/remote-config/src/register.ts +++ b/packages/remote-config/src/register.ts @@ -19,6 +19,9 @@ import { registerVersion, SDK_VERSION } from '@firebase/app'; +import { + isIndexedDBAvailable +} from '@firebase/util'; import { Component, ComponentType, @@ -68,7 +71,10 @@ export function registerRemoteConfig(): void { if (typeof window === 'undefined') { throw ERROR_FACTORY.create(ErrorCode.REGISTRATION_WINDOW); } - + // Guards against the SDK being used when indexedDB is not available. + if (!isIndexedDBAvailable()) { + throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNSUPPORTED); + } // Normalizes optional inputs. const { projectId, apiKey, appId } = app.options; if (!projectId) { diff --git a/packages/remote-config/src/storage/storage.ts b/packages/remote-config/src/storage/storage.ts index f5f457161b1..a7ec2d447f9 100644 --- a/packages/remote-config/src/storage/storage.ts +++ b/packages/remote-config/src/storage/storage.ts @@ -22,6 +22,7 @@ import { } from '../client/remote_config_fetch_client'; import { ERROR_FACTORY, ErrorCode } from '../errors'; import { FirebaseError } from '@firebase/util'; +import { ErrorFactory } from '@firebase/util'; /** * Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}. @@ -75,28 +76,36 @@ type ProjectNamespaceKeyFieldValue = // Visible for testing. export function openDatabase(): Promise { return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); - request.onerror = event => { - reject(toFirebaseError(event, ErrorCode.STORAGE_OPEN)); - }; - request.onsuccess = event => { - resolve((event.target as IDBOpenDBRequest).result); - }; - request.onupgradeneeded = event => { - const db = (event.target as IDBOpenDBRequest).result; + try { + const request = indexedDB.open(DB_NAME, DB_VERSION); + request.onerror = event => { + reject(toFirebaseError(event, ErrorCode.STORAGE_OPEN)); + }; + request.onsuccess = event => { + resolve((event.target as IDBOpenDBRequest).result); + }; + request.onupgradeneeded = event => { + const db = (event.target as IDBOpenDBRequest).result; - // We don't use 'break' in this switch statement, the fall-through - // behavior is what we want, because if there are multiple versions between - // the old version and the current version, we want ALL the migrations - // that correspond to those versions to run, not only the last one. - // eslint-disable-next-line default-case - switch (event.oldVersion) { - case 0: - db.createObjectStore(APP_NAMESPACE_STORE, { - keyPath: 'compositeKey' - }); - } - }; + // We don't use 'break' in this switch statement, the fall-through + // behavior is what we want, because if there are multiple versions between + // the old version and the current version, we want ALL the migrations + // that correspond to those versions to run, not only the last one. + // eslint-disable-next-line default-case + switch (event.oldVersion) { + case 0: + db.createObjectStore(APP_NAMESPACE_STORE, { + keyPath: 'compositeKey' + }); + } + }; + } catch (error) { + reject( + ERROR_FACTORY.create(ErrorCode.STORAGE_OPEN, { + originalErrorMessage: error + }) + ); + } }); } From 8e71b56a63d90b9bdcba056b342fe0b02103e422 Mon Sep 17 00:00:00 2001 From: XuechunHou Date: Wed, 12 Aug 2020 16:33:03 -0600 Subject: [PATCH 2/6] removed unused imports --- packages/remote-config/src/storage/storage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/remote-config/src/storage/storage.ts b/packages/remote-config/src/storage/storage.ts index a7ec2d447f9..d49ed0ab2c5 100644 --- a/packages/remote-config/src/storage/storage.ts +++ b/packages/remote-config/src/storage/storage.ts @@ -22,7 +22,6 @@ import { } from '../client/remote_config_fetch_client'; import { ERROR_FACTORY, ErrorCode } from '../errors'; import { FirebaseError } from '@firebase/util'; -import { ErrorFactory } from '@firebase/util'; /** * Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}. From b8ec2e095fc9b7d484a25409025729b7b77f5f29 Mon Sep 17 00:00:00 2001 From: XuechunHou Date: Tue, 18 Aug 2020 14:02:44 -0600 Subject: [PATCH 3/6] addressed PR comments --- packages/firebase/compat/index.d.ts | 4 ++-- packages/remote-config/src/errors.ts | 4 ++-- packages/remote-config/src/register.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/firebase/compat/index.d.ts b/packages/firebase/compat/index.d.ts index fcc7e34c842..c29869b014c 100644 --- a/packages/firebase/compat/index.d.ts +++ b/packages/firebase/compat/index.d.ts @@ -2032,8 +2032,8 @@ declare namespace firebase.remoteConfig { */ export type LogLevel = 'debug' | 'error' | 'silent'; /** - * An async function that returns true if current browser context supports - * initialization of `RemoteConfig` instance (`firebase.remoteConfig()`). + * Returns true if current browser context supports + * initialization of a `RemoteConfig` instance (`firebase.remoteConfig()`). * * Returns false otherwise. */ diff --git a/packages/remote-config/src/errors.ts b/packages/remote-config/src/errors.ts index 93e5bc5405c..eac9a25657b 100644 --- a/packages/remote-config/src/errors.ts +++ b/packages/remote-config/src/errors.ts @@ -31,7 +31,7 @@ export const enum ErrorCode { FETCH_THROTTLE = 'fetch-throttle', FETCH_PARSE = 'fetch-client-parse', FETCH_STATUS = 'fetch-status', - INDEXED_DB_UNSUPPORTED = 'indexed-db-unsupported' + INDEXED_DB_UNAVAILABLE = 'indexed-db-unavailable' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { @@ -66,7 +66,7 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { ' Original error: {$originalErrorMessage}.', [ErrorCode.FETCH_STATUS]: 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.', - [ErrorCode.INDEXED_DB_UNSUPPORTED]: + [ErrorCode.INDEXED_DB_UNAVAILABLE]: 'Indexed DB is not supported by current browser' }; diff --git a/packages/remote-config/src/register.ts b/packages/remote-config/src/register.ts index 7aa773d3879..df3e6f73dc7 100644 --- a/packages/remote-config/src/register.ts +++ b/packages/remote-config/src/register.ts @@ -73,7 +73,7 @@ export function registerRemoteConfig(): void { } // Guards against the SDK being used when indexedDB is not available. if (!isIndexedDBAvailable()) { - throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNSUPPORTED); + throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNAVAILABLE); } // Normalizes optional inputs. const { projectId, apiKey, appId } = app.options; From 40c5a11b4f61a344c11a36f40e8f6aee2b3a219d Mon Sep 17 00:00:00 2001 From: Xuechun Hou Date: Tue, 18 Aug 2020 14:06:17 -0600 Subject: [PATCH 4/6] Create blue-rice-ring.md --- .changeset/blue-rice-ring.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/blue-rice-ring.md diff --git a/.changeset/blue-rice-ring.md b/.changeset/blue-rice-ring.md new file mode 100644 index 00000000000..56e182581d6 --- /dev/null +++ b/.changeset/blue-rice-ring.md @@ -0,0 +1,6 @@ +--- +"firebase": minor +"@firebase/remote-config": minor +--- + +Issue 2393 - Add environment check to Remote-Config Module From ae139f1eccbf6b871c601b32dbd822a3e975e4a4 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Fri, 17 Sep 2021 10:39:46 -0700 Subject: [PATCH 5/6] Rebase and update --- common/api-review/remote-config.api.md | 3 +++ packages/firebase/compat/index.d.ts | 22 ++++++++++++------- packages/remote-config-compat/src/index.ts | 3 ++- .../remote-config-compat/src/remoteConfig.ts | 5 ++++- packages/remote-config/src/api2.ts | 10 +++++---- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/common/api-review/remote-config.api.md b/common/api-review/remote-config.api.md index 8ddf568d0c0..b5d02381cc3 100644 --- a/common/api-review/remote-config.api.md +++ b/common/api-review/remote-config.api.md @@ -39,6 +39,9 @@ export function getString(remoteConfig: RemoteConfig, key: string): string; // @public export function getValue(remoteConfig: RemoteConfig, key: string): Value; +// @public +export function isSupported(): Promise; + // @public export type LogLevel = 'debug' | 'error' | 'silent'; diff --git a/packages/firebase/compat/index.d.ts b/packages/firebase/compat/index.d.ts index c29869b014c..e90812b454f 100644 --- a/packages/firebase/compat/index.d.ts +++ b/packages/firebase/compat/index.d.ts @@ -2032,10 +2032,13 @@ declare namespace firebase.remoteConfig { */ export type LogLevel = 'debug' | 'error' | 'silent'; /** - * Returns true if current browser context supports - * initialization of a `RemoteConfig` instance (`firebase.remoteConfig()`). + * This method provides two different checks: * - * Returns false otherwise. + * 1. Check if IndexedDB exists in the browser environment. + * 2. Check if the current browser context allows IndexedDB `open()` calls. + * + * It returns a `Promise` which resolves to true if a {@link RemoteConfig} instance + * can be initialized in this environment, or false if it cannot. */ export function isSupported(): Promise; } @@ -4630,13 +4633,14 @@ declare namespace firebase.auth { * instance must be initialized with an API key, otherwise an error will be * thrown. */ - class RecaptchaVerifier extends RecaptchaVerifier_Instance { } + class RecaptchaVerifier extends RecaptchaVerifier_Instance {} /** * @webonly * @hidden */ class RecaptchaVerifier_Instance - implements firebase.auth.ApplicationVerifier { + implements firebase.auth.ApplicationVerifier + { constructor( container: any | string, parameters?: Object | null, @@ -7317,7 +7321,7 @@ declare namespace firebase.database { interface ThenableReference extends firebase.database.Reference, - Pick, 'then' | 'catch'> { } + Pick, 'then' | 'catch'> {} /** * Logs debugging information to the console. @@ -7697,7 +7701,9 @@ declare namespace firebase.storage { * resolves with the full updated metadata or rejects if the updated failed, * including if the object did not exist. */ - updateMetadata(metadata: firebase.storage.SettableMetadata): Promise; + updateMetadata( + metadata: firebase.storage.SettableMetadata + ): Promise; /** * List all items (files) and prefixes (folders) under this storage reference. * @@ -9527,7 +9533,7 @@ declare namespace firebase.firestore { */ export class QueryDocumentSnapshot< T = DocumentData - > extends DocumentSnapshot { + > extends DocumentSnapshot { private constructor(); /** diff --git a/packages/remote-config-compat/src/index.ts b/packages/remote-config-compat/src/index.ts index ac9b2df6ff9..a7e682ba97d 100644 --- a/packages/remote-config-compat/src/index.ts +++ b/packages/remote-config-compat/src/index.ts @@ -22,7 +22,7 @@ import { ComponentType, InstanceFactoryOptions } from '@firebase/component'; -import { RemoteConfigCompatImpl } from './remoteConfig'; +import { RemoteConfigCompatImpl, isSupported } from './remoteConfig'; import { name as packageName, version } from '../package.json'; import { RemoteConfig as RemoteConfigCompat } from '@firebase/remote-config-types'; @@ -35,6 +35,7 @@ function registerRemoteConfigCompat( remoteConfigFactory, ComponentType.PUBLIC ).setMultipleInstances(true) + .setServiceProps({ isSupported }) ); firebaseInstance.registerVersion(packageName, version); diff --git a/packages/remote-config-compat/src/remoteConfig.ts b/packages/remote-config-compat/src/remoteConfig.ts index af529b7a670..41379ee8d52 100644 --- a/packages/remote-config-compat/src/remoteConfig.ts +++ b/packages/remote-config-compat/src/remoteConfig.ts @@ -34,9 +34,12 @@ import { getBoolean, getNumber, getString, - getValue + getValue, + isSupported } from '@firebase/remote-config'; +export { isSupported }; + export class RemoteConfigCompatImpl implements RemoteConfigCompat, _FirebaseService { constructor(public app: FirebaseApp, readonly _delegate: RemoteConfig) {} diff --git a/packages/remote-config/src/api2.ts b/packages/remote-config/src/api2.ts index 6ff218ff4c9..42df64ee47e 100644 --- a/packages/remote-config/src/api2.ts +++ b/packages/remote-config/src/api2.ts @@ -41,12 +41,14 @@ export async function fetchAndActivate( } /** - * This is a public static method provided to users that wraps two different checks: + * This method provides two different checks: * - * 1. check if IndexedDB is supported by the browser environment. - * 2. check if the current browser context is valid for using IndexedDB. + * 1. Check if IndexedDB exists in the browser environment. + * 2. Check if the current browser context allows IndexedDB `open()` calls. + * + * @returns A `Promise` which resolves to true if a {@link RemoteConfig} instance + * can be initialized in this environment, or false if it cannot. * @public - * */ export async function isSupported(): Promise { if (!isIndexedDBAvailable()) { From d11c83db89e34a0604513fcb7e0b54a3e546d20c Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Fri, 17 Sep 2021 10:39:56 -0700 Subject: [PATCH 6/6] prettier --- packages/remote-config-compat/src/index.ts | 5 +++-- packages/remote-config-compat/src/remoteConfig.ts | 3 ++- packages/remote-config/src/api2.ts | 12 ++++++++---- packages/remote-config/src/register.ts | 4 +--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/remote-config-compat/src/index.ts b/packages/remote-config-compat/src/index.ts index a7e682ba97d..234030cae0e 100644 --- a/packages/remote-config-compat/src/index.ts +++ b/packages/remote-config-compat/src/index.ts @@ -34,8 +34,9 @@ function registerRemoteConfigCompat( 'remoteConfig-compat', remoteConfigFactory, ComponentType.PUBLIC - ).setMultipleInstances(true) - .setServiceProps({ isSupported }) + ) + .setMultipleInstances(true) + .setServiceProps({ isSupported }) ); firebaseInstance.registerVersion(packageName, version); diff --git a/packages/remote-config-compat/src/remoteConfig.ts b/packages/remote-config-compat/src/remoteConfig.ts index 41379ee8d52..7617a7f2d0b 100644 --- a/packages/remote-config-compat/src/remoteConfig.ts +++ b/packages/remote-config-compat/src/remoteConfig.ts @@ -41,7 +41,8 @@ import { export { isSupported }; export class RemoteConfigCompatImpl - implements RemoteConfigCompat, _FirebaseService { + implements RemoteConfigCompat, _FirebaseService +{ constructor(public app: FirebaseApp, readonly _delegate: RemoteConfig) {} get defaultConfig(): { [key: string]: string | number | boolean } { diff --git a/packages/remote-config/src/api2.ts b/packages/remote-config/src/api2.ts index 42df64ee47e..6bd67ffabb8 100644 --- a/packages/remote-config/src/api2.ts +++ b/packages/remote-config/src/api2.ts @@ -17,7 +17,11 @@ import { RemoteConfig } from './public_types'; import { activate, fetchConfig } from './api'; -import { getModularInstance, isIndexedDBAvailable, validateIndexedDBOpenable } from '@firebase/util'; +import { + getModularInstance, + isIndexedDBAvailable, + validateIndexedDBOpenable +} from '@firebase/util'; // This API is put in a separate file, so we can stub fetchConfig and activate in tests. // It's not possible to stub standalone functions from the same module. @@ -45,12 +49,12 @@ export async function fetchAndActivate( * * 1. Check if IndexedDB exists in the browser environment. * 2. Check if the current browser context allows IndexedDB `open()` calls. - * + * * @returns A `Promise` which resolves to true if a {@link RemoteConfig} instance * can be initialized in this environment, or false if it cannot. * @public */ - export async function isSupported(): Promise { +export async function isSupported(): Promise { if (!isIndexedDBAvailable()) { return false; } @@ -61,4 +65,4 @@ export async function fetchAndActivate( } catch (error) { return false; } -} \ No newline at end of file +} diff --git a/packages/remote-config/src/register.ts b/packages/remote-config/src/register.ts index df3e6f73dc7..b4ddae85dd6 100644 --- a/packages/remote-config/src/register.ts +++ b/packages/remote-config/src/register.ts @@ -19,9 +19,7 @@ import { registerVersion, SDK_VERSION } from '@firebase/app'; -import { - isIndexedDBAvailable -} from '@firebase/util'; +import { isIndexedDBAvailable } from '@firebase/util'; import { Component, ComponentType,