123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- "use strict";
- var _ = require('../../lodash'),
- // @todo discontinue in v4
- encodeQueryParam = require('postman-url-encoder/encoder').encodeQueryParam, Property = require('./property').Property, PropertyList = require('./property-list').PropertyList, E = '', AMPERSAND = '&', STRING = 'string', EQUALS = '=', EMPTY = '', HASH = '#', BRACE_START = '{{', BRACE_END = '}}', REGEX_HASH = /#/g, REGEX_EQUALS = /=/g, // eslint-disable-line no-div-regex
- REGEX_AMPERSAND = /&/g, REGEX_BRACE_START = /%7B%7B/g, REGEX_BRACE_END = /%7D%7D/g, REGEX_EXTRACT_VARS = /{{[^{}]*[&#=][^{}]*}}/g, QueryParam,
- /**
- * Percent encode reserved chars (&, = and #) in the given string.
- *
- * @private
- * @param {String} str
- * @param {Boolean} encodeEquals
- * @returns {String}
- */
- encodeReservedChars = function (str, encodeEquals) {
- if (!str) {
- return str;
- }
- // eslint-disable-next-line lodash/prefer-includes
- str.indexOf(AMPERSAND) !== -1 && (str = str.replace(REGEX_AMPERSAND, '%26'));
- // eslint-disable-next-line lodash/prefer-includes
- str.indexOf(HASH) !== -1 && (str = str.replace(REGEX_HASH, '%23'));
- // eslint-disable-next-line lodash/prefer-includes
- encodeEquals && str.indexOf(EQUALS) !== -1 && (str = str.replace(REGEX_EQUALS, '%3D'));
- return str;
- },
- /**
- * Normalize the given param string by percent-encoding the reserved chars
- * such that it won't affect the re-parsing.
- *
- * @note `&`, `=` and `#` needs to be percent-encoded otherwise re-parsing
- * the same URL string will generate different output
- *
- * @private
- * @param {String} str
- * @param {Boolean} encodeEquals
- * @returns {String}
- */
- normalizeParam = function (str, encodeEquals) {
- // bail out if the given sting is null or empty
- if (!(str && typeof str === STRING)) {
- return str;
- }
- // bail out if the given string does not include reserved chars
- // eslint-disable-next-line lodash/prefer-includes
- if (str.indexOf(AMPERSAND) === -1 && str.indexOf(HASH) === -1) {
- // eslint-disable-next-line lodash/prefer-includes
- if (!(encodeEquals && str.indexOf(EQUALS) !== -1)) {
- return str;
- }
- }
- var normalizedString = '', pointer = 0, variable, match, index;
- // find all the instances of {{<variable>}} which includes reserved chars
- while ((match = REGEX_EXTRACT_VARS.exec(str)) !== null) {
- variable = match[0];
- index = match.index;
- // [pointer, index) string is normalized + the matched variable
- normalizedString += encodeReservedChars(str.slice(pointer, index), encodeEquals) + variable;
- // update the pointer
- pointer = index + variable.length;
- }
- // whatever left in the string is normalized as well
- if (pointer < str.length) {
- normalizedString += encodeReservedChars(str.slice(pointer), encodeEquals);
- }
- return normalizedString;
- };
- /**
- * @typedef QueryParam.definition
- * @property {String} key The name ("key") of the query parameter.
- * @property {String} value The value of the parameter.
- */
- _.inherit((
- /**
- * Represents a URL query parameter, which can exist in request URL or POST data.
- *
- * @constructor
- * @extends {Property}
- * @param {FormParam.definition|String} options Pass the initial definition of the query parameter. In case of
- * string, the query parameter is parsed using {@link QueryParam.parseSingle}.
- */
- QueryParam = function PostmanQueryParam(options) {
- // this constructor is intended to inherit and as such the super constructor is required to be executed
- QueryParam.super_.apply(this, arguments);
- this.update(options);
- }), Property);
- _.assign(QueryParam.prototype, /** @lends QueryParam.prototype */ {
- /**
- * Converts the QueryParameter to a single param string.
- *
- * @returns {String}
- */
- toString: function () {
- return QueryParam.unparseSingle(this);
- },
- /**
- * Updates the key and value of the query parameter
- *
- * @param {String|Object} param
- * @param {String} param.key
- * @param {String=} [param.value]
- */
- update: function (param) {
- _.assign(this, /** @lends QueryParam.prototype */ _.isString(param) ? QueryParam.parseSingle(param) : {
- key: _.get(param, 'key'),
- value: _.get(param, 'value')
- });
- _.has(param, 'system') && (this.system = param.system);
- },
- valueOf: function () {
- return _.isString(this.value) ? this.value : EMPTY;
- }
- });
- _.assign(QueryParam, /** @lends QueryParam */ {
- /**
- * Defines the name of this property for internal use.
- * @private
- * @readOnly
- * @type {String}
- */
- _postman_propertyName: 'QueryParam',
- /**
- * Declare the list index key, so that property lists of query parameters work correctly
- *
- * @type {String}
- */
- _postman_propertyIndexKey: 'key',
- /**
- * Query params can have multiple values, so set this to true.
- *
- * @type {Boolean}
- */
- _postman_propertyAllowsMultipleValues: true,
- /**
- * Parse a query string into an array of objects, where each object contains a key and a value.
- *
- * @param {String} query
- * @returns {Array}
- */
- parse: function (query) {
- return _.isString(query) ? query.split(AMPERSAND).map(QueryParam.parseSingle) : [];
- },
- /**
- * Parses a single query parameter.
- *
- * @param {String} param
- * @param {Number} idx
- * @param {String[]} all - array of all params, in case this is being called while parsing multiple params.
- * @returns {{key: string|null, value: string|null}}
- */
- parseSingle: function (param, idx, all) {
- // helps handle weird edge cases such as "/get?a=b&&"
- if (param === EMPTY && // if param is empty
- _.isNumber(idx) && // this and the next condition ensures that this is part of a map call
- _.isArray(all) &&
- idx !== (all && (all.length - 1))) { // not last parameter in the array
- return { key: null, value: null };
- }
- var index = (typeof param === STRING) ? param.indexOf(EQUALS) : -1, paramObj = {};
- // this means that there was no value for this key (not even blank, so we store this info) and the value is set
- // to null
- if (index < 0) {
- paramObj.key = param.substr(0, param.length);
- paramObj.value = null;
- }
- else {
- paramObj.key = param.substr(0, index);
- paramObj.value = param.substr(index + 1);
- }
- return paramObj;
- },
- /**
- * Create a query string from array of parameters (or object of key-values). This function ensures that
- * the double braces "{{" and "}}" are not URL-encoded on unparsing, which allows for variable-substitution.
- *
- * @param {Array|Object} params
- * @param {Object=} options
- * @param {?Boolean} [options.encode=false] - Enables URL encoding of the parameters
- * @param {?Boolean} [options.ignoreDisabled=false] - Removes disabled query parameters when set to true.
- * @returns {string}
- *
- * @deprecated since v3.4.6, drop support for `options.encode`
- *
- * @todo - remove disabled arg and flatten params (retain back compat)
- */
- unparse: function (params, options) {
- if (!params) {
- return EMPTY;
- }
- var str, firstEnabledParam = true, encode = options && options.encode, ignoreDisabled = options && options.ignoreDisabled;
- // Convert hash maps to an array of params
- if (!_.isArray(params) && !PropertyList.isPropertyList(params)) {
- return _.reduce(params, function (result, value, key) {
- result && (result += AMPERSAND);
- return result + QueryParam.unparseSingle({ key: key, value: value }, encode);
- }, EMPTY);
- }
- // construct a query parameter string from the list, with considerations for disabled values
- str = params.reduce(function (result, param) {
- // If disabled parameters are to be ignored, bail out here
- if (ignoreDisabled && (param.disabled === true)) {
- return result;
- }
- // don't add '&' for the very first enabled param
- if (firstEnabledParam) {
- firstEnabledParam = false;
- }
- // add '&' before concatenating param
- else {
- result += AMPERSAND;
- }
- return result + QueryParam.unparseSingle(param, encode);
- }, EMPTY);
- encode && (str = str.replace(REGEX_BRACE_START, BRACE_START).replace(REGEX_BRACE_END, BRACE_END));
- return str;
- },
- /**
- * Takes a query param and converts to string
- *
- * @param {Object} obj
- * @param {Boolean} encode
- * @returns {String}
- *
- * @deprecated since v3.4.6, drop support for `encode`
- */
- unparseSingle: function (obj, encode) {
- if (!obj) {
- return EMPTY;
- }
- var key = obj.key, value = obj.value, result;
- if (typeof key === STRING) {
- result = encode ? encodeQueryParam(key) : normalizeParam(key, true);
- }
- else {
- result = E;
- }
- if (typeof value === STRING) {
- result += EQUALS + (encode ? encodeQueryParam(value) : normalizeParam(value));
- }
- return result;
- }
- });
- module.exports = {
- QueryParam: QueryParam
- };
|