unleash-server
Version:
Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.
213 lines • 9.12 kB
JavaScript
;
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 });
const memoizee_1 = __importDefault(require("memoizee"));
const joi_1 = require("joi");
const addons_1 = require("../addons");
const events = __importStar(require("../types/events"));
const addon_schema_1 = require("./addon-schema");
const name_exists_error_1 = __importDefault(require("../error/name-exists-error"));
const date_fns_1 = require("date-fns");
const SUPPORTED_EVENTS = Object.keys(events).map((k) => events[k]);
const MASKED_VALUE = '*****';
const WILDCARD_OPTION = '*';
class AddonService {
constructor({ addonStore, eventStore, featureToggleStore, }, { getLogger, server }, tagTypeService, addons) {
this.eventStore = eventStore;
this.addonStore = addonStore;
this.featureToggleStore = featureToggleStore;
this.logger = getLogger('services/addon-service.js');
this.tagTypeService = tagTypeService;
this.addonProviders =
addons ||
(0, addons_1.getAddons)({
getLogger,
unleashUrl: server.unleashUrl,
});
this.sensitiveParams = this.loadSensitiveParams(this.addonProviders);
if (addonStore) {
this.registerEventHandler();
}
// Memoized private function
this.fetchAddonConfigs = (0, memoizee_1.default)(async () => addonStore.getAll({ enabled: true }), {
promise: true,
maxAge: (0, date_fns_1.minutesToMilliseconds)(1),
});
}
loadSensitiveParams(addonProviders) {
const providerDefinitions = Object.values(addonProviders).map((p) => p.definition);
return providerDefinitions.reduce((obj, definition) => {
const sensitiveParams = definition.parameters
.filter((p) => p.sensitive)
.map((p) => p.name);
const o = { ...obj };
o[definition.name] = sensitiveParams;
return o;
}, {});
}
registerEventHandler() {
SUPPORTED_EVENTS.forEach((eventName) => this.eventStore.on(eventName, this.handleEvent(eventName)));
}
handleEvent(eventName) {
const { addonProviders } = this;
return (event) => {
this.fetchAddonConfigs().then((addonInstances) => {
addonInstances
.filter((addon) => addon.events.includes(eventName))
.filter((addon) => !addon.projects ||
addon.projects.length == 0 ||
addon.projects[0] === WILDCARD_OPTION ||
addon.projects.includes(event.project))
.filter((addon) => !addon.environments ||
addon.environments.length == 0 ||
addon.environments[0] === WILDCARD_OPTION ||
addon.environments.includes(event.environment))
.filter((addon) => addonProviders[addon.provider])
.forEach((addon) => addonProviders[addon.provider].handleEvent(event, addon.parameters));
});
};
}
// Should be used by the controller.
async getAddons() {
const addonConfigs = await this.addonStore.getAll();
return addonConfigs.map((a) => this.filterSensitiveFields(a));
}
filterSensitiveFields(addonConfig) {
const { sensitiveParams } = this;
const a = { ...addonConfig };
a.parameters = Object.keys(a.parameters).reduce((obj, paramKey) => {
const o = { ...obj };
if (sensitiveParams[a.provider].includes(paramKey)) {
o[paramKey] = MASKED_VALUE;
}
else {
o[paramKey] = a.parameters[paramKey];
}
return o;
}, {});
return a;
}
async getAddon(id) {
const addonConfig = await this.addonStore.get(id);
return this.filterSensitiveFields(addonConfig);
}
getProviderDefinitions() {
const { addonProviders } = this;
return Object.values(addonProviders).map((p) => p.definition);
}
async addTagTypes(providerName) {
const provider = this.addonProviders[providerName];
if (provider) {
const tagTypes = provider.definition.tagTypes || [];
const createTags = tagTypes.map(async (tagType) => {
try {
await this.tagTypeService.validateUnique(tagType);
await this.tagTypeService.createTagType(tagType, providerName);
}
catch (err) {
if (!(err instanceof name_exists_error_1.default)) {
this.logger.error(err);
}
}
});
await Promise.all(createTags);
}
return Promise.resolve();
}
async createAddon(data, userName) {
const addonConfig = await addon_schema_1.addonSchema.validateAsync(data);
await this.validateKnownProvider(addonConfig);
await this.validateRequiredParameters(addonConfig);
const createdAddon = await this.addonStore.insert(addonConfig);
await this.addTagTypes(createdAddon.provider);
this.logger.info(`User ${userName} created addon ${addonConfig.provider}`);
await this.eventStore.store({
type: events.ADDON_CONFIG_CREATED,
createdBy: userName,
data: { provider: addonConfig.provider },
});
return createdAddon;
}
async updateAddon(id, data, userName) {
const addonConfig = await addon_schema_1.addonSchema.validateAsync(data);
await this.validateRequiredParameters(addonConfig);
if (this.sensitiveParams[addonConfig.provider].length > 0) {
const existingConfig = await this.addonStore.get(id);
addonConfig.parameters = Object.keys(addonConfig.parameters).reduce((params, key) => {
const o = { ...params };
if (addonConfig.parameters[key] === MASKED_VALUE) {
o[key] = existingConfig.parameters[key];
}
else {
o[key] = addonConfig.parameters[key];
}
return o;
}, {});
}
const result = await this.addonStore.update(id, addonConfig);
await this.eventStore.store({
type: events.ADDON_CONFIG_UPDATED,
createdBy: userName,
data: { id, provider: addonConfig.provider },
});
this.logger.info(`User ${userName} updated addon ${id}`);
return result;
}
async removeAddon(id, userName) {
await this.addonStore.delete(id);
await this.eventStore.store({
type: events.ADDON_CONFIG_DELETED,
createdBy: userName,
data: { id },
});
this.logger.info(`User ${userName} removed addon ${id}`);
}
async validateKnownProvider(config) {
const p = this.addonProviders[config.provider];
if (!p) {
throw new TypeError(`Unknown addon provider ${config.provider}`);
}
else {
return true;
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async validateRequiredParameters({ provider, parameters, }) {
const providerDefinition = this.addonProviders[provider].definition;
const requiredParamsMissing = providerDefinition.parameters
.filter((p) => p.required)
.map((p) => p.name)
.filter((requiredParam) => !Object.keys(parameters).includes(requiredParam));
if (requiredParamsMissing.length > 0) {
throw new joi_1.ValidationError(`Missing required parameters: ${requiredParamsMissing.join(',')} `, '', undefined);
}
return true;
}
}
exports.default = AddonService;
//# sourceMappingURL=addon-service.js.map