ibm-appconfiguration-js-client-sdk
Version:
IBM Cloud App Configuration JavaScript Client SDK
177 lines (176 loc) • 7.03 kB
JavaScript
;
/**
* Copyright 2022 IBM Corp. All Rights Reserved.
*
* 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.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentRolloutPercentage = exports.parseRolloutConfigurationPhases = exports.PhaseScheduler = void 0;
var sorted_btree_1 = __importDefault(require("sorted-btree"));
var logger_1 = require("./logger");
var Constants = __importStar(require("./constants"));
var logger = new logger_1.Logger(Constants.APP_CONFIGURATION);
var PhaseScheduler = /** @class */ (function () {
function PhaseScheduler() {
// empty
}
// Add timestamp in sorted order (no duplicates)
PhaseScheduler.addTimestamp = function (timestamp) {
var insertIndex = 0;
for (var i = 0; i < this.timestamps.length; i += 1) {
if (timestamp === this.timestamps[i]) {
return;
}
if (this.timestamps[i] > timestamp) {
break;
}
insertIndex = i + 1;
}
// Insert at correct position
this.timestamps.splice(insertIndex, 0, timestamp);
// If this is first timestamp or earlier than current scheduled, reschedule
if (insertIndex === 0) {
this.scheduleNext();
}
};
// Schedule timer for first (earliest) timestamp
PhaseScheduler.scheduleNext = function () {
var _this = this;
// Clear existing timer
if (this.timerId !== null) {
clearTimeout(this.timerId);
this.timerId = null;
}
var currentTime = Date.now();
// Remove all past timestamps first
while (this.timestamps.length > 0 && this.timestamps[0] <= currentTime) {
this.timestamps.shift();
}
// call once to cover all the previous scheduled phases
this.onPhaseCallback();
// Check if there are any future timestamps
if (this.timestamps.length === 0) {
return; // No more timestamps to schedule
}
// Schedule timer for the first (earliest) future timestamp
var nextTimestamp = this.timestamps[0];
var delay = nextTimestamp - currentTime;
this.timerId = setTimeout(function () {
// Remove the executed timestamp
_this.timestamps.shift();
// Execute callback
_this.onPhaseCallback();
// Schedule next timestamp
_this.scheduleNext();
}, delay);
};
// Reset: clear timer and flush array
PhaseScheduler.reset = function () {
if (this.timerId !== null) {
clearTimeout(this.timerId);
this.timerId = null;
}
this.timestamps = [];
};
PhaseScheduler.timestamps = []; // Sorted array of timestamps
PhaseScheduler.timerId = null;
return PhaseScheduler;
}());
exports.PhaseScheduler = PhaseScheduler;
/**
* Parses progressive rollout phases into a B-Tree for efficient timestamp-to-percentage lookups
* @param configuration - The rollout configuration object
* @returns BTree mapping timestamp (ms) to percentage, or null on error
*/
function parseRolloutConfigurationPhases(configuration) {
if (!configuration || !configuration.start_at || !configuration.phases || configuration.phases.length === 0) {
return null;
}
try {
// Parse start timestamp
var startTime = new Date(configuration.start_at);
if (Number.isNaN(startTime.getTime())) {
logger.log("Invalid start_at: ".concat(configuration.start_at));
return null;
}
var result_1 = new sorted_btree_1.default();
result_1.set(0, 0);
var transitionTime_1 = startTime.getTime();
// Duration multipliers in milliseconds
var multipliers_1 = {
days: 86400000,
hours: 3600000,
minutes: 60000, // minutes
};
configuration.phases.forEach(function (phase) {
// Add phase entry
result_1.set(transitionTime_1, phase.percentage);
PhaseScheduler.addTimestamp(transitionTime_1);
// Calculate next transition time if duration is specified
if (phase.duration && phase.duration_type) {
transitionTime_1 += multipliers_1[phase.duration_type] * phase.duration;
}
});
return result_1;
}
catch (error) {
var errorMessage = error instanceof Error ? error.message : 'Unknown error';
logger.log("Error parsing rollout configuration: ".concat(errorMessage));
return null;
}
}
exports.parseRolloutConfigurationPhases = parseRolloutConfigurationPhases;
/**
* Returns the current rollout percentage based on current time
* @param rolloutMap - BTree mapping timestamp to percentage
* @returns Current rollout percentage
*/
function getCurrentRolloutPercentage(rolloutMap) {
if (!rolloutMap || rolloutMap.size === 0) {
return 0;
}
var currentTime = Date.now();
// Use getPairOrNextLower to find the entry with the largest timestamp that is <= currentTime
var entry = rolloutMap.getPairOrNextLower(currentTime);
if (entry && entry.length > 1) {
return entry[1];
}
return 0;
}
exports.getCurrentRolloutPercentage = getCurrentRolloutPercentage;