unleash-server
Version:
Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.
135 lines • 5.34 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const variant_1 = require("./variant");
const playground_strategy_schema_1 = require("../../openapi/spec/playground-strategy-schema");
class UnleashClient {
constructor(repository, strategies) {
this.repository = repository;
this.strategies = strategies || [];
this.strategies.forEach((strategy) => {
if (!strategy ||
!strategy.name ||
typeof strategy.name !== 'string' ||
!strategy.isEnabled ||
typeof strategy.isEnabled !== 'function') {
throw new Error('Invalid strategy data / interface');
}
});
}
getStrategy(name) {
return this.strategies.find((strategy) => strategy.name === name);
}
isEnabled(name, context, fallback) {
const feature = this.repository.getToggle(name);
return this.isFeatureEnabled(feature, context, fallback);
}
isFeatureEnabled(feature, context, fallback) {
if (!feature) {
return fallback();
}
if (!Array.isArray(feature.strategies)) {
return {
result: false,
strategies: [],
};
}
if (feature.strategies.length === 0) {
return {
result: feature.enabled,
strategies: [],
};
}
const strategies = feature.strategies.map((strategySelector) => {
const getStrategy = () => {
// the application hostname strategy relies on external
// variables to calculate its result. As such, we can't
// evaluate it in a way that makes sense. So we'll
// use the 'unknown' strategy instead.
if (strategySelector.name === 'applicationHostname') {
return this.getStrategy('unknown');
}
return (this.getStrategy(strategySelector.name) ??
this.getStrategy('unknown'));
};
const strategy = getStrategy();
const segments = strategySelector.segments
?.map(this.getSegment(this.repository))
.filter(Boolean) ?? [];
return {
name: strategySelector.name,
id: strategySelector.id,
parameters: strategySelector.parameters,
...strategy.isEnabledWithConstraints(strategySelector.parameters, context, strategySelector.constraints, segments),
};
});
// Feature evaluation
const overallStrategyResult = () => {
// if at least one strategy is enabled, then the feature is enabled
if (strategies.some((strategy) => strategy.result.enabled === true)) {
return true;
}
// if at least one strategy is unknown, then the feature _may_ be enabled
if (strategies.some((strategy) => strategy.result.enabled === 'unknown')) {
return playground_strategy_schema_1.playgroundStrategyEvaluation.unknownResult;
}
return false;
};
const evalResults = {
result: overallStrategyResult(),
strategies,
};
return evalResults;
}
getSegment(repo) {
return (segmentId) => {
const segment = repo.getSegment(segmentId);
if (!segment) {
return undefined;
}
return {
name: segment.name,
id: segmentId,
constraints: segment.constraints,
};
};
}
getVariant(name, context, fallbackVariant) {
return this.resolveVariant(name, context, true, fallbackVariant);
}
// This function is intended to close an issue in the proxy where feature enabled
// state gets checked twice when resolving a variant with random stickiness and
// gradual rollout. This is not intended for general use, prefer getVariant instead
forceGetVariant(name, context, fallbackVariant) {
return this.resolveVariant(name, context, false, fallbackVariant);
}
resolveVariant(name, context, checkToggle, fallbackVariant) {
const fallback = fallbackVariant || (0, variant_1.getDefaultVariant)();
const feature = this.repository.getToggle(name);
if (typeof feature === 'undefined' ||
!feature.variants ||
!Array.isArray(feature.variants) ||
feature.variants.length === 0 ||
!feature.enabled) {
return fallback;
}
let enabled = true;
if (checkToggle) {
enabled =
this.isFeatureEnabled(feature, context, () => fallbackVariant ? fallbackVariant.enabled : false).result === true;
if (!enabled) {
return fallback;
}
}
const variant = (0, variant_1.selectVariant)(feature, context);
if (variant === null) {
return fallback;
}
return {
name: variant.name,
payload: variant.payload,
enabled: !checkToggle || enabled,
};
}
}
exports.default = UnleashClient;
//# sourceMappingURL=client.js.map