header.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. "use strict";
  2. var _ = require('../../lodash'), E = '', SPC = ' ', HEADER_KV_SEPARATOR = ':', Property = require('./property').Property, PropertyList = require('./property-list').PropertyList, Header;
  3. /**
  4. * @typedef Header.definition
  5. * @property {String} key The Header name (e.g: 'Content-Type')
  6. * @property {String} value The value of the header.
  7. *
  8. * @example <caption>Create a header</caption>
  9. * var Header = require('postman-collection').Header,
  10. * header = new Header({
  11. * key: 'Content-Type',
  12. * value: 'application/xml'
  13. * });
  14. *
  15. * console.log(header.toString()) // prints the string representation of the Header.
  16. */
  17. _.inherit((
  18. /**
  19. * Represents an HTTP header, for requests or for responses.
  20. *
  21. * @constructor
  22. * @extends {Property}
  23. *
  24. * @param {Header.definition|String} options - Pass the header definition as an object or the value of the header.
  25. * If the value is passed as a string, it should either be in `name:value` format or the second "name" parameter
  26. * should be used to pass the name as string
  27. * @param {String} [name] - optional override the header name or use when the first parameter is the header value as
  28. * string.
  29. *
  30. * @example <caption>Parse a string of headers into an array of Header objects</caption>
  31. * var Header = require('postman-collection').Header,
  32. * headerString = 'Content-Type: application/json\nUser-Agent: MyClientLibrary/2.0\n';
  33. *
  34. * var rawHeaders = Header.parse(headerString);
  35. * console.log(rawHeaders); // [{ 'Content-Type': 'application/json', 'User-Agent': 'MyClientLibrary/2.0' }]
  36. *
  37. * var headers = rawHeaders.map(function (h) {
  38. * return new Header(h);
  39. * });
  40. *
  41. * function assert(condition, message) {
  42. * if (!condition) {
  43. * message = message || "Assertion failed";
  44. * if (typeof Error !== "undefined") {
  45. * throw new Error(message);
  46. * }
  47. * throw message; //fallback
  48. * }
  49. * else {
  50. * console.log("Assertion passed");
  51. * }
  52. * }
  53. *
  54. * assert(headerString.trim() === Header.unparse(headers).trim());
  55. */
  56. Header = function PostmanHeader(options, name) {
  57. if (_.isString(options)) {
  58. options = _.isString(name) ? { key: name, value: options } : Header.parseSingle(options);
  59. }
  60. // this constructor is intended to inherit and as such the super constructor is required to be executed
  61. Header.super_.apply(this, arguments);
  62. this.update(options);
  63. }), Property);
  64. _.assign(Header.prototype, /** @lends Header.prototype */ {
  65. /**
  66. * Converts the header to a single header string.
  67. *
  68. * @returns {String}
  69. */
  70. toString: function () {
  71. return this.key + ': ' + this.value;
  72. },
  73. /**
  74. * Return the value of this header.
  75. *
  76. * @return {String}
  77. */
  78. valueOf: function () {
  79. return this.value;
  80. },
  81. /**
  82. * Assigns the given properties to the Header
  83. *
  84. * @param {Object} options
  85. * @todo check for allowed characters in header key-value or store encoded.
  86. */
  87. update: function (options) {
  88. /**
  89. * The header Key
  90. * @type {String}
  91. * @todo avoid headers with falsy key.
  92. */
  93. this.key = _.get(options, 'key') || E;
  94. /**
  95. * The header value
  96. * @type {String}
  97. */
  98. this.value = _.get(options, 'value', E);
  99. /**
  100. * Indicates whether the header was added by internal SDK operations, such as authorizing a request.
  101. * @type {*|boolean}
  102. */
  103. _.has(options, 'system') && (this.system = options.system);
  104. /**
  105. * Indicates whether the header should be .
  106. * @type {*|boolean}
  107. * @todo figure out whether this should be in property.js
  108. */
  109. _.has(options, 'disabled') && (this.disabled = options.disabled);
  110. }
  111. });
  112. _.assign(Header, /** @lends Header */ {
  113. /**
  114. * Defines the name of this property for internal use.
  115. * @private
  116. * @readOnly
  117. * @type {String}
  118. */
  119. _postman_propertyName: 'Header',
  120. /**
  121. * Specify the key to be used while indexing this object
  122. * @private
  123. * @readOnly
  124. * @type {String}
  125. */
  126. _postman_propertyIndexKey: 'key',
  127. /**
  128. * Specifies whether the index lookup of this property, when in a list is case insensitive or not
  129. * @private
  130. * @readOnly
  131. * @type {boolean}
  132. */
  133. _postman_propertyIndexCaseInsensitive: true,
  134. /**
  135. * Since each header may have multiple possible values, this is set to true.
  136. *
  137. * @private
  138. * @readOnly
  139. * @type {Boolean}
  140. */
  141. _postman_propertyAllowsMultipleValues: true,
  142. /**
  143. * Parses a multi line header string into an array of {@link Header.definition}.
  144. *
  145. * @param {String} headerString
  146. * @returns {Array}
  147. */
  148. parse: function (headerString) {
  149. var headers = [], regexes = {
  150. header: /^(\S+):(.*)$/gm,
  151. fold: /\r\n([ \t])/g,
  152. trim: /^\s*(.*\S)?\s*$/
  153. }, match = regexes.header.exec(headerString);
  154. headerString = headerString.toString().replace(regexes.fold, '$1');
  155. while (match) {
  156. headers.push({
  157. key: match[1],
  158. value: match[2].replace(regexes.trim, '$1')
  159. });
  160. match = regexes.header.exec(headerString);
  161. }
  162. return headers;
  163. },
  164. /**
  165. * Parses a single Header.
  166. *
  167. * @param {String} header
  168. * @returns {{key: string, value: string}}
  169. */
  170. parseSingle: function (header) {
  171. if (!_.isString(header)) {
  172. return { key: E, value: E };
  173. }
  174. var index = header.indexOf(HEADER_KV_SEPARATOR), key, value;
  175. (index < 0) && (index = header.length);
  176. key = header.substr(0, index);
  177. value = header.substr(index + 1);
  178. return {
  179. key: _.trim(key),
  180. value: _.trim(value)
  181. };
  182. },
  183. /**
  184. * Stringifies an Array or {@link PropertyList} of Headers into a single string.
  185. *
  186. * @param {Array|PropertyList<Header>} headers
  187. * @param {String=} separator - Specify a string for separating each header, by default, '\n', but sometimes,
  188. * it might be more useful to use a carriage return ('\r\n')
  189. * @returns {string}
  190. */
  191. unparse: function (headers, separator) {
  192. if (!_.isArray(headers) && !PropertyList.isPropertyList(headers)) {
  193. return E;
  194. }
  195. return headers.map(Header.unparseSingle).join(separator ? separator : '\n');
  196. },
  197. /**
  198. * Unparses a single Header.
  199. *
  200. * @param {String} header
  201. * @returns {String}
  202. */
  203. unparseSingle: function (header) {
  204. if (!_.isObject(header)) {
  205. return E;
  206. }
  207. return header.key + HEADER_KV_SEPARATOR + SPC + header.value;
  208. },
  209. /**
  210. * Check whether an object is an instance of PostmanHeader.
  211. *
  212. * @param {*} obj
  213. * @returns {Boolean}
  214. */
  215. isHeader: function (obj) {
  216. return Boolean(obj) && ((obj instanceof Header) ||
  217. _.inSuperChain(obj.constructor, '_postman_propertyName', Header._postman_propertyName));
  218. },
  219. /* eslint-disable jsdoc/check-param-names */
  220. /**
  221. * Create a new header instance
  222. *
  223. * @param {Header.definition|String} [value] - Pass the header definition as an object or the value of the header.
  224. * If the value is passed as a string, it should either be in `name:value` format or the second "name" parameter
  225. * should be used to pass the name as string
  226. * @param {String} [name] - optional override the header name or use when the first parameter is the header value as
  227. * string.
  228. * @returns {Header}
  229. */
  230. create: function () {
  231. var args = Array.prototype.slice.call(arguments);
  232. args.unshift(Header);
  233. return new (Header.bind.apply(Header, args))(); // eslint-disable-line prefer-spread
  234. }
  235. /* eslint-enable jsdoc/check-param-names */
  236. });
  237. module.exports = {
  238. Header: Header
  239. };