sinch-rtc
Version:
RTC JavaScript/Web SDK
156 lines • 7.24 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeaturesFactory = exports.Features = void 0;
const Event_1 = require("../utils/Event");
const fsm_1 = require("../session/fsm");
const FeatureStorage_1 = require("../storage/FeatureStorage");
const TimeService_1 = require("../utils/TimeService");
const constants_1 = require("../constants");
const utils_1 = require("../utils");
class Features {
constructor(featureStorage) {
this.featureStorage = featureStorage;
this.featureFlags = new Map();
this.timeService = TimeService_1.TimeServiceFactory.create();
this.onFeatureChanged = new Event_1.Event();
}
getFeatureFlag(key) {
return this.featureFlags.get(key);
}
initialiseFeatureFlags() {
const shouldUseDefaultValues = this.featureStorage.isEmpty();
Object.keys(constants_1.FeatureFlag).forEach((featureFlagKey) => {
const featureFlag = constants_1.FeatureFlag[featureFlagKey];
const featureFlagValue = shouldUseDefaultValues
? constants_1.FeatureFlagDefaults[featureFlag]
: this.featureStorage.getFeatureFlag(featureFlag);
this.featureFlags.set(featureFlag, featureFlagValue !== null && featureFlagValue !== void 0 ? featureFlagValue : false);
});
}
shouldRefresh() {
const lastRefreshedAtMs = this.featureStorage.getFeatureFlag(FeatureStorage_1.FeatureStorage.LAST_REFRESHED_AT);
if (!lastRefreshedAtMs) {
return true;
}
const nowMs = this.timeService.now.getTime();
return nowMs - lastRefreshedAtMs >= FeatureStorage_1.FeatureStorage.REFRESH_INTERVAL_MS;
}
setUserInstance(userId, instanceId, applicationKey) {
this.userId = userId;
this.instanceId = instanceId;
this.featureStorage.setApplicationConfig(userId, applicationKey);
this.initialiseFeatureFlags();
}
refresh(apiClient) {
return __awaiter(this, void 0, void 0, function* () {
const fetchedFeatureFlags = yield this.fetchFeatureFlags(apiClient);
this.updateAndPublishFeaturesIfNeeded(fetchedFeatureFlags);
});
}
addListener(listener) {
this.onFeatureChanged.add(listener);
}
resetListeners() {
this.onFeatureChanged = new Event_1.Event();
}
fetchFeatureFlags(apiClient) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.userId) {
throw new fsm_1.InvalidOperationError("Missing userId");
}
if (!this.instanceId) {
throw new fsm_1.InvalidOperationError("Missing instanceId");
}
const fetchedFeatureFlags = new Map();
try {
const featureFlagsResponse = yield apiClient.getFeatureFlags({
userId: this.userId,
instanceId: this.instanceId,
});
for (const [featureFlag, value] of Object.entries(featureFlagsResponse)) {
fetchedFeatureFlags.set(featureFlag, value);
}
}
catch (error) {
console.error("Could not fetch feature flags. Will use default values", {
error,
});
fetchedFeatureFlags.clear();
}
return fetchedFeatureFlags;
});
}
updateFeatureFlags(fetchedFeatureFlags) {
// add default values if fetched values are undefined/wrong
this.featureFlags.forEach((_, featureFlagKey) => {
const fetchedFeatureFlagValue = fetchedFeatureFlags.get(featureFlagKey);
const defaultValue = constants_1.FeatureFlagDefaults[featureFlagKey];
const isFetchedValueValid = utils_1.TypeHelper.isBoolean(fetchedFeatureFlagValue) ||
(utils_1.TypeHelper.isNumber(fetchedFeatureFlagValue) &&
typeof fetchedFeatureFlagValue === typeof defaultValue);
this.featureFlags.set(featureFlagKey, isFetchedValueValid
? fetchedFeatureFlagValue
: constants_1.FeatureFlagDefaults[featureFlagKey]);
});
}
updateAndPublishFeaturesIfNeeded(fetchedFeatureFlags) {
if (this.hasFeaturesChanged(fetchedFeatureFlags)) {
this.updateFeatureFlags(fetchedFeatureFlags);
this.storeFeatures();
this.publishFeaturesChanged();
}
}
publishFeaturesChanged() {
this.onFeatureChanged.fire(this.featureFlags);
}
hasFeaturesChanged(fetchedFeatureFlags) {
const featureFlagsFromStorage = Object.entries(constants_1.FeatureFlag).reduce((accumulator, featureFlagEntry) => {
const featureFlagFromStorage = this.featureStorage.getFeatureFlag(featureFlagEntry[1]);
if (utils_1.TypeHelper.isBoolean(featureFlagFromStorage) ||
utils_1.TypeHelper.isNumber(featureFlagFromStorage)) {
accumulator.set(featureFlagEntry[1], featureFlagFromStorage);
}
return accumulator;
}, new Map());
// if a flag was removed on the server, we want to refresh it in local storage
// otherwise if both fetched feature flags and features in storage are empty we want to use default values
if (fetchedFeatureFlags.size < featureFlagsFromStorage.size ||
!(fetchedFeatureFlags.size && featureFlagsFromStorage.size)) {
return true;
}
// otherwise we compare the features by key
for (const [featureFlagKey, value] of fetchedFeatureFlags) {
const valueFromStorage = featureFlagsFromStorage.get(featureFlagKey);
if (valueFromStorage !== value ||
(valueFromStorage === undefined &&
!featureFlagsFromStorage.has(featureFlagKey))) {
return true;
}
return false;
}
}
storeFeatures() {
this.featureStorage.removeAll();
for (const [featureFlagKey, value] of this.featureFlags) {
this.featureStorage.setFeatureFlag(featureFlagKey, value);
}
this.featureStorage.setFeatureFlag(FeatureStorage_1.FeatureStorage.LAST_REFRESHED_AT, this.timeService.now.getTime());
}
}
exports.Features = Features;
class FeaturesFactory {
static create(featureStorage) {
return new Features(featureStorage);
}
}
exports.FeaturesFactory = FeaturesFactory;
//# sourceMappingURL=Features.js.map