var _ = require('../util').lodash, PropertyBase = require('./property-base').PropertyBase, PropertyList = require('./property-list').PropertyList, QueryParam = require('./query-param').QueryParam, FormParam = require('./form-param').FormParam, EMPTY = '', RequestBody; /** * @typedef RequestBody.definition * @property {String} mode * @property {String} raw * @property {String} file * @property {Object} graphql * @property {Object[]} formdata * @property {Object[]|String} urlencoded */ _.inherit(( /** * RequestBody holds data related to the request body. By default, it provides a nice wrapper for url-encoded, * form-data, and raw types of request bodies. * * @constructor * @extends {PropertyBase} * * @param {Object} options - */ RequestBody = function PostmanRequestBody (options) { // this constructor is intended to inherit and as such the super constructor is required to be executed RequestBody.super_.apply(this, arguments); if (!options) { return; } // in case definition object is missing, there is no point moving forward this.update(options); }), PropertyBase); _.assign(RequestBody.prototype, /** @lends RequestBody.prototype */ { /** * Set the content of this request data * * @param {Object} options - */ update (options) { _.isString(options) && (options = { mode: 'raw', raw: options }); if (!options.mode) { return; } // need a valid mode @todo raise error? var mode = RequestBody.MODES[options.mode.toString().toLowerCase()] || RequestBody.MODES.raw, urlencoded = options.urlencoded, formdata = options.formdata, graphql = options.graphql, file = options.file, raw = options.raw; // Handle URL Encoded data if (options.urlencoded) { _.isString(options.urlencoded) && (urlencoded = QueryParam.parse(options.urlencoded)); urlencoded = new PropertyList(QueryParam, this, urlencoded); } // Handle Form data if (options.formdata) { formdata = new PropertyList(FormParam, this, options.formdata); } // Handle GraphQL data if (options.graphql) { graphql = { query: graphql.query, operationName: graphql.operationName, variables: graphql.variables }; } _.isString(options.file) && (file = { src: file }); // If mode is raw but options does not give raw content, set it to empty string (mode === RequestBody.MODES.raw && !raw) && (raw = ''); // If mode is urlencoded but options does not provide any content, set it to an empty property list (mode === RequestBody.MODES.urlencoded && !urlencoded) && (urlencoded = new PropertyList(QueryParam, this, [])); // If mode is formdata but options does not provide any content, set it to an empty property list (mode === RequestBody.MODES.formdata && !formdata) && (formdata = new PropertyList(FormParam, this, [])); // If mode is graphql but options does not provide any content, set empty query (mode === RequestBody.MODES.graphql && !graphql) && (graphql = {}); _.assign(this, /** @lends RequestBody.prototype */ { /** * Indicates the type of request data to use. * * @type {String} */ mode: mode, /** * If the request has raw body data associated with it, the data is held in this field. * * @type {String} */ raw: raw, /** * Any URL encoded body params go here. * * @type {PropertyList} */ urlencoded: urlencoded, /** * Form data parameters for this request are held in this field. * * @type {PropertyList} */ formdata: formdata, /** * Holds a reference to a file which should be read as the RequestBody. It can be a file path (when used * with Node) or a unique ID (when used with the browser). * * @note The reference stored here should be resolved by a resolver function (which should be provided to * the Postman Runtime). */ file: file, /** * If the request has raw graphql data associated with it, the data is held in this field. * * @type {Object} */ graphql: graphql, /** * If the request has body Options associated with it, the data is held in this field. * * @type {Object} */ options: _.isObject(options.options) ? options.options : undefined, /** * Indicates whether to include body in request or not. * * @type {Boolean} */ disabled: options.disabled }); }, /** * Stringifies and returns the request body. * * @note FormData is not supported yet. * @returns {*} */ toString () { // Formdata. Goodluck. if (this.mode === RequestBody.MODES.formdata || this.mode === RequestBody.MODES.file) { // @todo: implement this, check if we need to return undefined or something. return EMPTY; } if (this.mode === RequestBody.MODES.urlencoded) { return PropertyList.isPropertyList(this.urlencoded) ? QueryParam.unparse(this.urlencoded.all()) : ((this.urlencoded && _.isFunction(this.urlencoded.toString)) ? this.urlencoded.toString() : EMPTY); } if (this.mode === RequestBody.MODES.raw) { return (this.raw && _.isFunction(this.raw.toString)) ? this.raw.toString() : EMPTY; } return EMPTY; }, /** * If the request body is set to a mode, but does not contain data, then we should not be sending it. * * @returns {Boolean} */ isEmpty () { var mode = this.mode, data = mode && this[mode]; // bail out if there's no data for the selected mode if (!data) { return true; } // Handle file mode // @note this is a legacy exception. ideally every individual data mode // in future would declare its "empty state". if (mode === RequestBody.MODES.file) { return !(data.src || data.content); } if (_.isString(data)) { return (data.length === 0); } if (_.isFunction(data.count)) { // handle for property lists return (data.count() === 0); } return _.isEmpty(data); // catch all for remaining data modes }, /** * Convert the request body to JSON compatible plain object * * @returns {Object} */ toJSON () { var obj = PropertyBase.toJSON(this); // make sure that file content is removed because it is non-serializable ReadStream _.unset(obj, 'file.content'); return obj; } }); _.assign(RequestBody, /** @lends RequestBody **/{ /** * Defines the name of this property for internal use. * * @private * @readOnly * @type {String} */ _postman_propertyName: 'RequestBody', /** * @enum {string} MODES */ MODES: { file: 'file', formdata: 'formdata', graphql: 'graphql', raw: 'raw', urlencoded: 'urlencoded' } }); module.exports = { RequestBody };