123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /*! firebase-admin v12.1.1 */
- /*!
- * Copyright 2024 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.
- */
- 'use strict';
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.ConditionEvaluator = void 0;
- const remote_config_api_1 = require("./remote-config-api");
- const farmhash = require("farmhash");
- const long = require("long");
- /**
- * Encapsulates condition evaluation logic to simplify organization and
- * facilitate testing.
- *
- * @internal
- */
- class ConditionEvaluator {
- evaluateConditions(namedConditions, context) {
- // The order of the conditions is significant.
- // A JS Map preserves the order of insertion ("Iteration happens in insertion order"
- // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description).
- const evaluatedConditions = new Map();
- for (const namedCondition of namedConditions) {
- evaluatedConditions.set(namedCondition.name, this.evaluateCondition(namedCondition.condition, context));
- }
- return evaluatedConditions;
- }
- evaluateCondition(condition, context, nestingLevel = 0) {
- if (nestingLevel >= ConditionEvaluator.MAX_CONDITION_RECURSION_DEPTH) {
- // TODO: add logging once we have a wrapped logger.
- return false;
- }
- if (condition.orCondition) {
- return this.evaluateOrCondition(condition.orCondition, context, nestingLevel + 1);
- }
- if (condition.andCondition) {
- return this.evaluateAndCondition(condition.andCondition, context, nestingLevel + 1);
- }
- if (condition.true) {
- return true;
- }
- if (condition.false) {
- return false;
- }
- if (condition.percent) {
- return this.evaluatePercentCondition(condition.percent, context);
- }
- // TODO: add logging once we have a wrapped logger.
- return false;
- }
- evaluateOrCondition(orCondition, context, nestingLevel) {
- const subConditions = orCondition.conditions || [];
- for (const subCondition of subConditions) {
- // Recursive call.
- const result = this.evaluateCondition(subCondition, context, nestingLevel + 1);
- // Short-circuit the evaluation result for true.
- if (result) {
- return result;
- }
- }
- return false;
- }
- evaluateAndCondition(andCondition, context, nestingLevel) {
- const subConditions = andCondition.conditions || [];
- for (const subCondition of subConditions) {
- // Recursive call.
- const result = this.evaluateCondition(subCondition, context, nestingLevel + 1);
- // Short-circuit the evaluation result for false.
- if (!result) {
- return result;
- }
- }
- return true;
- }
- evaluatePercentCondition(percentCondition, context) {
- if (!context.randomizationId) {
- // TODO: add logging once we have a wrapped logger.
- return false;
- }
- // This is the entry point for processing percent condition data from the response.
- // We're not using a proto library, so we can't assume undefined fields have
- // default values.
- const { seed, percentOperator, microPercent, microPercentRange } = percentCondition;
- if (!percentOperator) {
- // TODO: add logging once we have a wrapped logger.
- return false;
- }
- const normalizedMicroPercent = microPercent || 0;
- const normalizedMicroPercentUpperBound = microPercentRange?.microPercentUpperBound || 0;
- const normalizedMicroPercentLowerBound = microPercentRange?.microPercentLowerBound || 0;
- const seedPrefix = seed && seed.length > 0 ? `${seed}.` : '';
- const stringToHash = `${seedPrefix}${context.randomizationId}`;
- // Using a 64-bit long for consistency with the Remote Config fetch endpoint.
- let hash64 = long.fromString(farmhash.fingerprint64(stringToHash));
- // Negate the hash if its value is less than 0. We handle this manually because the
- // Long library doesn't provided an absolute value method.
- if (hash64.lt(0)) {
- hash64 = hash64.negate();
- }
- const instanceMicroPercentile = hash64.mod(100 * 1000000);
- switch (percentOperator) {
- case remote_config_api_1.PercentConditionOperator.LESS_OR_EQUAL:
- return instanceMicroPercentile.lte(normalizedMicroPercent);
- case remote_config_api_1.PercentConditionOperator.GREATER_THAN:
- return instanceMicroPercentile.gt(normalizedMicroPercent);
- case remote_config_api_1.PercentConditionOperator.BETWEEN:
- return instanceMicroPercentile.gt(normalizedMicroPercentLowerBound)
- && instanceMicroPercentile.lte(normalizedMicroPercentUpperBound);
- case remote_config_api_1.PercentConditionOperator.UNKNOWN:
- default:
- break;
- }
- // TODO: add logging once we have a wrapped logger.
- return false;
- }
- }
- exports.ConditionEvaluator = ConditionEvaluator;
- ConditionEvaluator.MAX_CONDITION_RECURSION_DEPTH = 10;
|