diff --git a/common/api-review/remote-config.api.md b/common/api-review/remote-config.api.md index 1da7c29df0d..de009aa46de 100644 --- a/common/api-review/remote-config.api.md +++ b/common/api-review/remote-config.api.md @@ -42,11 +42,15 @@ export interface FetchResponse { config?: FirebaseRemoteConfigObject; eTag?: string; status: number; + templateVersion?: number; } // @public export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle'; +// @public +export type FetchType = 'BASE' | 'REALTIME'; + // @public export interface FirebaseRemoteConfigObject { // (undocumented) diff --git a/docs-devsite/remote-config.fetchresponse.md b/docs-devsite/remote-config.fetchresponse.md index 414188e72bb..1955dd47492 100644 --- a/docs-devsite/remote-config.fetchresponse.md +++ b/docs-devsite/remote-config.fetchresponse.md @@ -27,6 +27,7 @@ export interface FetchResponse | [config](./remote-config.fetchresponse.md#fetchresponseconfig) | [FirebaseRemoteConfigObject](./remote-config.firebaseremoteconfigobject.md#firebaseremoteconfigobject_interface) | Defines the map of parameters returned as "entries" in the fetch response body.
Only defined for 200 responses. | | [eTag](./remote-config.fetchresponse.md#fetchresponseetag) | string | Defines the ETag response header value.
Only defined for 200 and 304 responses. | | [status](./remote-config.fetchresponse.md#fetchresponsestatus) | number | The HTTP status, which is useful for differentiating success responses with data from those without.
The Remote Config client is modeled after the native Fetch
interface, so HTTP status is first-class.
Disambiguation: the fetch response returns a legacy "state" value that is redundant with the HTTP status code. The former is normalized into the latter. | +| [templateVersion](./remote-config.fetchresponse.md#fetchresponsetemplateversion) | number | The version number of the config template fetched from the server. | ## FetchResponse.config @@ -65,3 +66,13 @@ The HTTP status, which is useful for differentiating success responses with data ```typescript status: number; ``` + +## FetchResponse.templateVersion + +The version number of the config template fetched from the server. + +Signature: + +```typescript +templateVersion?: number; +``` diff --git a/docs-devsite/remote-config.md b/docs-devsite/remote-config.md index 1b8232588de..6f4b78b397f 100644 --- a/docs-devsite/remote-config.md +++ b/docs-devsite/remote-config.md @@ -53,6 +53,7 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm | Type Alias | Description | | --- | --- | | [FetchStatus](./remote-config.md#fetchstatus) | Summarizes the outcome of the last attempt to fetch config from the Firebase Remote Config server.
Optional in case no custom signals are set for the instance. */ customSignals?: CustomSignals; + + /** + * The type of fetch to perform, such as a regular fetch or a real-time fetch. + * + *
Optional as not all fetch requests need to be distinguished. + */ + fetchType?: FetchType; + + /** + * The number of fetch attempts made so far for this request. + * + *
Optional as not all fetch requests are part of a retry series.
+ */
+ fetchAttempt?: number;
}
diff --git a/packages/remote-config/src/client/rest_client.ts b/packages/remote-config/src/client/rest_client.ts
index 57f55f53d88..542b2ad7ead 100644
--- a/packages/remote-config/src/client/rest_client.ts
+++ b/packages/remote-config/src/client/rest_client.ts
@@ -82,12 +82,16 @@ export class RestClient implements RemoteConfigFetchClient {
const url = `${urlBase}/v1/projects/${this.projectId}/namespaces/${this.namespace}:fetch?key=${this.apiKey}`;
+ const fetchType = request.fetchType || 'BASE';
+ const fetchAttempt = request.fetchAttempt || 0;
+
const headers = {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip',
// Deviates from pure decorator by not passing max-age header since we don't currently have
// service behavior using that header.
- 'If-None-Match': request.eTag || '*'
+ 'If-None-Match': request.eTag || '*',
+ 'X_FIREBASE_RC_FETCH_TYPE': `${fetchType}/${fetchAttempt}`
};
const requestBody: FetchRequestBody = {
@@ -140,6 +144,7 @@ export class RestClient implements RemoteConfigFetchClient {
let config: FirebaseRemoteConfigObject | undefined;
let state: string | undefined;
+ let templateVersion: number | undefined;
// JSON parsing throws SyntaxError if the response body isn't a JSON string.
// Requesting application/json and checking for a 200 ensures there's JSON data.
@@ -154,6 +159,7 @@ export class RestClient implements RemoteConfigFetchClient {
}
config = responseBody['entries'];
state = responseBody['state'];
+ templateVersion = responseBody['templateVersion'];
}
// Normalizes based on legacy state.
@@ -176,6 +182,6 @@ export class RestClient implements RemoteConfigFetchClient {
});
}
- return { status, eTag: responseEtag, config };
+ return { status, eTag: responseEtag, config, templateVersion };
}
}
diff --git a/packages/remote-config/src/errors.ts b/packages/remote-config/src/errors.ts
index ac7c71b3218..d12bd766c55 100644
--- a/packages/remote-config/src/errors.ts
+++ b/packages/remote-config/src/errors.ts
@@ -34,7 +34,10 @@ export const enum ErrorCode {
FETCH_STATUS = 'fetch-status',
INDEXED_DB_UNAVAILABLE = 'indexed-db-unavailable',
CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS = 'custom-signal-max-allowed-signals',
- CONFIG_UPDATE_STREAM_ERROR = 'stream-error'
+ CONFIG_UPDATE_STREAM_ERROR = 'stream-error',
+ CONFIG_UPDATE_UNAVAILABLE = 'realtime-unavailable',
+ CONFIG_UPDATE_MESSAGE_INVALID = 'update-message-invalid',
+ CONFIG_UPDATE_NOT_FETCHED = 'update-not-fetched'
}
const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
@@ -75,7 +78,11 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
[ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS]:
'Setting more than {$maxSignals} custom signals is not supported.',
[ErrorCode.CONFIG_UPDATE_STREAM_ERROR]:
- 'The stream was not able to connect to the backend.'
+ 'The stream was not able to connect to the backend.',
+ [ErrorCode.CONFIG_UPDATE_UNAVAILABLE]: 'The Realtime service is unavailable.',
+ [ErrorCode.CONFIG_UPDATE_MESSAGE_INVALID]:
+ 'The stream invalidation message was unparsable.',
+ [ErrorCode.CONFIG_UPDATE_NOT_FETCHED]: 'Unable to fetch the latest config.'
};
// Note this is effectively a type system binding a code to params. This approach overlaps with the
@@ -99,6 +106,9 @@ interface ErrorParams {
httpStatus?: number;
originalErrorMessage?: string;
};
+ [ErrorCode.CONFIG_UPDATE_UNAVAILABLE]: { originalErrorMessage: string };
+ [ErrorCode.CONFIG_UPDATE_MESSAGE_INVALID]: { originalErrorMessage: string };
+ [ErrorCode.CONFIG_UPDATE_NOT_FETCHED]: { originalErrorMessage: string };
}
export const ERROR_FACTORY = new ErrorFactory Modeled after the native `Response` interface, but simplified for Remote Config's
* use case.
+ *
+ * @public
*/
export interface FetchResponse {
/**
@@ -90,6 +94,11 @@ export interface FetchResponse {
*/
config?: FirebaseRemoteConfigObject;
+ /**
+ * The version number of the config template fetched from the server.
+ */
+ templateVersion?: number;
+
// Note: we're not extracting experiment metadata until
// ABT and Analytics have Web SDKs.
}
@@ -257,6 +266,18 @@ export interface ConfigUpdateObserver {
*/
export type Unsubscribe = () => void;
+/**
+ * Indicates the type of fetch request.
+ *
+ *
+ *
+ *
+ * @public
+ */
+export type FetchType = 'BASE' | 'REALTIME';
+
declare module '@firebase/component' {
interface NameServiceMapping {
'remote-config': RemoteConfig;
diff --git a/packages/remote-config/src/register.ts b/packages/remote-config/src/register.ts
index df54439b3f5..9c25d01831a 100644
--- a/packages/remote-config/src/register.ts
+++ b/packages/remote-config/src/register.ts
@@ -116,7 +116,9 @@ export function registerRemoteConfig(): void {
projectId,
apiKey,
appId,
- logger
+ logger,
+ storageCache,
+ cachingClient
);
const remoteConfigInstance = new RemoteConfigImpl(
diff --git a/packages/remote-config/src/storage/storage.ts b/packages/remote-config/src/storage/storage.ts
index 8dd767ef101..bd262d29968 100644
--- a/packages/remote-config/src/storage/storage.ts
+++ b/packages/remote-config/src/storage/storage.ts
@@ -192,10 +192,6 @@ export abstract class Storage {
return this.get