diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index e9d4bb0e49..e4675c2009 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -434,6 +434,16 @@ export function remoteConfig(app?: App): remoteConfig.RemoteConfig; // @public (undocumented) export namespace remoteConfig { + // Warning: (ae-forgotten-export) The symbol "ExperimentParameterValue" needs to be exported by the entry point default-namespace.d.ts + export type ExperimentParameterValue = ExperimentParameterValue; + // Warning: (ae-forgotten-export) The symbol "ExperimentValue" needs to be exported by the entry point default-namespace.d.ts + export type ExperimentValue = ExperimentValue; + // Warning: (ae-forgotten-export) The symbol "ExperimentVariantExplicitValue" needs to be exported by the entry point default-namespace.d.ts + export type ExperimentVariantExplicitValue = ExperimentVariantExplicitValue; + // Warning: (ae-forgotten-export) The symbol "ExperimentVariantNoChange" needs to be exported by the entry point default-namespace.d.ts + export type ExperimentVariantNoChange = ExperimentVariantNoChange; + // Warning: (ae-forgotten-export) The symbol "ExperimentVariantValue" needs to be exported by the entry point default-namespace.d.ts + export type ExperimentVariantValue = ExperimentVariantValue; // Warning: (ae-forgotten-export) The symbol "ExplicitParameterValue" needs to be exported by the entry point default-namespace.d.ts export type ExplicitParameterValue = ExplicitParameterValue; // Warning: (ae-forgotten-export) The symbol "InAppDefaultValue" needs to be exported by the entry point default-namespace.d.ts @@ -444,6 +454,10 @@ export namespace remoteConfig { export type ListVersionsResult = ListVersionsResult; // Warning: (ae-forgotten-export) The symbol "ParameterValueType" needs to be exported by the entry point default-namespace.d.ts export type ParameterValueType = ParameterValueType; + // Warning: (ae-forgotten-export) The symbol "PersonalizationParameterValue" needs to be exported by the entry point default-namespace.d.ts + export type PersonalizationParameterValue = PersonalizationParameterValue; + // Warning: (ae-forgotten-export) The symbol "PersonalizationValue" needs to be exported by the entry point default-namespace.d.ts + export type PersonalizationValue = PersonalizationValue; // Warning: (ae-forgotten-export) The symbol "RemoteConfig" needs to be exported by the entry point default-namespace.d.ts export type RemoteConfig = RemoteConfig; // Warning: (ae-forgotten-export) The symbol "RemoteConfigCondition" needs to be exported by the entry point default-namespace.d.ts @@ -458,6 +472,10 @@ export namespace remoteConfig { export type RemoteConfigTemplate = RemoteConfigTemplate; // Warning: (ae-forgotten-export) The symbol "RemoteConfigUser" needs to be exported by the entry point default-namespace.d.ts export type RemoteConfigUser = RemoteConfigUser; + // Warning: (ae-forgotten-export) The symbol "RolloutParameterValue" needs to be exported by the entry point default-namespace.d.ts + export type RolloutParameterValue = RolloutParameterValue; + // Warning: (ae-forgotten-export) The symbol "RolloutValue" needs to be exported by the entry point default-namespace.d.ts + export type RolloutValue = RolloutValue; // Warning: (ae-forgotten-export) The symbol "TagColor" needs to be exported by the entry point default-namespace.d.ts export type TagColor = TagColor; // Warning: (ae-forgotten-export) The symbol "Version" needs to be exported by the entry point default-namespace.d.ts diff --git a/etc/firebase-admin.remote-config.api.md b/etc/firebase-admin.remote-config.api.md index cf7eb32b93..4bbc0671ee 100644 --- a/etc/firebase-admin.remote-config.api.md +++ b/etc/firebase-admin.remote-config.api.md @@ -47,6 +47,14 @@ export type DefaultConfig = { // @public export type EvaluationContext = UserProvidedSignals & PredefinedSignals; +// @public (undocumented) +export interface ExperimentParameterValue { + // Warning: (ae-forgotten-export) The symbol "ExperimentValue" needs to be exported by the entry point index.d.ts + // + // (undocumented) + experimentValue: ExperimentValue; +} + // @public export interface ExplicitParameterValue { value: string; @@ -142,6 +150,14 @@ export enum PercentConditionOperator { UNKNOWN = "UNKNOWN" } +// @public (undocumented) +export interface PersonalizationParameterValue { + // Warning: (ae-forgotten-export) The symbol "PersonalizationValue" needs to be exported by the entry point index.d.ts + // + // (undocumented) + personalizationValue: PersonalizationValue; +} + // @public export type PredefinedSignals = { randomizationId?: string; @@ -197,7 +213,7 @@ export interface RemoteConfigParameterGroup { } // @public -export type RemoteConfigParameterValue = ExplicitParameterValue | InAppDefaultValue; +export type RemoteConfigParameterValue = ExplicitParameterValue | InAppDefaultValue | RolloutParameterValue | PersonalizationParameterValue | ExperimentParameterValue; // @public export interface RemoteConfigTemplate { @@ -219,6 +235,14 @@ export interface RemoteConfigUser { name?: string; } +// @public (undocumented) +export interface RolloutParameterValue { + // Warning: (ae-forgotten-export) The symbol "RolloutValue" needs to be exported by the entry point index.d.ts + // + // (undocumented) + rolloutValue: RolloutValue; +} + // @public export interface ServerConfig { getAll(): { diff --git a/src/remote-config/index.ts b/src/remote-config/index.ts index 790568735e..f6121c60ac 100644 --- a/src/remote-config/index.ts +++ b/src/remote-config/index.ts @@ -47,6 +47,9 @@ export { PredefinedSignals, RemoteConfigCondition, RemoteConfigParameter, + ExperimentParameterValue, + PersonalizationParameterValue, + RolloutParameterValue, RemoteConfigParameterGroup, RemoteConfigParameterValue, RemoteConfigTemplate, diff --git a/src/remote-config/remote-config-api.ts b/src/remote-config/remote-config-api.ts index ed7da05496..e34a7d8317 100644 --- a/src/remote-config/remote-config-api.ts +++ b/src/remote-config/remote-config-api.ts @@ -354,12 +354,73 @@ export interface InAppDefaultValue { useInAppDefault: boolean; } +/** + * Represents a Rollout value. + */ +export interface RolloutValue { + rolloutId: string; + value: string; + percent: number; // Numeric value between 1-100 +} + +/** + * Represents a Personalization value. + */ +export interface PersonalizationValue { + personalizationId: string; +} + +/** + * Represents a specific variant value within an Experiment. + */ +export interface ExperimentVariantExplicitValue { + variantId: string; + value: string; + noChange?: never; +} + +/** + * Represents a no-change variant value within an Experiment. + */ +export interface ExperimentVariantNoChange { + variantId: string; + value?: never; + noChange: true; +} + +export type ExperimentVariantValue = ExperimentVariantExplicitValue | ExperimentVariantNoChange; + +/** + * Represents an Experiment value. + */ +export interface ExperimentValue { + experimentId: string; + variantValue: ExperimentVariantValue[]; +} + +export interface RolloutParameterValue { + rolloutValue: RolloutValue; +} + +export interface PersonalizationParameterValue { + personalizationValue: PersonalizationValue; +} + +export interface ExperimentParameterValue { + experimentValue: ExperimentValue; +} + /** * Type representing a Remote Config parameter value. * A `RemoteConfigParameterValue` could be either an `ExplicitParameterValue` or * an `InAppDefaultValue`. */ -export type RemoteConfigParameterValue = ExplicitParameterValue | InAppDefaultValue; +export type RemoteConfigParameterValue = + | ExplicitParameterValue + | InAppDefaultValue + | RolloutParameterValue + | PersonalizationParameterValue + | ExperimentParameterValue; /** * Interface representing a Remote Config parameter. diff --git a/src/remote-config/remote-config-namespace.ts b/src/remote-config/remote-config-namespace.ts index 159204d316..abc315b150 100644 --- a/src/remote-config/remote-config-namespace.ts +++ b/src/remote-config/remote-config-namespace.ts @@ -18,6 +18,15 @@ import { App } from '../app'; import { ExplicitParameterValue as TExplicitParameterValue, InAppDefaultValue as TInAppDefaultValue, + RolloutValue as TRolloutValue, + PersonalizationValue as TPersonalizationValue, + ExperimentVariantExplicitValue as TExperimentVariantExplicitValue, + ExperimentVariantNoChange as TExperimentVariantNoChange, + ExperimentVariantValue as TExperimentVariantValue, + ExperimentValue as TExperimentValue, + RolloutParameterValue as TRolloutParameterValue, + PersonalizationParameterValue as TPersonalizationParameterValue, + ExperimentParameterValue as TExperimentParameterValue, ListVersionsOptions as TListVersionsOptions, ListVersionsResult as TListVersionsResult, ParameterValueType as TParameterValueType, @@ -73,6 +82,51 @@ export namespace remoteConfig { */ export type InAppDefaultValue = TInAppDefaultValue; + /** + * Type alias to {@link firebase-admin.remote-config#RolloutValue}. + */ + export type RolloutValue = TRolloutValue; + + /** + * Type alias to {@link firebase-admin.remote-config#PersonalizationValue}. + */ + export type PersonalizationValue = TPersonalizationValue; + + /** + * Type alias to {@link firebase-admin.remote-config#ExperimentVariantExplicitValue}. + */ + export type ExperimentVariantExplicitValue = TExperimentVariantExplicitValue; + + /** + * Type alias to {@link firebase-admin.remote-config#ExperimentVariantNoChange}. + */ + export type ExperimentVariantNoChange = TExperimentVariantNoChange; + + /** + * Type alias to {@link firebase-admin.remote-config#ExperimentVariantValue}. + */ + export type ExperimentVariantValue = TExperimentVariantValue; + + /** + * Type alias to {@link firebase-admin.remote-config#ExperimentValue}. + */ + export type ExperimentValue = TExperimentValue; + + /** + * Type alias to {@link firebase-admin.remote-config#RolloutParameterValue}. + */ + export type RolloutParameterValue = TRolloutParameterValue; + + /** + * Type alias to {@link firebase-admin.remote-config#PersonalizationParameterValue}. + */ + export type PersonalizationParameterValue = TPersonalizationParameterValue; + + /** + * Type alias to {@link firebase-admin.remote-config#ExperimentParameterValue}. + */ + export type ExperimentParameterValue = TExperimentParameterValue; + /** * Type alias to {@link firebase-admin.remote-config#ListVersionsOptions}. */ diff --git a/src/remote-config/remote-config.ts b/src/remote-config/remote-config.ts index 91eebfa0cb..6ae44c4064 100644 --- a/src/remote-config/remote-config.ts +++ b/src/remote-config/remote-config.ts @@ -382,6 +382,7 @@ class ServerTemplateImpl implements ServerTemplate { // Iterates in order over condition list. If there is a value associated // with a condition, this checks if the condition is true. for (const [conditionName, conditionEvaluation] of evaluatedConditions) { + if (normalizedConditionalValues[conditionName] && conditionEvaluation) { parameterValueWrapper = normalizedConditionalValues[conditionName]; break; @@ -395,7 +396,9 @@ class ServerTemplateImpl implements ServerTemplate { if (parameterValueWrapper) { const parameterValue = (parameterValueWrapper as ExplicitParameterValue).value; - configValues[key] = new ValueImpl('remote', parameterValue); + if (parameterValue !== undefined) { + configValues[key] = new ValueImpl('remote', parameterValue); + } continue; } diff --git a/test/unit/remote-config/remote-config.spec.ts b/test/unit/remote-config/remote-config.spec.ts index e7e4f84e57..6d501aeb4b 100644 --- a/test/unit/remote-config/remote-config.spec.ts +++ b/test/unit/remote-config/remote-config.spec.ts @@ -27,6 +27,9 @@ import { TagColor, ListVersionsResult, RemoteConfigFetchResponse, + RolloutParameterValue, + PersonalizationParameterValue, + ExperimentParameterValue, } from '../../../src/remote-config/index'; import { FirebaseApp } from '../../../src/app/firebase-app'; import * as mocks from '../../resources/mocks'; @@ -96,6 +99,48 @@ describe('RemoteConfig', () => { description: 'this is a promo', valueType: 'BOOLEAN', }, + new_ui_enabled: { + defaultValue: { value: 'false' }, + conditionalValues: { + ios: { + rolloutValue: { + rolloutId: 'rollout_1', + value: 'true', + percent: 50, + } + } + }, + description: 'New UI Rollout', + valueType: 'BOOLEAN', + }, + personalized_welcome_message: { + defaultValue: { value: 'Welcome!' }, + conditionalValues: { + ios: { + personalizationValue: { + personalizationId: 'personalization_1', + } + } + }, + description: 'Personalized Welcome Message', + valueType: 'STRING', + }, + experiment_enabled: { + defaultValue: { value: 'false' }, + conditionalValues: { + ios: { + experimentValue: { + experimentId: 'experiment_1', + variantValue: [ + { variantId: 'variant_A', value: 'true' }, + { variantId: 'variant_B', noChange: true } + ] + } + } + }, + description: 'Experiment Enabled', + valueType: 'BOOLEAN', + } }, parameterGroups: PARAMETER_GROUPS, etag: 'etag-123456789012-5', @@ -153,6 +198,48 @@ describe('RemoteConfig', () => { description: 'this is a promo', valueType: 'BOOLEAN', }, + new_ui_enabled: { + defaultValue: { value: 'false' }, + conditionalValues: { + ios: { + rolloutValue: { + rolloutId: 'rollout_1', + value: 'true', + percent: 50, + } + } + }, + description: 'New UI Rollout', + valueType: 'BOOLEAN', + }, + personalized_welcome_message: { + defaultValue: { value: 'Welcome!' }, + conditionalValues: { + ios: { + personalizationValue: { + personalizationId: 'personalization_1', + } + } + }, + description: 'Personalized Welcome Message', + valueType: 'STRING', + }, + experiment_enabled: { + defaultValue: { value: 'false' }, + conditionalValues: { + ios: { + experimentValue: { + experimentId: 'experiment_1', + variantValue: [ + { variantId: 'variant_A', value: 'true' }, + { variantId: 'variant_B', noChange: true } + ] + } + } + }, + description: 'Experiment Enabled', + valueType: 'BOOLEAN', + } }, parameterGroups: PARAMETER_GROUPS, etag: 'etag-123456789012-6', @@ -542,6 +629,32 @@ describe('RemoteConfig', () => { expect(p1.description).equals('this is a promo'); expect(p1.valueType).equals('BOOLEAN'); + const p2 = newTemplate.parameters['new_ui_enabled']; + expect(p2.defaultValue).deep.equals({ value: 'false' }); + const rolloutParam = p2.conditionalValues['ios'] as RolloutParameterValue; + expect(rolloutParam.rolloutValue.rolloutId).to.equal('rollout_1'); + expect(rolloutParam.rolloutValue.value).to.equal('true'); + expect(rolloutParam.rolloutValue.percent).to.equal(50); + expect(p2.description).equals('New UI Rollout'); + expect(p2.valueType).equals('BOOLEAN'); + + const p3 = newTemplate.parameters['personalized_welcome_message']; + expect(p3.defaultValue).deep.equals({ value: 'Welcome!' }); + const personalizationParam = p3.conditionalValues['ios'] as PersonalizationParameterValue; + expect(personalizationParam.personalizationValue.personalizationId).to.equal('personalization_1'); + expect(p3.description).equals('Personalized Welcome Message'); + expect(p3.valueType).equals('STRING'); + + const p4 = newTemplate.parameters['experiment_enabled']; + expect(p4.defaultValue).deep.equals({ value: 'false' }); + const experimentParam = p4.conditionalValues['ios'] as ExperimentParameterValue; + expect(experimentParam.experimentValue.experimentId).to.equal('experiment_1'); + expect(experimentParam.experimentValue.variantValue.length).to.equal(2); + expect(experimentParam.experimentValue.variantValue[0]).to.deep.equal({ variantId: 'variant_A', value: 'true' }); + expect(experimentParam.experimentValue.variantValue[1]).to.deep.equal({ variantId: 'variant_B', noChange: true }); + expect(p4.description).equals('Experiment Enabled'); + expect(p4.valueType).equals('BOOLEAN'); + expect(newTemplate.parameterGroups).deep.equals(PARAMETER_GROUPS); const c = newTemplate.conditions.find((c) => c.name === 'ios');