Skip to content

Commit 3e314a7

Browse files
committed
implement funcitonal APIs
1 parent fba771f commit 3e314a7

File tree

6 files changed

+185
-69
lines changed

6 files changed

+185
-69
lines changed

packages-exp/remote-config-exp/index.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

packages-exp/remote-config-exp/src/api.ts

Lines changed: 135 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,169 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { _getProvider } from '@firebase/app-exp';
1819
import { FirebaseApp } from '@firebase/app-types-exp';
1920
import {
20-
LogLevel,
21+
LogLevel as RemoteConfigLogLevel,
2122
RemoteConfig,
22-
Value
23+
Value as ValueType
2324
} from '@firebase/remote-config-types-exp';
25+
import { RemoteConfigAbortSignal } from './client/remote_config_fetch_client';
26+
import { RC_COMPONENT_NAME } from './constants';
27+
import { ErrorCode, hasErrorCode } from './errors';
28+
import { RemoteConfig as RemoteConfigImpl } from './remote_config';
29+
import { Value } from './value';
30+
import { LogLevel as FirebaseLogLevel } from '@firebase/logger';
2431

2532
export function getRemoteConfig(app: FirebaseApp): RemoteConfig {
26-
throw Error('not implemented!');
33+
const rcProvider = _getProvider(app, RC_COMPONENT_NAME);
34+
return rcProvider.getImmediate();
2735
}
2836

