123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /*! firebase-admin v12.1.1 */
- "use strict";
- /*!
- * Copyright 2020 Google Inc.
- *
- * 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.
- */
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.RemoteConfig = void 0;
- const validator = require("../utils/validator");
- const remote_config_api_client_internal_1 = require("./remote-config-api-client-internal");
- const condition_evaluator_internal_1 = require("./condition-evaluator-internal");
- const value_impl_1 = require("./internal/value-impl");
- /**
- * The Firebase `RemoteConfig` service interface.
- */
- class RemoteConfig {
- /**
- * @param app - The app for this RemoteConfig service.
- * @constructor
- * @internal
- */
- constructor(app) {
- this.app = app;
- this.client = new remote_config_api_client_internal_1.RemoteConfigApiClient(app);
- }
- /**
- * Gets the current active version of the {@link RemoteConfigTemplate} of the project.
- *
- * @returns A promise that fulfills with a `RemoteConfigTemplate`.
- */
- getTemplate() {
- return this.client.getTemplate()
- .then((templateResponse) => {
- return new RemoteConfigTemplateImpl(templateResponse);
- });
- }
- /**
- * Gets the requested version of the {@link RemoteConfigTemplate} of the project.
- *
- * @param versionNumber - Version number of the Remote Config template to look up.
- *
- * @returns A promise that fulfills with a `RemoteConfigTemplate`.
- */
- getTemplateAtVersion(versionNumber) {
- return this.client.getTemplateAtVersion(versionNumber)
- .then((templateResponse) => {
- return new RemoteConfigTemplateImpl(templateResponse);
- });
- }
- /**
- * Validates a {@link RemoteConfigTemplate}.
- *
- * @param template - The Remote Config template to be validated.
- * @returns A promise that fulfills with the validated `RemoteConfigTemplate`.
- */
- validateTemplate(template) {
- return this.client.validateTemplate(template)
- .then((templateResponse) => {
- return new RemoteConfigTemplateImpl(templateResponse);
- });
- }
- /**
- * Publishes a Remote Config template.
- *
- * @param template - The Remote Config template to be published.
- * @param options - Optional options object when publishing a Remote Config template:
- * - `force`: Setting this to `true` forces the Remote Config template to
- * be updated and circumvent the ETag. This approach is not recommended
- * because it risks causing the loss of updates to your Remote Config
- * template if multiple clients are updating the Remote Config template.
- * See {@link https://firebase.google.com/docs/remote-config/use-config-rest#etag_usage_and_forced_updates |
- * ETag usage and forced updates}.
- *
- * @returns A Promise that fulfills with the published `RemoteConfigTemplate`.
- */
- publishTemplate(template, options) {
- return this.client.publishTemplate(template, options)
- .then((templateResponse) => {
- return new RemoteConfigTemplateImpl(templateResponse);
- });
- }
- /**
- * Rolls back a project's published Remote Config template to the specified version.
- * A rollback is equivalent to getting a previously published Remote Config
- * template and re-publishing it using a force update.
- *
- * @param versionNumber - The version number of the Remote Config template to roll back to.
- * The specified version number must be lower than the current version number, and not have
- * been deleted due to staleness. Only the last 300 versions are stored.
- * All versions that correspond to non-active Remote Config templates (that is, all except the
- * template that is being fetched by clients) are also deleted if they are more than 90 days old.
- * @returns A promise that fulfills with the published `RemoteConfigTemplate`.
- */
- rollback(versionNumber) {
- return this.client.rollback(versionNumber)
- .then((templateResponse) => {
- return new RemoteConfigTemplateImpl(templateResponse);
- });
- }
- /**
- * Gets a list of Remote Config template versions that have been published, sorted in reverse
- * chronological order. Only the last 300 versions are stored.
- * All versions that correspond to non-active Remote Config templates (i.e., all except the
- * template that is being fetched by clients) are also deleted if they are older than 90 days.
- *
- * @param options - Optional options object for getting a list of versions.
- * @returns A promise that fulfills with a `ListVersionsResult`.
- */
- listVersions(options) {
- return this.client.listVersions(options)
- .then((listVersionsResponse) => {
- return {
- versions: listVersionsResponse.versions?.map(version => new VersionImpl(version)) ?? [],
- nextPageToken: listVersionsResponse.nextPageToken,
- };
- });
- }
- /**
- * Creates and returns a new Remote Config template from a JSON string.
- *
- * @param json - The JSON string to populate a Remote Config template.
- *
- * @returns A new template instance.
- */
- createTemplateFromJSON(json) {
- if (!validator.isNonEmptyString(json)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'JSON string must be a valid non-empty string');
- }
- let template;
- try {
- template = JSON.parse(json);
- }
- catch (e) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', `Failed to parse the JSON string: ${json}. ` + e);
- }
- return new RemoteConfigTemplateImpl(template);
- }
- /**
- * Instantiates {@link ServerTemplate} and then fetches and caches the latest
- * template version of the project.
- */
- async getServerTemplate(options) {
- const template = this.initServerTemplate(options);
- await template.load();
- return template;
- }
- /**
- * Synchronously instantiates {@link ServerTemplate}.
- */
- initServerTemplate(options) {
- const template = new ServerTemplateImpl(this.client, new condition_evaluator_internal_1.ConditionEvaluator(), options?.defaultConfig);
- if (options?.template) {
- template.set(options?.template);
- }
- return template;
- }
- }
- exports.RemoteConfig = RemoteConfig;
- /**
- * Remote Config template internal implementation.
- */
- class RemoteConfigTemplateImpl {
- constructor(config) {
- if (!validator.isNonNullObject(config) ||
- !validator.isNonEmptyString(config.etag)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', `Invalid Remote Config template: ${JSON.stringify(config)}`);
- }
- this.etagInternal = config.etag;
- if (typeof config.parameters !== 'undefined') {
- if (!validator.isNonNullObject(config.parameters)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Remote Config parameters must be a non-null object');
- }
- this.parameters = config.parameters;
- }
- else {
- this.parameters = {};
- }
- if (typeof config.parameterGroups !== 'undefined') {
- if (!validator.isNonNullObject(config.parameterGroups)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Remote Config parameter groups must be a non-null object');
- }
- this.parameterGroups = config.parameterGroups;
- }
- else {
- this.parameterGroups = {};
- }
- if (typeof config.conditions !== 'undefined') {
- if (!validator.isArray(config.conditions)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Remote Config conditions must be an array');
- }
- this.conditions = config.conditions;
- }
- else {
- this.conditions = [];
- }
- if (typeof config.version !== 'undefined') {
- this.version = new VersionImpl(config.version);
- }
- }
- /**
- * Gets the ETag of the template.
- *
- * @returns The ETag of the Remote Config template.
- */
- get etag() {
- return this.etagInternal;
- }
- /**
- * Returns a JSON-serializable representation of this object.
- *
- * @returns A JSON-serializable representation of this object.
- */
- toJSON() {
- return {
- conditions: this.conditions,
- parameters: this.parameters,
- parameterGroups: this.parameterGroups,
- etag: this.etag,
- version: this.version,
- };
- }
- }
- /**
- * Remote Config dataplane template data implementation.
- */
- class ServerTemplateImpl {
- constructor(apiClient, conditionEvaluator, defaultConfig = {}) {
- this.apiClient = apiClient;
- this.conditionEvaluator = conditionEvaluator;
- this.defaultConfig = defaultConfig;
- this.stringifiedDefaultConfig = {};
- // RC stores all remote values as string, but it's more intuitive
- // to declare default values with specific types, so this converts
- // the external declaration to an internal string representation.
- for (const key in defaultConfig) {
- this.stringifiedDefaultConfig[key] = String(defaultConfig[key]);
- }
- }
- /**
- * Fetches and caches the current active version of the project's {@link ServerTemplate}.
- */
- load() {
- return this.apiClient.getServerTemplate()
- .then((template) => {
- this.cache = new ServerTemplateDataImpl(template);
- });
- }
- /**
- * Parses a {@link ServerTemplateDataType} and caches it.
- */
- set(template) {
- let parsed;
- if (validator.isString(template)) {
- try {
- parsed = JSON.parse(template);
- }
- catch (e) {
- // Transforms JSON parse errors to Firebase error.
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', `Failed to parse the JSON string: ${template}. ` + e);
- }
- }
- else {
- parsed = template;
- }
- // Throws template parse errors.
- this.cache = new ServerTemplateDataImpl(parsed);
- }
- /**
- * Evaluates the current template in cache to produce a {@link ServerConfig}.
- */
- evaluate(context = {}) {
- if (!this.cache) {
- // This is the only place we should throw during evaluation, since it's under the
- // control of application logic. To preserve forward-compatibility, we should only
- // return false in cases where the SDK is unsure how to evaluate the fetched template.
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('failed-precondition', 'No Remote Config Server template in cache. Call load() before calling evaluate().');
- }
- const evaluatedConditions = this.conditionEvaluator.evaluateConditions(this.cache.conditions, context);
- const configValues = {};
- // Initializes config Value objects with default values.
- for (const key in this.stringifiedDefaultConfig) {
- configValues[key] = new value_impl_1.ValueImpl('default', this.stringifiedDefaultConfig[key]);
- }
- // Overlays config Value objects derived by evaluating the template.
- for (const [key, parameter] of Object.entries(this.cache.parameters)) {
- const { conditionalValues, defaultValue } = parameter;
- // Supports parameters with no conditional values.
- const normalizedConditionalValues = conditionalValues || {};
- let parameterValueWrapper = undefined;
- // 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;
- }
- }
- if (parameterValueWrapper && parameterValueWrapper.useInAppDefault) {
- // TODO: add logging once we have a wrapped logger.
- continue;
- }
- if (parameterValueWrapper) {
- const parameterValue = parameterValueWrapper.value;
- configValues[key] = new value_impl_1.ValueImpl('remote', parameterValue);
- continue;
- }
- if (!defaultValue) {
- // TODO: add logging once we have a wrapped logger.
- continue;
- }
- if (defaultValue.useInAppDefault) {
- // TODO: add logging once we have a wrapped logger.
- continue;
- }
- const parameterDefaultValue = defaultValue.value;
- configValues[key] = new value_impl_1.ValueImpl('remote', parameterDefaultValue);
- }
- return new ServerConfigImpl(configValues);
- }
- /**
- * @returns JSON representation of the server template
- */
- toJSON() {
- return this.cache;
- }
- }
- class ServerConfigImpl {
- constructor(configValues) {
- this.configValues = configValues;
- }
- getBoolean(key) {
- return this.getValue(key).asBoolean();
- }
- getNumber(key) {
- return this.getValue(key).asNumber();
- }
- getString(key) {
- return this.getValue(key).asString();
- }
- getValue(key) {
- return this.configValues[key] || new value_impl_1.ValueImpl('static');
- }
- }
- /**
- * Remote Config dataplane template data implementation.
- */
- class ServerTemplateDataImpl {
- constructor(template) {
- if (!validator.isNonNullObject(template) ||
- !validator.isNonEmptyString(template.etag)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', `Invalid Remote Config template: ${JSON.stringify(template)}`);
- }
- this.etag = template.etag;
- if (typeof template.parameters !== 'undefined') {
- if (!validator.isNonNullObject(template.parameters)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Remote Config parameters must be a non-null object');
- }
- this.parameters = template.parameters;
- }
- else {
- this.parameters = {};
- }
- if (typeof template.conditions !== 'undefined') {
- if (!validator.isArray(template.conditions)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Remote Config conditions must be an array');
- }
- this.conditions = template.conditions;
- }
- else {
- this.conditions = [];
- }
- if (typeof template.version !== 'undefined') {
- this.version = new VersionImpl(template.version);
- }
- }
- }
- /**
- * Remote Config Version internal implementation.
- */
- class VersionImpl {
- constructor(version) {
- if (!validator.isNonNullObject(version)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', `Invalid Remote Config version instance: ${JSON.stringify(version)}`);
- }
- if (typeof version.versionNumber !== 'undefined') {
- if (!validator.isNonEmptyString(version.versionNumber) &&
- !validator.isNumber(version.versionNumber)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version number must be a non-empty string in int64 format or a number');
- }
- if (!Number.isInteger(Number(version.versionNumber))) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version number must be an integer or a string in int64 format');
- }
- this.versionNumber = version.versionNumber;
- }
- if (typeof version.updateOrigin !== 'undefined') {
- if (!validator.isNonEmptyString(version.updateOrigin)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version update origin must be a non-empty string');
- }
- this.updateOrigin = version.updateOrigin;
- }
- if (typeof version.updateType !== 'undefined') {
- if (!validator.isNonEmptyString(version.updateType)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version update type must be a non-empty string');
- }
- this.updateType = version.updateType;
- }
- if (typeof version.updateUser !== 'undefined') {
- if (!validator.isNonNullObject(version.updateUser)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version update user must be a non-null object');
- }
- this.updateUser = version.updateUser;
- }
- if (typeof version.description !== 'undefined') {
- if (!validator.isNonEmptyString(version.description)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version description must be a non-empty string');
- }
- this.description = version.description;
- }
- if (typeof version.rollbackSource !== 'undefined') {
- if (!validator.isNonEmptyString(version.rollbackSource)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version rollback source must be a non-empty string');
- }
- this.rollbackSource = version.rollbackSource;
- }
- if (typeof version.isLegacy !== 'undefined') {
- if (!validator.isBoolean(version.isLegacy)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version.isLegacy must be a boolean');
- }
- this.isLegacy = version.isLegacy;
- }
- // The backend API provides timestamps in ISO date strings. The Admin SDK exposes timestamps
- // in UTC date strings. If a developer uses a previously obtained template with UTC timestamps
- // we could still validate it below.
- if (typeof version.updateTime !== 'undefined') {
- if (!this.isValidTimestamp(version.updateTime)) {
- throw new remote_config_api_client_internal_1.FirebaseRemoteConfigError('invalid-argument', 'Version update time must be a valid date string');
- }
- this.updateTime = new Date(version.updateTime).toUTCString();
- }
- }
- /**
- * @returns A JSON-serializable representation of this object.
- */
- toJSON() {
- return {
- versionNumber: this.versionNumber,
- updateOrigin: this.updateOrigin,
- updateType: this.updateType,
- updateUser: this.updateUser,
- description: this.description,
- rollbackSource: this.rollbackSource,
- isLegacy: this.isLegacy,
- updateTime: this.updateTime,
- };
- }
- isValidTimestamp(timestamp) {
- // This validation fails for timestamps earlier than January 1, 1970 and considers strings
- // such as "1.2" as valid timestamps.
- return validator.isNonEmptyString(timestamp) && (new Date(timestamp)).getTime() > 0;
- }
- }
|