UNPKG

voluptasmollitia

Version:
261 lines (245 loc) 8.41 kB
/** * @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 { _getProvider, FirebaseApp, getApp } from '@firebase/app-exp'; import { LogLevel as RemoteConfigLogLevel, RemoteConfig, Value } from './public_types'; import { RemoteConfigAbortSignal } from './client/remote_config_fetch_client'; import { RC_COMPONENT_NAME } from './constants'; import { ErrorCode, hasErrorCode } from './errors'; import { RemoteConfig as RemoteConfigImpl } from './remote_config'; import { Value as ValueImpl } from './value'; import { LogLevel as FirebaseLogLevel } from '@firebase/logger'; import { getModularInstance } from '@firebase/util'; /** * * @param app - The `FirebaseApp` instance. * @returns A `RemoteConfig` instance. * * @public */ export function getRemoteConfig(app: FirebaseApp = getApp()): RemoteConfig { app = getModularInstance(app); const rcProvider = _getProvider(app, RC_COMPONENT_NAME); return rcProvider.getImmediate(); } /** * Makes the last fetched config available to the getters. * @param remoteConfig - The `RemoteConfig` instance. * @returns A promise which resolves to true if the current call activated the fetched configs. * If the fetched configs were already activated, the promise will resolve to false. * * @public */ export async function activate(remoteConfig: RemoteConfig): Promise<boolean> { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; const [lastSuccessfulFetchResponse, activeConfigEtag] = await Promise.all([ rc._storage.getLastSuccessfulFetchResponse(), rc._storage.getActiveConfigEtag() ]); if ( !lastSuccessfulFetchResponse || !lastSuccessfulFetchResponse.config || !lastSuccessfulFetchResponse.eTag || lastSuccessfulFetchResponse.eTag === activeConfigEtag ) { // Either there is no successful fetched config, or is the same as current active // config. return false; } await Promise.all([ rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config), rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag) ]); return true; } /** * Ensures the last activated config are available to the getters. * @param remoteConfig - The `RemoteConfig` instance. * * @returns A promise that resolves when the last activated config is available to the getters. * @public */ export function ensureInitialized(remoteConfig: RemoteConfig): Promise<void> { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; if (!rc._initializePromise) { rc._initializePromise = rc._storageCache.loadFromStorage().then(() => { rc._isInitializationComplete = true; }); } return rc._initializePromise; } /** * Fetches and caches configuration from the Remote Config service. * @param remoteConfig - The `RemoteConfig` instance. * @public */ export async function fetchConfig(remoteConfig: RemoteConfig): Promise<void> { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; // Aborts the request after the given timeout, causing the fetch call to // reject with an AbortError. // // <p>Aborting after the request completes is a no-op, so we don't need a // corresponding clearTimeout. // // Locating abort logic here because: // * it uses a developer setting (timeout) // * it applies to all retries (like curl's max-time arg) // * it is consistent with the Fetch API's signal input const abortSignal = new RemoteConfigAbortSignal(); setTimeout(async () => { // Note a very low delay, eg < 10ms, can elapse before listeners are initialized. abortSignal.abort(); }, rc.settings.fetchTimeoutMillis); // Catches *all* errors thrown by client so status can be set consistently. try { await rc._client.fetch({ cacheMaxAgeMillis: rc.settings.minimumFetchIntervalMillis, signal: abortSignal }); await rc._storageCache.setLastFetchStatus('success'); } catch (e) { const lastFetchStatus = hasErrorCode(e, ErrorCode.FETCH_THROTTLE) ? 'throttle' : 'failure'; await rc._storageCache.setLastFetchStatus(lastFetchStatus); throw e; } } /** * Gets all config. * * @param remoteConfig - The `RemoteConfig` instance. * @returns All config. * * @public */ export function getAll(remoteConfig: RemoteConfig): Record<string, Value> { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; return getAllKeys( rc._storageCache.getActiveConfig(), rc.defaultConfig ).reduce((allConfigs, key) => { allConfigs[key] = getValue(remoteConfig, key); return allConfigs; }, {} as Record<string, Value>); } /** * Gets the value for the given key as a boolean. * * Convenience method for calling <code>remoteConfig.getValue(key).asBoolean()</code>. * * @param remoteConfig - The `RemoteConfig` instance. * @param key - The name of the parameter. * * @returns The value for the given key as a boolean. * @public */ export function getBoolean(remoteConfig: RemoteConfig, key: string): boolean { return getValue(getModularInstance(remoteConfig), key).asBoolean(); } /** * Gets the value for the given key as a number. * * Convenience method for calling <code>remoteConfig.getValue(key).asNumber()</code>. * * @param remoteConfig - The `RemoteConfig` instance. * @param key - The name of the parameter. * * @returns The value for the given key as a number. * * @public */ export function getNumber(remoteConfig: RemoteConfig, key: string): number { return getValue(getModularInstance(remoteConfig), key).asNumber(); } /** * Gets the value for the given key as a string. * Convenience method for calling <code>remoteConfig.getValue(key).asString()</code>. * * @param remoteConfig - The `RemoteConfig` instance. * @param key - The name of the parameter. * * @returns The value for the given key as a string. * * @public */ export function getString(remoteConfig: RemoteConfig, key: string): string { return getValue(getModularInstance(remoteConfig), key).asString(); } /** * Gets the {@link Value} for the given key. * * @param remoteConfig - The `RemoteConfig` instance. * @param key - The name of the parameter. * * @returns The value for the given key. * * @public */ export function getValue(remoteConfig: RemoteConfig, key: string): Value { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; if (!rc._isInitializationComplete) { rc._logger.debug( `A value was requested for key "${key}" before SDK initialization completed.` + ' Await on ensureInitialized if the intent was to get a previously activated value.' ); } const activeConfig = rc._storageCache.getActiveConfig(); if (activeConfig && activeConfig[key] !== undefined) { return new ValueImpl('remote', activeConfig[key]); } else if (rc.defaultConfig && rc.defaultConfig[key] !== undefined) { return new ValueImpl('default', String(rc.defaultConfig[key])); } rc._logger.debug( `Returning static value for key "${key}".` + ' Define a default or remote value if this is unintentional.' ); return new ValueImpl('static'); } /** * Defines the log level to use. * * @param remoteConfig - The `RemoteConfig` instance. * @param logLevel - The log level to set. * * @public */ export function setLogLevel( remoteConfig: RemoteConfig, logLevel: RemoteConfigLogLevel ): void { const rc = getModularInstance(remoteConfig) as RemoteConfigImpl; switch (logLevel) { case 'debug': rc._logger.logLevel = FirebaseLogLevel.DEBUG; break; case 'silent': rc._logger.logLevel = FirebaseLogLevel.SILENT; break; default: rc._logger.logLevel = FirebaseLogLevel.ERROR; } } /** * Dedupes and returns an array of all the keys of the received objects. */ function getAllKeys(obj1: {} = {}, obj2: {} = {}): string[] { return Object.keys({ ...obj1, ...obj2 }); }