UNPKG

@splitsoftware/splitio

Version:
149 lines (148 loc) 7.36 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.splitsParserFromFileFactory = void 0; var tslib_1 = require("tslib"); var fs_1 = (0, tslib_1.__importDefault)(require("fs")); var path_1 = (0, tslib_1.__importDefault)(require("path")); var js_yaml_1 = (0, tslib_1.__importDefault)(require("js-yaml")); var lang_1 = require("@splitsoftware/splitio-commons/cjs/utils/lang"); var parseCondition_1 = require("@splitsoftware/splitio-commons/cjs/sync/offline/splitsParser/parseCondition"); var logPrefix = 'sync:offline:fetcher: '; var DEFAULT_FILENAME = '.split'; function configFilesPath(configFilePath) { if (configFilePath === DEFAULT_FILENAME || !(0, lang_1.isString)(configFilePath)) { var root = process.env.HOME; // @TODO env var not documented in help center if (process.env.SPLIT_CONFIG_ROOT) root = process.env.SPLIT_CONFIG_ROOT; if (!root) throw new Error('Missing root of the feature flags mock file.'); configFilePath = path_1.default.join(root, DEFAULT_FILENAME); } // Validate the extensions if (!((0, lang_1.endsWith)(configFilePath, '.yaml', true) || (0, lang_1.endsWith)(configFilePath, '.yml', true) || (0, lang_1.endsWith)(configFilePath, '.split', true))) throw new Error("Invalid extension specified for feature flags mock file. Accepted extensions are \".yml\" and \".yaml\". Your specified file is " + configFilePath); if (!fs_1.default.existsSync(configFilePath)) throw new Error("Feature flags mock file not found in " + configFilePath + " - Please review the file location."); return configFilePath; } // This function is not pure nor meant to be. Here we apply modifications to cover // for behavior that's ensured by the BE. function arrangeConditions(mocksData) { // Iterate through each feature flag data (0, lang_1.forOwn)(mocksData, function (data) { var conditions = data.conditions; // On the manager, as feature flag JSONs come with all treatments on the partitions prop, // we'll add all the treatments to the first condition. var firstRolloutCondition = (0, lang_1.find)(conditions, function (cond) { return cond.conditionType === 'ROLLOUT'; }); // Malformed mocks may have var treatments = (0, lang_1.uniq)(data.treatments); // If they're only specifying a whitelist we add the treatments there. var allTreatmentsCondition = firstRolloutCondition ? firstRolloutCondition : conditions[0]; var fullyAllocatedTreatment = allTreatmentsCondition.partitions[0].treatment; treatments.forEach(function (treatment) { if (treatment !== fullyAllocatedTreatment) { allTreatmentsCondition.partitions.push({ treatment: treatment, size: 0 }); } }); // Don't need these anymore delete data.treatments; }); } function splitsParserFromFileFactory() { var previousMock = 'NO_MOCK_LOADED'; // Parse `.split` configuration file and return a map of feature flag objects function readFeatureFlagConfigFile(log, filePath) { var FEATURE_FLAG_POSITION = 0; var TREATMENT_POSITION = 1; var data; try { data = fs_1.default.readFileSync(filePath, 'utf-8'); } catch (e) { log.error(e && e.message); return {}; } if (data === previousMock) return false; previousMock = data; var featureFlagObjects = data.split(/\r?\n/).reduce(function (accum, line, index) { var tuple = line.trim(); if (tuple === '' || tuple.charAt(0) === '#') { log.debug(logPrefix + ("Ignoring empty line or comment at #" + index)); } else { tuple = tuple.split(/\s+/); if (tuple.length !== 2) { log.debug(logPrefix + ("Ignoring line since it does not have exactly two columns #" + index)); } else { var featureFlagName = tuple[FEATURE_FLAG_POSITION]; var condition = (0, parseCondition_1.parseCondition)({ treatment: tuple[TREATMENT_POSITION] }); accum[featureFlagName] = { conditions: [condition], configurations: {}, trafficTypeName: 'localhost' }; } } return accum; }, {}); return featureFlagObjects; } // Parse `.yml` or `.yaml` configuration files and return a map of feature flag objects function readYAMLConfigFile(log, filePath) { var data = ''; var yamldoc = null; try { data = fs_1.default.readFileSync(filePath, 'utf8'); if (data === previousMock) return false; previousMock = data; yamldoc = js_yaml_1.default.safeLoad(data); } catch (e) { log.error(e); return {}; } // Each entry will be mapped to a condition, but we'll also keep the configurations map. var mocksData = (yamldoc).reduce(function (accum, featureFlagEntry) { var featureFlagName = Object.keys(featureFlagEntry)[0]; if (!featureFlagName || !(0, lang_1.isString)(featureFlagEntry[featureFlagName].treatment)) log.error(logPrefix + 'Ignoring entry on YAML since the format is incorrect.'); var mockData = featureFlagEntry[featureFlagName]; // "Template" for each feature flag accumulated data if (!accum[featureFlagName]) { accum[featureFlagName] = { configurations: {}, conditions: [], treatments: [], trafficTypeName: 'localhost' }; } // Assign the config if there is one on the mock if (mockData.config) accum[featureFlagName].configurations[mockData.treatment] = mockData.config; // Parse the condition from the entry. var condition = (0, parseCondition_1.parseCondition)(mockData); accum[featureFlagName].conditions[condition.conditionType === 'ROLLOUT' ? 'push' : 'unshift'](condition); // Also keep track of the treatments, will be useful for manager functionality. accum[featureFlagName].treatments.push(mockData.treatment); return accum; }, {}); arrangeConditions(mocksData); return mocksData; } // Load the content of a configuration file into an Object return function featureFlagsParserFromFile(_a) { var features = _a.features, log = _a.log; var filePath = configFilesPath(features); var mockData; // If we have a filePath, it means the extension is correct, choose the parser. if ((0, lang_1.endsWith)(filePath, '.split')) { log.warn(logPrefix + '.split mocks will be deprecated soon in favor of YAML files, which provide more targeting power. Take a look in our documentation.'); mockData = readFeatureFlagConfigFile(log, filePath); } else { mockData = readYAMLConfigFile(log, filePath); } return mockData; }; } exports.splitsParserFromFileFactory = splitsParserFromFileFactory;