variable.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. "use strict";
  2. var _ = require('../../lodash'), Property = require('./property').Property, E = '', ANY = 'any', NULL = 'null', STRING = 'string', Variable;
  3. /**
  4. * The object representation of a Variable consists the variable value and type. It also optionally includes the `id`
  5. * and a friendly `name` of the variable. The `id` and the `name` of a variable is usually managed and used when a
  6. * variable is made part of a {@link VariableList} instance.
  7. *
  8. * @typedef {Object} Variable.definition
  9. * @property {*=} [value] - The value of the variable that will be stored and will be typecast to the `type`
  10. * set in the variable or passed along in this parameter.
  11. * @property {String=} [type] - The type of this variable from the list of types defined at {@link Variable.types}.
  12. *
  13. * @example
  14. * {
  15. * "id": "my-var-1",
  16. * "name": "MyFirstVariable",
  17. * "value": "Hello World",
  18. * "type": "string"
  19. * }
  20. */
  21. _.inherit((
  22. /**
  23. * A variable inside a collection is similar to variables in any programming construct. The variable has an
  24. * identifier name (provided by its id) and a value. A variable is optionally accompanied by a variable type. One
  25. * or more variables can be associated with a collection and can be referred from anywhere else in the collection
  26. * using the double-brace {{variable-id}} format. Properties can then use the `.toObjectResolved` function to
  27. * procure an object representation of the property with all variable references replaced by corresponding values.
  28. *
  29. * @constructor
  30. * @extends {Property}
  31. * @param {Variable.definition=} [definition] - Specify the initial value and type of the variable.
  32. */
  33. Variable = function PostmanVariable(definition) {
  34. // this constructor is intended to inherit and as such the super constructor is required to be executed
  35. Variable.super_.apply(this, arguments);
  36. // check what is the property name for indexing this variable
  37. var indexer = this.constructor._postman_propertyIndexKey;
  38. _.assign(this, /** @lends Variable.prototype */ {
  39. /**
  40. * @type {Variable.types}
  41. */
  42. type: ANY,
  43. /**
  44. * @type {*}
  45. */
  46. value: undefined
  47. });
  48. if (!_.isNil(definition)) {
  49. /**
  50. * The name of the variable. This is used for referencing this variable from other locations and scripts
  51. * @type {String}
  52. * @name key
  53. * @memberOf Variable.prototype
  54. */
  55. _.has(definition, indexer) && (this[indexer] = definition[indexer]);
  56. this.update(definition);
  57. }
  58. }), Property);
  59. _.assign(Variable.prototype, /** @lends Variable.prototype */ {
  60. /**
  61. * Gets the value of the variable.
  62. *
  63. * @returns {Variable.types}
  64. */
  65. get: function () {
  66. return _.isFunction(this.value) ? this.castOut(this.value()) : this.castOut(this.value);
  67. },
  68. /**
  69. * Sets the value of the variable.
  70. *
  71. * @param {*} value
  72. */
  73. set: function (value) {
  74. // @todo - figure out how secure is this!
  75. this.value = _.isFunction(value) ? value : this.castIn(value);
  76. },
  77. /**
  78. * An alias of this.get and this.set.
  79. *
  80. * @param {*=} [value]
  81. * @returns {*}
  82. */
  83. valueOf: function (value) {
  84. arguments.length && this.set(value);
  85. return this.get();
  86. },
  87. /**
  88. * Returns the stringified value of the variable.
  89. *
  90. * @returns {String}
  91. */
  92. toString: function () {
  93. var value = this.valueOf();
  94. // returns String representation of null as it's a valid JSON type
  95. // refer: https://github.com/postmanlabs/postman-app-support/issues/8493
  96. if (value === null) {
  97. return NULL;
  98. }
  99. // returns empty string if the value is undefined or does not implement
  100. // the toString method
  101. return (!_.isNil(value) && _.isFunction(value.toString)) ? value.toString() : E;
  102. },
  103. /**
  104. * Typecasts a value to the {@link Variable.types} of this {@link Variable}. Returns the value of the variable
  105. * converted to the type specified in {@link Variable#type}.
  106. *
  107. * @param {*} value
  108. * @returns {*}
  109. */
  110. cast: function (value) {
  111. return this.castOut(value);
  112. },
  113. /**
  114. * Typecasts a value to the {@link Variable.types} of this {@link Variable}. Returns the value of the variable
  115. * converted to the type specified in {@link Variable#type}.
  116. *
  117. * @private
  118. * @param {*} value
  119. * @returns {*}
  120. */
  121. castIn: function (value) {
  122. var handler = Variable.types[this.type] || Variable.types.any;
  123. return _.isFunction(handler) ? handler(value) : handler.in(value);
  124. },
  125. /**
  126. * Typecasts a value from the {@link Variable.types} of this {@link Variable}. Returns the value of the variable
  127. * converted to the type specified in {@link Variable#type}.
  128. *
  129. * @private
  130. * @param {*} value
  131. * @returns {*}
  132. */
  133. castOut: function (value) {
  134. var handler = Variable.types[this.type] || Variable.types.any;
  135. return _.isFunction(handler) ? handler(value) : handler.out(value);
  136. },
  137. /**
  138. * Sets or gets the type of the value.
  139. *
  140. * @param {String} typeName
  141. * @param {Boolean} _noCast
  142. * @returns {String} - returns the current type of the variable from the list of {@link Variable.types}
  143. */
  144. valueType: function (typeName, _noCast) {
  145. !_.isNil(typeName) && (typeName = typeName.toString().toLowerCase()); // sanitize
  146. if (!Variable.types[typeName]) {
  147. return this.type || ANY; // @todo: throw new Error('Invalid variable type.');
  148. }
  149. // set type if it is valid
  150. this.type = typeName;
  151. // 1. get the current value
  152. // 2. set the new type if it is valid and cast the stored value
  153. // 3. then set the interstitial value
  154. var interstitialCastValue;
  155. // do not touch value functions
  156. if (!(_noCast || _.isFunction(this.value))) {
  157. interstitialCastValue = this.get();
  158. this.set(interstitialCastValue);
  159. interstitialCastValue = null; // just a precaution
  160. }
  161. return this.type;
  162. },
  163. /**
  164. * Updates the type and value of a variable from an object or JSON definition of the variable.
  165. *
  166. * @param {Variable.definition} options
  167. */
  168. update: function (options) {
  169. if (!_.isObject(options)) {
  170. return;
  171. }
  172. // set type and value.
  173. // @note that we cannot update the key, once created during construction
  174. options.hasOwnProperty('type') && this.valueType(options.type, options.hasOwnProperty('value'));
  175. options.hasOwnProperty('value') && this.set(options.value);
  176. options.hasOwnProperty('system') && (this.system = options.system);
  177. options.hasOwnProperty('disabled') && (this.disabled = options.disabled);
  178. }
  179. });
  180. _.assign(Variable, /** @lends Variable */ {
  181. /**
  182. * Defines the name of this property for internal use.
  183. * @private
  184. * @readOnly
  185. * @type {String}
  186. */
  187. _postman_propertyName: 'Variable',
  188. /**
  189. * Specify the key to be used while indexing this object
  190. * @private
  191. * @readOnly
  192. * @type {String}
  193. */
  194. _postman_propertyIndexKey: 'key',
  195. /**
  196. * The possible supported types of a variable is defined here. The keys defined here are the possible values of
  197. * {@link Variable#type}.
  198. *
  199. * Additional variable types can be supported by adding the type-casting function to this enumeration.
  200. * @enum {Function}
  201. * @readonly
  202. */
  203. types: {
  204. /**
  205. * When a variable's `type` is set to "string", it ensures that {@link Variable#get} converts the value of the
  206. * variable to a string before returning the data.
  207. */
  208. string: String,
  209. /**
  210. * A boolean type of variable can either be set to `true` or `false`. Any other value set is converted to
  211. * Boolean when procured from {@link Variable#get}.
  212. */
  213. boolean: Boolean,
  214. /**
  215. * A "number" type variable ensures that the value is always represented as a number. A non-number type value
  216. * is returned as `NaN`.
  217. */
  218. number: Number,
  219. /**
  220. * A "json" type value stores JSON data format
  221. * @deprecated Use "object" or "array" type instead. To be removed in 4.0.
  222. */
  223. json: {
  224. /**
  225. * @param {Object|Array} val
  226. * @returns {String}
  227. */
  228. in: function (val) {
  229. try {
  230. // @todo: should we check if `val` is a valid JSON string?
  231. val = typeof val === STRING ? val : JSON.stringify(val);
  232. }
  233. catch (e) {
  234. val = NULL;
  235. }
  236. return val;
  237. },
  238. /**
  239. * A "json" type value stores JSON data format
  240. *
  241. * @param {String} val
  242. * @returns {Object}
  243. */
  244. out: function (val) {
  245. try {
  246. val = JSON.parse(val);
  247. }
  248. catch (e) {
  249. val = null;
  250. }
  251. return val;
  252. }
  253. },
  254. /**
  255. * A "array" type value stores Array data format
  256. */
  257. array: {
  258. /**
  259. * @param {Array} val
  260. * @returns {String}
  261. */
  262. in: function (val) {
  263. var value;
  264. try {
  265. // @todo: should we check if `val` is a valid Array or Array string?
  266. value = typeof val === STRING ? val : JSON.stringify(val);
  267. }
  268. catch (e) {
  269. value = NULL;
  270. }
  271. return value;
  272. },
  273. /**
  274. * A "array" type value stores Array data format
  275. *
  276. * @param {String} val
  277. * @returns {Object}
  278. */
  279. out: function (val) {
  280. var value;
  281. try {
  282. value = JSON.parse(val);
  283. }
  284. catch (e) {
  285. value = undefined;
  286. }
  287. return Array.isArray(value) ? value : undefined;
  288. }
  289. },
  290. /**
  291. * A "object" type value stores Object data format
  292. */
  293. object: {
  294. /**
  295. * @param {Object} val
  296. * @returns {String}
  297. */
  298. in: function (val) {
  299. var value;
  300. try {
  301. // @todo: should we check if `val` is a valid JSON string?
  302. value = typeof val === STRING ? val : JSON.stringify(val);
  303. }
  304. catch (e) {
  305. value = NULL;
  306. }
  307. return value;
  308. },
  309. /**
  310. * A "object" type value stores Object data format
  311. *
  312. * @param {String} val
  313. * @returns {Object}
  314. */
  315. out: function (val) {
  316. var value;
  317. try {
  318. value = JSON.parse(val);
  319. }
  320. catch (e) {
  321. value = undefined;
  322. }
  323. return (value instanceof Object && !Array.isArray(value)) ? value : undefined;
  324. }
  325. },
  326. /**
  327. * Free-form type of a value. This is the default for any variable, unless specified otherwise. It ensures that
  328. * the variable can store data in any type and no conversion is done while using {@link Variable#get}.
  329. */
  330. any: {
  331. /**
  332. * @param {*} val
  333. * @returns {*}
  334. */
  335. in: function (val) {
  336. return val; // pass through
  337. },
  338. /**
  339. * @param {*} val
  340. * @returns {*}
  341. */
  342. out: function (val) {
  343. return val; // pass through
  344. }
  345. }
  346. },
  347. /**
  348. * @param {*} obj
  349. * @returns {Boolean}
  350. */
  351. isVariable: function (obj) {
  352. return Boolean(obj) && ((obj instanceof Variable) ||
  353. _.inSuperChain(obj.constructor, '_postman_propertyName', Variable._postman_propertyName));
  354. }
  355. });
  356. module.exports = {
  357. Variable: Variable
  358. };