244 lines
6.8 KiB
JavaScript
244 lines
6.8 KiB
JavaScript
var _ = require('../util').lodash,
|
|
dynamicVariables = require('./dynamic-variables'),
|
|
E = '',
|
|
|
|
SuperString, // constructor
|
|
Substitutor; // constructor
|
|
|
|
/**
|
|
* A string-like instance with additional functionalities to track string operations better
|
|
*
|
|
* @constructor
|
|
* @private
|
|
* @param {String} value -
|
|
*/
|
|
SuperString = function SuperString (value) {
|
|
this.value = _.isString(value) ? value : (_.isFunction(value.toString) && value.toString() || E);
|
|
|
|
/**
|
|
* The total number of times there was a successful substitution.
|
|
*
|
|
* @type {number}
|
|
*/
|
|
this.substitutions = 0;
|
|
|
|
/**
|
|
* Keeps a track of the number of tokens replaced in the last replace command
|
|
*
|
|
* @type {number}
|
|
*/
|
|
this.replacements = 0;
|
|
};
|
|
|
|
_.assign(SuperString.prototype, /** @lends SuperString.prototype */ {
|
|
/**
|
|
* Equivalent to string replace but performs additional tracking of the number of tokens replaced
|
|
*
|
|
* @param {RegExp|String} regex -
|
|
* @param {Function|String} fn -
|
|
* @returns {SuperString}
|
|
*/
|
|
replace (regex, fn) {
|
|
var replacements = 0; // maintain a count of tokens replaced
|
|
|
|
// to ensure we do not perform needless operations in the replacement, we use multiple replacement functions
|
|
// after validating the parameters
|
|
this.value = (this.value.replace(regex, _.isFunction(fn) ?
|
|
function () {
|
|
replacements += 1;
|
|
|
|
return fn.apply(this, arguments);
|
|
} :
|
|
// this case is returned when replacer is not a function (ensures we do not need to check it)
|
|
/* istanbul ignore next */
|
|
function () {
|
|
replacements += 1;
|
|
|
|
return fn;
|
|
})
|
|
);
|
|
|
|
this.replacements = replacements; // store the last replacements
|
|
replacements && (this.substitutions += 1); // if any replacement is done, count that some substitution was made
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* @returns {String}
|
|
*/
|
|
toString () {
|
|
return this.value;
|
|
},
|
|
|
|
/**
|
|
* @returns {String}
|
|
*/
|
|
valueOf () {
|
|
return this.value;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Perform replacement of tokens in a SuperString with values stored in keys of an array of objects.
|
|
*
|
|
* @constructor
|
|
* @private
|
|
* @param {Array} variables -
|
|
* @param {Object} defaults -
|
|
*/
|
|
Substitutor = function (variables, defaults) {
|
|
defaults && variables.push(defaults);
|
|
this.variables = variables;
|
|
};
|
|
|
|
_.assign(Substitutor.prototype, /** @lends Substitutor.prototype */ {
|
|
/**
|
|
* Find a key from the array of variable objects provided to the substitutor
|
|
*
|
|
* @param {String} key -
|
|
* @returns {*}
|
|
*/
|
|
find (key) {
|
|
var arr = this.variables,
|
|
obj,
|
|
value,
|
|
i,
|
|
ii;
|
|
|
|
for (i = 0, ii = arr.length; i < ii; i++) {
|
|
obj = arr[i];
|
|
// ensure that the item is an object
|
|
if (!(obj && _.isObject(obj))) {
|
|
continue;
|
|
}
|
|
|
|
// in case the object is a postman variable list, we give special attention
|
|
if (obj.constructor._postman_propertyName === 'VariableList') {
|
|
value = obj.oneNormalizedVariable(key);
|
|
|
|
if (value && !value.disabled) {
|
|
return value;
|
|
}
|
|
}
|
|
// else we return the value from the plain object
|
|
else if (_.has(obj, key)) {
|
|
return obj[key];
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {String} value -
|
|
* @returns {String}
|
|
*/
|
|
parse (value) {
|
|
// convert the value into a SuperString so that it can return tracking results during replacements
|
|
value = new SuperString(value);
|
|
|
|
// get an instance of a replacer function that would be used to replace ejs like variable replacement
|
|
// tokens
|
|
var replacer = Substitutor.replacer(this);
|
|
|
|
// replace the value once and keep on doing it until all tokens are replaced or we have reached a limit of
|
|
// replacements
|
|
do {
|
|
value = value.replace(Substitutor.REGEX_EXTRACT_VARS, replacer);
|
|
} while (value.replacements && (value.substitutions < Substitutor.VARS_SUBREPLACE_LIMIT));
|
|
|
|
// @todo: uncomment this code, and try to raise a warning in some way.
|
|
// do a final check that if recursion limits are reached then replace with blank string
|
|
// if (value.substitutions >= Substitutor.VARS_SUBREPLACE_LIMIT) {
|
|
// value = value.replace(Substitutor.REGEX_EXTRACT_VARS, E);
|
|
// }
|
|
|
|
return value;
|
|
}
|
|
});
|
|
|
|
_.assign(Substitutor, /** @lends Substitutor */ {
|
|
/**
|
|
* Regular expression to be used in {String}.replace for extracting variable substitutions
|
|
*
|
|
* @readOnly
|
|
* @type {RegExp}
|
|
*/
|
|
REGEX_EXTRACT_VARS: /\{\{([^{}]*?)}}/g,
|
|
|
|
/**
|
|
* Defines the number of times the variable substitution mechanism will repeat until all tokens are resolved
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
VARS_SUBREPLACE_LIMIT: 19,
|
|
|
|
/**
|
|
* Maintain a list of types that are native
|
|
*
|
|
* @readOnly
|
|
* @enum {String}
|
|
*/
|
|
NATIVETYPES: {
|
|
string: true,
|
|
number: true,
|
|
boolean: true
|
|
},
|
|
|
|
/**
|
|
* Holds the default variables that Postman supports.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
DEFAULT_VARS: {},
|
|
|
|
/**
|
|
* Create an instance of a substitutor or reuse one
|
|
*
|
|
* @param {Array|Substitutor} variables -
|
|
* @param {Object=} defaults An object containing default variables to substitute
|
|
* @returns {Substitutor}
|
|
*/
|
|
box: function (variables, defaults) {
|
|
return (variables instanceof Substitutor) ? variables : new Substitutor(variables, defaults);
|
|
},
|
|
|
|
/**
|
|
* Checks whether a variable is instance of substitutor
|
|
*
|
|
* @param {*} subject -
|
|
* @returns {Boolean}
|
|
*/
|
|
isInstance: function (subject) {
|
|
return (subject instanceof Substitutor);
|
|
},
|
|
|
|
/**
|
|
* Get an instance of a function that is useful to be passed to a string replace function for extracting tokens
|
|
* and replacing by substitutions
|
|
*
|
|
* @private
|
|
* @param {Substitutor} substitutor -
|
|
* @returns {Function}
|
|
*/
|
|
replacer: function (substitutor) {
|
|
return function (match, token) {
|
|
var r = substitutor.find(token);
|
|
|
|
r && _.isFunction(r) && (r = r());
|
|
r && _.isFunction(r.toString) && (r = r.toString());
|
|
|
|
return Substitutor.NATIVETYPES[(typeof r)] ? r : match;
|
|
};
|
|
}
|
|
});
|
|
|
|
// @todo make the default variables of SuperString extensible and do this anywhere else but here
|
|
_.forOwn(dynamicVariables, function (variable, name) {
|
|
Substitutor.DEFAULT_VARS[name] = variable.generator;
|
|
});
|
|
|
|
module.exports = {
|
|
SuperString,
|
|
Substitutor
|
|
};
|