232 lines
7.2 KiB
JavaScript
232 lines
7.2 KiB
JavaScript
var _ = require('../util').lodash,
|
|
|
|
__PARENT = '__parent',
|
|
|
|
PropertyBase; // constructor
|
|
|
|
/**
|
|
* @typedef PropertyBase.definition
|
|
* @property {String|Description} [description]
|
|
*/
|
|
/**
|
|
* Base of all properties in Postman Collection. It defines the root for all standalone properties for postman
|
|
* collection.
|
|
*
|
|
* @constructor
|
|
* @param {PropertyBase.definition} definition -
|
|
*/
|
|
PropertyBase = function PropertyBase (definition) {
|
|
// In case definition object is missing, there is no point moving forward. Also if the definition is basic string
|
|
// we do not need to do anything with it.
|
|
if (!definition || typeof definition === 'string') { return; }
|
|
|
|
// call the meta extraction functions to create the object where all keys that are prefixed with underscore can be
|
|
// stored. more details on that can be retrieved from the propertyExtractMeta function itself.
|
|
// @todo: make this a closed function to do getter and setter which is non enumerable
|
|
var src = definition && definition.info || definition,
|
|
meta = _(src).pickBy(PropertyBase.propertyIsMeta).mapKeys(PropertyBase.propertyUnprefixMeta).value();
|
|
|
|
if (_.keys(meta).length) {
|
|
this._ = _.isObject(this._) ?
|
|
/* istanbul ignore next */
|
|
_.mergeDefined(this._, meta) :
|
|
meta;
|
|
}
|
|
};
|
|
|
|
_.assign(PropertyBase.prototype, /** @lends PropertyBase.prototype */ {
|
|
|
|
/**
|
|
* Invokes the given iterator for every parent in the parent chain of the given element.
|
|
*
|
|
* @param {Object} options - A set of options for the parent chain traversal.
|
|
* @param {?Boolean} [options.withRoot=false] - Set to true to include the collection object as well.
|
|
* @param {Function} iterator - The function to call for every parent in the ancestry chain.
|
|
* @todo Cache the results
|
|
*/
|
|
forEachParent (options, iterator) {
|
|
_.isFunction(options) && (iterator = options, options = {});
|
|
if (!_.isFunction(iterator) || !_.isObject(options)) { return; }
|
|
|
|
var parent = this.parent(),
|
|
grandparent = parent && _.isFunction(parent.parent) && parent.parent();
|
|
|
|
while (parent && (grandparent || options.withRoot)) {
|
|
iterator(parent);
|
|
parent = grandparent;
|
|
grandparent = grandparent && _.isFunction(grandparent.parent) && grandparent.parent();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Tries to find the given property locally, and then proceeds to lookup in each parent,
|
|
* going up the chain as necessary. Lookup will continue until `customizer` returns a truthy value. If used
|
|
* without a customizer, the lookup will stop at the first parent that contains the property.
|
|
*
|
|
* @param {String} property -
|
|
* @param {Function} [customizer] -
|
|
* @returns {*|undefined}
|
|
*/
|
|
findInParents (property, customizer) {
|
|
var owner = this.findParentContaining(property, customizer);
|
|
|
|
return owner ? owner[property] : undefined;
|
|
},
|
|
|
|
/**
|
|
* Looks up the closest parent which has a truthy value for the given property. Lookup will continue
|
|
* until `customizer` returns a truthy value. If used without a customizer,
|
|
* the lookup will stop at the first parent that contains the property.
|
|
*
|
|
* @private
|
|
* @param {String} property -
|
|
* @param {Function} [customizer] -
|
|
* @returns {PropertyBase|undefined}
|
|
*/
|
|
findParentContaining (property, customizer) {
|
|
var parent = this;
|
|
|
|
// if customizer is present test with it
|
|
if (customizer) {
|
|
customizer = customizer.bind(this);
|
|
|
|
do {
|
|
// else check for existence
|
|
if (customizer(parent)) {
|
|
return parent;
|
|
}
|
|
|
|
parent = parent.__parent;
|
|
} while (parent);
|
|
}
|
|
|
|
// else check for existence
|
|
else {
|
|
do {
|
|
if (parent[property]) {
|
|
return parent;
|
|
}
|
|
|
|
parent = parent.__parent;
|
|
} while (parent);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns the JSON representation of a property, which conforms to the way it is defined in a collection.
|
|
* You can use this method to get the instantaneous representation of any property, including a {@link Collection}.
|
|
*/
|
|
toJSON () {
|
|
return _.reduce(this, function (accumulator, value, key) {
|
|
if (value === undefined) { // true/false/null need to be preserved.
|
|
return accumulator;
|
|
}
|
|
|
|
// Handle plurality of PropertyLists in the SDK vs the exported JSON.
|
|
// Basically, removes the trailing "s" from key if the value is a property list.
|
|
// eslint-disable-next-line max-len
|
|
if (value && value._postman_propertyIsList && !value._postman_proprtyIsSerialisedAsPlural && _.endsWith(key, 's')) {
|
|
key = key.slice(0, -1);
|
|
}
|
|
|
|
// Handle 'PropertyBase's
|
|
if (value && _.isFunction(value.toJSON)) {
|
|
accumulator[key] = value.toJSON();
|
|
|
|
return accumulator;
|
|
}
|
|
|
|
// Handle Strings
|
|
if (_.isString(value)) {
|
|
accumulator[key] = value;
|
|
|
|
return accumulator;
|
|
}
|
|
|
|
// Everything else
|
|
accumulator[key] = _.cloneElement(value);
|
|
|
|
return accumulator;
|
|
}, {});
|
|
},
|
|
|
|
/**
|
|
* Returns the meta keys associated with the property
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
meta () {
|
|
return arguments.length ? _.pick(this._, Array.prototype.slice.apply(arguments)) : _.cloneDeep(this._);
|
|
},
|
|
|
|
/**
|
|
* Returns the parent of item
|
|
*
|
|
* @returns {*|undefined}
|
|
*/
|
|
parent () {
|
|
// @todo return grandparent only if it is a list
|
|
return this && this.__parent && (this.__parent.__parent || this.__parent) || undefined;
|
|
},
|
|
|
|
/**
|
|
* Accepts an object and sets it as the parent of the current property.
|
|
*
|
|
* @param {Object} parent The object to set as parent.
|
|
* @private
|
|
*/
|
|
setParent (parent) {
|
|
_.assignHidden(this, __PARENT, parent);
|
|
}
|
|
});
|
|
|
|
_.assign(PropertyBase, /** @lends PropertyBase */ {
|
|
|
|
/**
|
|
* Defines the name of this property for internal use.
|
|
*
|
|
* @private
|
|
* @readOnly
|
|
* @type {String}
|
|
*/
|
|
_postman_propertyName: 'PropertyBase',
|
|
|
|
/**
|
|
* Filter function to check whether a key starts with underscore or not. These usually are the meta properties. It
|
|
* returns `true` if the criteria is matched.
|
|
*
|
|
* @param {*} value -
|
|
* @param {String} key -
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
propertyIsMeta: function (value, key) {
|
|
return _.startsWith(key, '_') && (key !== '_');
|
|
},
|
|
|
|
/**
|
|
* Map function that removes the underscore prefix from an object key.
|
|
*
|
|
* @param {*} value -
|
|
* @param {String} key -
|
|
* @returns {String}
|
|
*/
|
|
propertyUnprefixMeta: function (value, key) {
|
|
return _.trimStart(key, '_');
|
|
},
|
|
|
|
/**
|
|
* Static function which allows calling toJSON() on any object.
|
|
*
|
|
* @param {Object} obj -
|
|
* @returns {*}
|
|
*/
|
|
toJSON: function (obj) {
|
|
return PropertyBase.prototype.toJSON.call(obj);
|
|
}
|
|
});
|
|
|
|
module.exports = {
|
|
PropertyBase
|
|
};
|