29-
export function activate(remoteConfig: RemoteConfig): Promise<boolean> {
30-
throw Error('not implemented!');
37+
export async function activate(remoteConfig: RemoteConfig): Promise<boolean> {
38+
const rc = asRemoteConfigImpl(remoteConfig);
39+
const [lastSuccessfulFetchResponse, activeConfigEtag] = await Promise.all([
40+
rc._storage.getLastSuccessfulFetchResponse(),
41+
rc._storage.getActiveConfigEtag()
42+
]);
43+
if (
44+
!lastSuccessfulFetchResponse ||
45+
!lastSuccessfulFetchResponse.config ||
46+
!lastSuccessfulFetchResponse.eTag ||
47+
lastSuccessfulFetchResponse.eTag === activeConfigEtag
48+
) {
49+
// Either there is no successful fetched config, or is the same as current active
50+
// config.
51+
return false;
52+
}
53+
await Promise.all([
54+
rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
55+
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
56+
]);
57+
return true;
3158
}
3259

3360
export function ensureInitialized(remoteConfig: RemoteConfig): Promise<void> {
34-
throw Error('not implemented!');
61+
const rc = remoteConfig as RemoteConfigImpl;
62+
if (!rc._initializePromise) {
63+
rc._initializePromise = rc._storageCache.loadFromStorage().then(() => {
64+
rc._isInitializationComplete = true;
65+
});
66+
}
67+
return rc._initializePromise;
3568
}
3669

37-
export function fetchConfig(remoteConfig: RemoteConfig): Promise<void> {
38-
throw Error('not implemented!');
70+
export async function fetchConfig(remoteConfig: RemoteConfig): Promise<void> {
71+
const rc = asRemoteConfigImpl(remoteConfig);
72+
// Aborts the request after the given timeout, causing the fetch call to
73+
// reject with an AbortError.
74+
//
75+
// <p>Aborting after the request completes is a no-op, so we don't need a
76+
// corresponding clearTimeout.
77+
//
78+
// Locating abort logic here because:
79+
// * it uses a developer setting (timeout)
80+
// * it applies to all retries (like curl's max-time arg)
81+
// * it is consistent with the Fetch API's signal input
82+
const abortSignal = new RemoteConfigAbortSignal();
83+
84+
setTimeout(async () => {
85+
// Note a very low delay, eg < 10ms, can elapse before listeners are initialized.
86+
abortSignal.abort();
87+
}, rc.settings.fetchTimeoutMillis);
88+
89+
// Catches *all* errors thrown by client so status can be set consistently.
90+
try {
91+
await rc._client.fetch({
92+
cacheMaxAgeMillis: rc.settings.minimumFetchIntervalMillis,
93+
signal: abortSignal
94+
});
95+
96+
await rc._storageCache.setLastFetchStatus('success');
97+
} catch (e) {
98+
const lastFetchStatus = hasErrorCode(e, ErrorCode.FETCH_THROTTLE)
99+
? 'throttle'
100+
: 'failure';
101+
await rc._storageCache.setLastFetchStatus(lastFetchStatus);
102+
throw e;
103+
}
39104
}
40105

41-
export function fetchAndActivate(remoteConfig: RemoteConfig): Promise<boolean> {
42-
throw Error('not implemented!');
106+
export async function fetchAndActivate(
107+
remoteConfig: RemoteConfig
108+
): Promise<boolean> {
109+
await fetchConfig(remoteConfig);
110+
return activate(remoteConfig);
43111
}
44112

45-
export function getAll(remoteConfig: RemoteConfig): Record<string, Value> {
46-
throw Error('not implemented!');
113+
export function getAll(remoteConfig: RemoteConfig): Record<string, ValueType> {
114+
const rc = asRemoteConfigImpl(remoteConfig);
115+
return getAllKeys(
116+
rc._storageCache.getActiveConfig(),
117+
rc.defaultConfig
118+
).reduce((allConfigs, key) => {
119+
allConfigs[key] = getValue(remoteConfig, key);
120+
return allConfigs;
121+
}, {} as Record<string, ValueType>);
47122
}
48123

49124
export function getBoolean(remoteConfig: RemoteConfig, key: string): boolean {
50-
throw Error('not implemented!');
125+
return getValue(remoteConfig, key).asBoolean();
51126
}
52127

53128
export function getNumber(remoteConfig: RemoteConfig, key: string): number {
54-
throw Error('not implemented!');
129+
return getValue(remoteConfig, key).asNumber();
55130
}
56131

57132
export function getString(remoteConfig: RemoteConfig, key: string): string {
58-
throw Error('not implemented!');
133+
return getValue(remoteConfig, key).asString();
134+
}
135+
136+
export function getValue(remoteConfig: RemoteConfig, key: string): ValueType {
137+
const rc = asRemoteConfigImpl(remoteConfig);
138+
if (!rc._isInitializationComplete) {
139+
rc._logger.debug(
140+
`A value was requested for key "${key}" before SDK initialization completed.` +
141+
' Await on ensureInitialized if the intent was to get a previously activated value.'
142+
);
143+
}
144+
const activeConfig = rc._storageCache.getActiveConfig();
145+
if (activeConfig && activeConfig[key] !== undefined) {
146+
return new Value('remote', activeConfig[key]);
147+
} else if (rc.defaultConfig && rc.defaultConfig[key] !== undefined) {
148+
return new Value('default', String(rc.defaultConfig[key]));
149+
}
150+
rc._logger.debug(
151+
`Returning static value for key "${key}".` +
152+
' Define a default or remote value if this is unintentional.'
153+
);
154+
return new Value('static');
59155
}
60156

61-
export function getValue(remoteConfig: RemoteConfig, key: string): Value {
62-
throw Error('not implemented!');
157+
export function setLogLevel(
158+
remoteConfig: RemoteConfig,
159+
logLevel: RemoteConfigLogLevel
160+
) {
161+
const rc = asRemoteConfigImpl(remoteConfig);
162+
switch (logLevel) {
163+
case 'debug':
164+
rc._logger.logLevel = FirebaseLogLevel.DEBUG;
165+
break;
166+
case 'silent':
167+
rc._logger.logLevel = FirebaseLogLevel.SILENT;
168+
break;
169+
default:
170+
rc._logger.logLevel = FirebaseLogLevel.ERROR;
171+
}
63172
}
64173

65-
export function setLogLevel(remoteConfig: RemoteConfig, logLevel: LogLevel) {
66-
throw Error('not implemented!');
174+
function asRemoteConfigImpl(remoteConfig: RemoteConfig): RemoteConfigImpl {
175+
return remoteConfig as RemoteConfigImpl;
176+
}
177+
178+
/**
179+
* Dedupes and returns an array of all the keys of the received objects.
180+
*/
181+
function getAllKeys(obj1: {} = {}, obj2: {} = {}): string[] {
182+
return Object.keys({ ...obj1, ...obj2 });
67183
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2020 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+
export const RC_COMPONENT_NAME = 'remote-config-exp';

packages-exp/remote-config-exp/src/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,19 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { registerRemoteConfig } from './register';
19+
20+
// Facilitates debugging by enabling settings changes without rebuilding asset.
21+
// Note these debug options are not part of a documented, supported API and can change at any time.
22+
// Consolidates debug options for easier discovery.
23+
// Uses transient variables on window to avoid lingering state causing panic.
24+
declare global {
25+
interface Window {
26+
FIREBASE_REMOTE_CONFIG_URL_BASE: string;
27+
}
28+
}
29+
1830
export * from './api';
31+
32+
/** register component and version */
33+
registerRemoteConfig();

packages-exp/remote-config-exp/src/register.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,12 @@ import { ensureInitialized } from './api';
3131
import { CachingClient } from './client/caching_client';
3232
import { RestClient } from './client/rest_client';
3333
import { RetryingClient } from './client/retrying_client';
34+
import { RC_COMPONENT_NAME } from './constants';
3435
import { ErrorCode, ERROR_FACTORY } from './errors';
3536
import { RemoteConfig as RemoteConfigImpl } from './remote_config';
3637
import { Storage } from './storage/storage';
3738
import { StorageCache } from './storage/storage_cache';
3839

39-
const RC_COMPONENT_NAME = 'remote-config-exp';
40-
4140
export function registerRemoteConfig(): void {
4241
_registerComponent(
4342
new Component(

packages-exp/remote-config-exp/src/remote_config.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,21 @@ export class RemoteConfig implements RemoteConfigType {
6868
// JS doesn't support private yet
6969
// (https://github.com/tc39/proposal-class-fields#private-fields), so we hint using an
7070
// underscore prefix.
71-
private readonly _client: RemoteConfigFetchClient,
72-
private readonly _storageCache: StorageCache,
73-
private readonly _storage: Storage,
74-
private readonly _logger: Logger
71+
/**
72+
* @internal
73+
*/
74+
readonly _client: RemoteConfigFetchClient,
75+
/**
76+
* @internal
77+
*/
78+
readonly _storageCache: StorageCache,
79+
/**
80+
* @internal
81+
*/
82+
readonly _storage: Storage,
83+
/**
84+
* @internal
85+
*/
86+
readonly _logger: Logger
7587
) {}
7688
}

0 commit comments

Comments
 (0)